1 /* Copyright (c) 2006, 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.
29 #include <glib/gi18n.h>
31 #include <tny-account-store.h>
32 #include <tny-simple-list.h>
34 #include <tny-mime-part.h>
35 #include <tny-vfs-stream.h>
36 #include <tny-error.h>
37 #include "modest-marshal.h"
38 #include "modest-platform.h"
39 #include <modest-utils.h>
40 #include <modest-maemo-utils.h>
41 #include <modest-tny-msg.h>
42 #include <modest-msg-view-window.h>
43 #include <modest-main-window-ui.h>
44 #include "modest-msg-view-window-ui-dimming.h"
45 #include <modest-widget-memory.h>
46 #include <modest-progress-object.h>
47 #include <modest-runtime.h>
48 #include <modest-window-priv.h>
49 #include <modest-tny-folder.h>
50 #include <modest-text-utils.h>
51 #include <modest-account-mgr-helpers.h>
52 #include <hildon/hildon-pannable-area.h>
53 #include <hildon/hildon-picker-dialog.h>
54 #include <hildon/hildon-app-menu.h>
55 #include "modest-defs.h"
56 #include "modest-hildon-includes.h"
57 #include "modest-ui-dimming-manager.h"
58 #include <gdk/gdkkeysyms.h>
59 #include <modest-tny-account.h>
60 #include <modest-mime-part-view.h>
61 #include <modest-isearch-view.h>
62 #include <modest-tny-mime-part.h>
63 #include <modest-address-book.h>
66 #include <glib/gstdio.h>
67 #include <modest-debug.h>
68 #include <modest-header-window.h>
69 #include <modest-account-protocol.h>
70 #include <modest-hildon2-window-mgr.h>
71 #include <tny-camel-msg.h>
72 #include <tny-camel-bs-mime-part.h>
73 #include <tny-camel-bs-msg.h>
75 #include <X11/Xatom.h>
76 #include <X11/XKBlib.h>
77 #include <X11/Xdmcp.h>
79 #define MYDOCS_ENV "MYDOCSDIR"
80 #define DOCS_FOLDER ".documents"
82 typedef struct _ModestMsgViewWindowPrivate ModestMsgViewWindowPrivate;
83 struct _ModestMsgViewWindowPrivate {
86 GtkWidget *main_scroll;
87 GtkWidget *find_toolbar;
90 /* Progress observers */
91 GSList *progress_widgets;
94 GtkWidget *prev_toolitem;
95 GtkWidget *next_toolitem;
96 gboolean progress_hint;
99 /* Optimized view enabled */
100 gboolean optimized_view;
102 /* Whether this was created via the *_new_for_search_result() function. */
103 gboolean is_search_result;
105 /* Whether the message is in outbox */
108 /* A reference to the @model of the header view
109 * to allow selecting previous/next messages,
110 * if the message is currently selected in the header view.
112 const gchar *header_folder_id;
113 GtkTreeModel *header_model;
114 GtkTreeRowReference *row_reference;
115 GtkTreeRowReference *next_row_reference;
117 gulong clipboard_change_handler;
118 gulong queue_change_handler;
119 gulong account_removed_handler;
120 gulong row_changed_handler;
121 gulong row_deleted_handler;
122 gulong row_inserted_handler;
123 gulong rows_reordered_handler;
124 gulong fetch_image_redraw_handler;
127 GtkWidget *remove_attachment_banner;
130 TnyMimePart *other_body;
136 static void modest_msg_view_window_class_init (ModestMsgViewWindowClass *klass);
137 static void modest_msg_view_window_init (ModestMsgViewWindow *obj);
138 static void modest_header_view_observer_init (ModestHeaderViewObserverIface *iface_class);
139 static void modest_msg_view_window_finalize (GObject *obj);
140 static void modest_msg_view_window_show_find_toolbar (GtkWidget *obj, gpointer data);
141 static void modest_msg_view_window_find_toolbar_close (GtkWidget *widget,
142 ModestMsgViewWindow *obj);
143 static void modest_msg_view_window_find_toolbar_search (GtkWidget *widget,
144 ModestMsgViewWindow *obj);
145 static void modest_msg_view_window_toggle_find_toolbar (GtkWidget *obj,
147 static void modest_msg_view_window_disconnect_signals (ModestWindow *self);
149 static gdouble modest_msg_view_window_get_zoom (ModestWindow *window);
150 static void modest_msg_view_window_set_zoom (ModestWindow *window,
152 static gboolean modest_msg_view_window_zoom_minus (ModestWindow *window);
153 static gboolean modest_msg_view_window_zoom_plus (ModestWindow *window);
154 static gboolean modest_msg_view_window_key_event (GtkWidget *window,
157 static void modest_msg_view_window_update_priority (ModestMsgViewWindow *window);
159 static void modest_msg_view_window_show_toolbar (ModestWindow *window,
160 gboolean show_toolbar);
162 static void modest_msg_view_window_clipboard_owner_change (GtkClipboard *clipboard,
164 ModestMsgViewWindow *window);
166 static void modest_msg_view_window_on_row_changed (GtkTreeModel *header_model,
169 ModestMsgViewWindow *window);
171 static void modest_msg_view_window_on_row_deleted (GtkTreeModel *header_model,
173 ModestMsgViewWindow *window);
175 static void modest_msg_view_window_on_row_inserted (GtkTreeModel *header_model,
176 GtkTreePath *tree_path,
177 GtkTreeIter *tree_iter,
178 ModestMsgViewWindow *window);
180 static void modest_msg_view_window_on_row_reordered (GtkTreeModel *header_model,
184 ModestMsgViewWindow *window);
186 static void modest_msg_view_window_update_model_replaced (ModestHeaderViewObserver *window,
188 const gchar *tny_folder_id);
190 static void on_queue_changed (ModestMailOperationQueue *queue,
191 ModestMailOperation *mail_op,
192 ModestMailOperationQueueNotification type,
193 ModestMsgViewWindow *self);
195 static void on_account_removed (TnyAccountStore *account_store,
199 static void on_move_focus (GtkWidget *widget,
200 GtkDirectionType direction,
203 static void view_msg_cb (ModestMailOperation *mail_op,
210 static void set_progress_hint (ModestMsgViewWindow *self,
213 static void update_window_title (ModestMsgViewWindow *window);
215 static void init_window (ModestMsgViewWindow *obj);
217 static gboolean msg_is_visible (TnyHeader *header, gboolean check_outbox);
219 static void check_dimming_rules_after_change (ModestMsgViewWindow *window);
221 static gboolean on_fetch_image (ModestMsgView *msgview,
224 ModestMsgViewWindow *window);
226 static gboolean modest_msg_view_window_scroll_child (ModestMsgViewWindow *self,
227 GtkScrollType scroll_type,
230 static gboolean message_reader (ModestMsgViewWindow *window,
231 ModestMsgViewWindowPrivate *priv,
233 const gchar *msg_uid,
235 GtkTreeRowReference *row_reference);
237 static void setup_menu (ModestMsgViewWindow *self);
238 static gboolean _modest_msg_view_window_map_event (GtkWidget *widget,
241 static void update_branding (ModestMsgViewWindow *self);
242 static void sync_flags (ModestMsgViewWindow *self);
244 static gboolean on_realize (GtkWidget *widget,
247 /* list my signals */
254 static const GtkToggleActionEntry msg_view_toggle_action_entries [] = {
255 { "FindInMessage", MODEST_TOOLBAR_ICON_FIND, N_("qgn_toolb_gene_find"), "<CTRL>F", NULL, G_CALLBACK (modest_msg_view_window_toggle_find_toolbar), FALSE },
258 #define MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE((o), \
259 MODEST_TYPE_MSG_VIEW_WINDOW, \
260 ModestMsgViewWindowPrivate))
262 static GtkWindowClass *parent_class = NULL;
264 /* uncomment the following if you have defined any signals */
265 static guint signals[LAST_SIGNAL] = {0};
268 modest_msg_view_window_get_type (void)
270 static GType my_type = 0;
272 static const GTypeInfo my_info = {
273 sizeof(ModestMsgViewWindowClass),
274 NULL, /* base init */
275 NULL, /* base finalize */
276 (GClassInitFunc) modest_msg_view_window_class_init,
277 NULL, /* class finalize */
278 NULL, /* class data */
279 sizeof(ModestMsgViewWindow),
281 (GInstanceInitFunc) modest_msg_view_window_init,
284 my_type = g_type_register_static (MODEST_TYPE_HILDON2_WINDOW,
285 "ModestMsgViewWindow",
288 static const GInterfaceInfo modest_header_view_observer_info =
290 (GInterfaceInitFunc) modest_header_view_observer_init,
291 NULL, /* interface_finalize */
292 NULL /* interface_data */
295 g_type_add_interface_static (my_type,
296 MODEST_TYPE_HEADER_VIEW_OBSERVER,
297 &modest_header_view_observer_info);
303 save_state (ModestWindow *self)
305 modest_widget_memory_save (modest_runtime_get_conf (),
307 MODEST_CONF_MSG_VIEW_WINDOW_KEY);
311 modest_msg_view_window_scroll_child (ModestMsgViewWindow *self,
312 GtkScrollType scroll_type,
316 ModestMsgViewWindowPrivate *priv;
319 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(self);
321 switch (scroll_type) {
322 case GTK_SCROLL_STEP_UP:
325 case GTK_SCROLL_STEP_DOWN:
328 case GTK_SCROLL_PAGE_UP:
331 case GTK_SCROLL_PAGE_DOWN:
334 case GTK_SCROLL_START:
345 modest_maemo_utils_scroll_pannable((HildonPannableArea *) priv->main_scroll, 0, step);
347 return (gboolean) step;
351 add_scroll_binding (GtkBindingSet *binding_set,
353 GtkScrollType scroll)
355 guint keypad_keyval = keyval - GDK_Left + GDK_KP_Left;
357 gtk_binding_entry_add_signal (binding_set, keyval, 0,
359 GTK_TYPE_SCROLL_TYPE, scroll,
360 G_TYPE_BOOLEAN, FALSE);
361 gtk_binding_entry_add_signal (binding_set, keypad_keyval, 0,
363 GTK_TYPE_SCROLL_TYPE, scroll,
364 G_TYPE_BOOLEAN, FALSE);
368 modest_msg_view_window_class_init (ModestMsgViewWindowClass *klass)
370 GObjectClass *gobject_class;
371 HildonWindowClass *hildon_window_class;
372 ModestWindowClass *modest_window_class;
373 GtkBindingSet *binding_set;
375 gobject_class = (GObjectClass*) klass;
376 hildon_window_class = (HildonWindowClass *) klass;
377 modest_window_class = (ModestWindowClass *) klass;
379 parent_class = g_type_class_peek_parent (klass);
380 gobject_class->finalize = modest_msg_view_window_finalize;
382 modest_window_class->set_zoom_func = modest_msg_view_window_set_zoom;
383 modest_window_class->get_zoom_func = modest_msg_view_window_get_zoom;
384 modest_window_class->zoom_plus_func = modest_msg_view_window_zoom_plus;
385 modest_window_class->zoom_minus_func = modest_msg_view_window_zoom_minus;
386 modest_window_class->show_toolbar_func = modest_msg_view_window_show_toolbar;
387 modest_window_class->disconnect_signals_func = modest_msg_view_window_disconnect_signals;
389 modest_window_class->save_state_func = save_state;
391 klass->scroll_child = modest_msg_view_window_scroll_child;
393 signals[MSG_CHANGED_SIGNAL] =
394 g_signal_new ("msg-changed",
395 G_TYPE_FROM_CLASS (gobject_class),
397 G_STRUCT_OFFSET (ModestMsgViewWindowClass, msg_changed),
399 modest_marshal_VOID__POINTER_POINTER,
400 G_TYPE_NONE, 2, G_TYPE_POINTER, G_TYPE_POINTER);
402 signals[SCROLL_CHILD_SIGNAL] =
403 g_signal_new ("scroll-child",
404 G_TYPE_FROM_CLASS (gobject_class),
405 G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
406 G_STRUCT_OFFSET (ModestMsgViewWindowClass, scroll_child),
408 modest_marshal_BOOLEAN__ENUM_BOOLEAN,
409 G_TYPE_BOOLEAN, 2, GTK_TYPE_SCROLL_TYPE, G_TYPE_BOOLEAN);
411 binding_set = gtk_binding_set_by_class (klass);
412 add_scroll_binding (binding_set, GDK_Up, GTK_SCROLL_STEP_UP);
413 add_scroll_binding (binding_set, GDK_Down, GTK_SCROLL_STEP_DOWN);
414 add_scroll_binding (binding_set, GDK_Page_Up, GTK_SCROLL_PAGE_UP);
415 add_scroll_binding (binding_set, GDK_Page_Down, GTK_SCROLL_PAGE_DOWN);
416 add_scroll_binding (binding_set, GDK_Home, GTK_SCROLL_START);
417 add_scroll_binding (binding_set, GDK_End, GTK_SCROLL_END);
419 g_type_class_add_private (gobject_class, sizeof(ModestMsgViewWindowPrivate));
423 static void modest_header_view_observer_init(
424 ModestHeaderViewObserverIface *iface_class)
426 iface_class->update_func = modest_msg_view_window_update_model_replaced;
430 modest_msg_view_window_init (ModestMsgViewWindow *obj)
432 ModestMsgViewWindowPrivate *priv;
433 ModestWindowPrivate *parent_priv = NULL;
434 GtkActionGroup *action_group = NULL;
435 GError *error = NULL;
437 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(obj);
438 parent_priv = MODEST_WINDOW_GET_PRIVATE(obj);
439 parent_priv->ui_manager = gtk_ui_manager_new();
441 action_group = gtk_action_group_new ("ModestMsgViewWindowActions");
442 gtk_action_group_set_translation_domain (action_group, GETTEXT_PACKAGE);
444 /* Add common actions */
445 gtk_action_group_add_actions (action_group,
446 modest_action_entries,
447 G_N_ELEMENTS (modest_action_entries),
449 gtk_action_group_add_toggle_actions (action_group,
450 msg_view_toggle_action_entries,
451 G_N_ELEMENTS (msg_view_toggle_action_entries),
454 gtk_ui_manager_insert_action_group (parent_priv->ui_manager, action_group, 0);
455 g_object_unref (action_group);
457 /* Load the UI definition */
458 gtk_ui_manager_add_ui_from_file (parent_priv->ui_manager, MODEST_UIDIR "modest-msg-view-window-ui.xml",
461 g_printerr ("modest: could not merge modest-msg-view-window-ui.xml: %s\n", error->message);
462 g_error_free (error);
467 /* Add accelerators */
468 gtk_window_add_accel_group (GTK_WINDOW (obj),
469 gtk_ui_manager_get_accel_group (parent_priv->ui_manager));
471 priv->is_search_result = FALSE;
472 priv->is_outbox = FALSE;
474 priv->msg_view = NULL;
475 priv->header_model = NULL;
476 priv->header_folder_id = NULL;
477 priv->clipboard_change_handler = 0;
478 priv->queue_change_handler = 0;
479 priv->account_removed_handler = 0;
480 priv->row_changed_handler = 0;
481 priv->row_deleted_handler = 0;
482 priv->row_inserted_handler = 0;
483 priv->rows_reordered_handler = 0;
484 priv->fetch_image_redraw_handler = 0;
485 priv->progress_hint = FALSE;
486 priv->fetching_images = 0;
488 priv->optimized_view = FALSE;
489 priv->purge_timeout = 0;
490 priv->remove_attachment_banner = NULL;
491 priv->msg_uid = NULL;
492 priv->other_body = NULL;
494 priv->sighandlers = NULL;
497 init_window (MODEST_MSG_VIEW_WINDOW(obj));
499 hildon_program_add_window (hildon_program_get_instance(),
502 /* Grab the zoom keys, it will be used for Zoom and not for
504 g_signal_connect (G_OBJECT (obj), "realize",
505 G_CALLBACK (on_realize),
510 update_progress_hint (ModestMsgViewWindow *self)
512 ModestMsgViewWindowPrivate *priv;
513 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(self);
515 if (GTK_WIDGET_VISIBLE (self)) {
516 hildon_gtk_window_set_progress_indicator (GTK_WINDOW (self),
517 (priv->progress_hint || (priv->fetching_images > 0))?1:0);
522 set_progress_hint (ModestMsgViewWindow *self,
525 ModestWindowPrivate *parent_priv;
526 ModestMsgViewWindowPrivate *priv;
528 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self));
530 parent_priv = MODEST_WINDOW_GET_PRIVATE(self);
531 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(self);
533 /* Sets current progress hint */
534 priv->progress_hint = enabled;
536 update_progress_hint (self);
542 init_window (ModestMsgViewWindow *obj)
544 GtkWidget *main_vbox;
545 ModestMsgViewWindowPrivate *priv;
547 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(obj);
549 priv->msg_view = GTK_WIDGET (tny_platform_factory_new_msg_view (modest_tny_platform_factory_get_instance ()));
550 modest_msg_view_set_shadow_type (MODEST_MSG_VIEW (priv->msg_view), GTK_SHADOW_NONE);
551 main_vbox = gtk_vbox_new (FALSE, 6);
552 priv->main_scroll = hildon_pannable_area_new ();
553 g_object_set (G_OBJECT (priv->main_scroll),
554 "mov-mode", HILDON_MOVEMENT_MODE_BOTH,
557 gtk_container_add (GTK_CONTAINER (priv->main_scroll), priv->msg_view);
558 gtk_box_pack_start (GTK_BOX(main_vbox), priv->main_scroll, TRUE, TRUE, 0);
559 gtk_container_add (GTK_CONTAINER(obj), main_vbox);
561 /* NULL-ize fields if the window is destroyed */
562 g_signal_connect (priv->msg_view, "destroy", G_CALLBACK (gtk_widget_destroyed), &(priv->msg_view));
564 gtk_widget_show_all (GTK_WIDGET(main_vbox));
568 modest_msg_view_window_disconnect_signals (ModestWindow *self)
570 ModestMsgViewWindowPrivate *priv;
571 GtkWidget *header_view = NULL;
572 GtkWindow *parent_window = NULL;
574 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
576 if (gtk_clipboard_get (GDK_SELECTION_PRIMARY) &&
577 g_signal_handler_is_connected (gtk_clipboard_get (GDK_SELECTION_PRIMARY),
578 priv->clipboard_change_handler))
579 g_signal_handler_disconnect (gtk_clipboard_get (GDK_SELECTION_PRIMARY),
580 priv->clipboard_change_handler);
582 if (g_signal_handler_is_connected (G_OBJECT (modest_runtime_get_mail_operation_queue ()),
583 priv->queue_change_handler))
584 g_signal_handler_disconnect (G_OBJECT (modest_runtime_get_mail_operation_queue ()),
585 priv->queue_change_handler);
587 if (g_signal_handler_is_connected (G_OBJECT (modest_runtime_get_account_store ()),
588 priv->account_removed_handler))
589 g_signal_handler_disconnect (G_OBJECT (modest_runtime_get_account_store ()),
590 priv->account_removed_handler);
592 if (priv->header_model) {
593 if (g_signal_handler_is_connected(G_OBJECT (priv->header_model),
594 priv->row_changed_handler))
595 g_signal_handler_disconnect(G_OBJECT (priv->header_model),
596 priv->row_changed_handler);
598 if (g_signal_handler_is_connected(G_OBJECT (priv->header_model),
599 priv->row_deleted_handler))
600 g_signal_handler_disconnect(G_OBJECT (priv->header_model),
601 priv->row_deleted_handler);
603 if (g_signal_handler_is_connected(G_OBJECT (priv->header_model),
604 priv->row_inserted_handler))
605 g_signal_handler_disconnect(G_OBJECT (priv->header_model),
606 priv->row_inserted_handler);
608 if (g_signal_handler_is_connected(G_OBJECT (priv->header_model),
609 priv->rows_reordered_handler))
610 g_signal_handler_disconnect(G_OBJECT (priv->header_model),
611 priv->rows_reordered_handler);
614 modest_signal_mgr_disconnect_all_and_destroy (priv->sighandlers);
615 priv->sighandlers = NULL;
617 parent_window = gtk_window_get_transient_for (GTK_WINDOW (self));
618 if (parent_window && MODEST_IS_HEADER_WINDOW (parent_window)) {
619 header_view = GTK_WIDGET (modest_header_window_get_header_view (MODEST_HEADER_WINDOW (parent_window)));
621 modest_header_view_remove_observer(MODEST_HEADER_VIEW (header_view),
622 MODEST_HEADER_VIEW_OBSERVER(self));
628 modest_msg_view_window_finalize (GObject *obj)
630 ModestMsgViewWindowPrivate *priv;
632 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (obj);
634 /* Sanity check: shouldn't be needed, the window mgr should
635 call this function before */
636 modest_msg_view_window_disconnect_signals (MODEST_WINDOW (obj));
638 if (priv->fetch_image_redraw_handler > 0) {
639 g_source_remove (priv->fetch_image_redraw_handler);
640 priv->fetch_image_redraw_handler = 0;
643 if (priv->other_body != NULL) {
644 g_object_unref (priv->other_body);
645 priv->other_body = NULL;
648 if (priv->top_msg != NULL) {
649 g_object_unref (priv->top_msg);
650 priv->top_msg = NULL;
653 if (priv->header_model != NULL) {
654 g_object_unref (priv->header_model);
655 priv->header_model = NULL;
658 if (priv->remove_attachment_banner) {
659 gtk_widget_destroy (priv->remove_attachment_banner);
660 g_object_unref (priv->remove_attachment_banner);
661 priv->remove_attachment_banner = NULL;
664 if (priv->purge_timeout > 0) {
665 g_source_remove (priv->purge_timeout);
666 priv->purge_timeout = 0;
669 if (priv->row_reference) {
670 gtk_tree_row_reference_free (priv->row_reference);
671 priv->row_reference = NULL;
674 if (priv->next_row_reference) {
675 gtk_tree_row_reference_free (priv->next_row_reference);
676 priv->next_row_reference = NULL;
680 g_free (priv->msg_uid);
681 priv->msg_uid = NULL;
684 G_OBJECT_CLASS(parent_class)->finalize (obj);
688 select_next_valid_row (GtkTreeModel *model,
689 GtkTreeRowReference **row_reference,
693 GtkTreeIter tmp_iter;
695 GtkTreePath *next = NULL;
696 gboolean retval = FALSE, finished;
698 g_return_val_if_fail (gtk_tree_row_reference_valid (*row_reference), FALSE);
700 path = gtk_tree_row_reference_get_path (*row_reference);
701 gtk_tree_model_get_iter (model, &tmp_iter, path);
702 gtk_tree_row_reference_free (*row_reference);
703 *row_reference = NULL;
707 TnyHeader *header = NULL;
709 if (gtk_tree_model_iter_next (model, &tmp_iter)) {
710 gtk_tree_model_get (model, &tmp_iter,
711 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
715 if (msg_is_visible (header, is_outbox)) {
716 next = gtk_tree_model_get_path (model, &tmp_iter);
717 *row_reference = gtk_tree_row_reference_new (model, next);
718 gtk_tree_path_free (next);
722 g_object_unref (header);
725 } else if (cycle && gtk_tree_model_get_iter_first (model, &tmp_iter)) {
726 next = gtk_tree_model_get_path (model, &tmp_iter);
728 /* Ensure that we are not selecting the same */
729 if (gtk_tree_path_compare (path, next) != 0) {
730 gtk_tree_model_get (model, &tmp_iter,
731 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
734 if (msg_is_visible (header, is_outbox)) {
735 *row_reference = gtk_tree_row_reference_new (model, next);
739 g_object_unref (header);
743 /* If we ended up in the same message
744 then there is no valid next
748 gtk_tree_path_free (next);
750 /* If there are no more messages and we don't
751 want to start again in the first one then
752 there is no valid next message */
758 gtk_tree_path_free (path);
763 /* TODO: This should be in _init(), with the parameters as properties. */
765 modest_msg_view_window_construct (ModestMsgViewWindow *self,
766 const gchar *modest_account_name,
767 const gchar *mailbox,
768 const gchar *msg_uid)
771 ModestMsgViewWindowPrivate *priv = NULL;
772 ModestWindowPrivate *parent_priv = NULL;
773 ModestDimmingRulesGroup *toolbar_rules_group = NULL;
774 ModestDimmingRulesGroup *clipboard_rules_group = NULL;
776 obj = G_OBJECT (self);
777 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(obj);
778 parent_priv = MODEST_WINDOW_GET_PRIVATE(obj);
780 priv->msg_uid = g_strdup (msg_uid);
783 parent_priv->menubar = NULL;
785 toolbar_rules_group = modest_dimming_rules_group_new (MODEST_DIMMING_RULES_TOOLBAR, TRUE);
786 clipboard_rules_group = modest_dimming_rules_group_new (MODEST_DIMMING_RULES_CLIPBOARD, FALSE);
789 /* Add common dimming rules */
790 modest_dimming_rules_group_add_rules (toolbar_rules_group,
791 modest_msg_view_toolbar_dimming_entries,
792 G_N_ELEMENTS (modest_msg_view_toolbar_dimming_entries),
793 MODEST_WINDOW (self));
794 modest_dimming_rules_group_add_rules (clipboard_rules_group,
795 modest_msg_view_clipboard_dimming_entries,
796 G_N_ELEMENTS (modest_msg_view_clipboard_dimming_entries),
797 MODEST_WINDOW (self));
799 /* Insert dimming rules group for this window */
800 modest_ui_dimming_manager_insert_rules_group (parent_priv->ui_dimming_manager, toolbar_rules_group);
801 modest_ui_dimming_manager_insert_rules_group (parent_priv->ui_dimming_manager, clipboard_rules_group);
802 g_object_unref (toolbar_rules_group);
803 g_object_unref (clipboard_rules_group);
805 /* g_signal_connect (G_OBJECT(obj), "delete-event", G_CALLBACK(on_delete_event), obj); */
807 priv->clipboard_change_handler = g_signal_connect (G_OBJECT (gtk_clipboard_get (GDK_SELECTION_PRIMARY)), "owner-change", G_CALLBACK (modest_msg_view_window_clipboard_owner_change), obj);
808 g_signal_connect (G_OBJECT(priv->msg_view), "activate_link",
809 G_CALLBACK (modest_ui_actions_on_msg_link_clicked), obj);
810 g_signal_connect (G_OBJECT(priv->msg_view), "link_hover",
811 G_CALLBACK (modest_ui_actions_on_msg_link_hover), obj);
812 g_signal_connect (G_OBJECT(priv->msg_view), "attachment_clicked",
813 G_CALLBACK (modest_ui_actions_on_msg_attachment_clicked), obj);
814 g_signal_connect (G_OBJECT(priv->msg_view), "recpt_activated",
815 G_CALLBACK (modest_ui_actions_on_msg_recpt_activated), obj);
816 g_signal_connect (G_OBJECT(priv->msg_view), "show_details",
817 G_CALLBACK (modest_ui_actions_on_details), obj);
818 g_signal_connect (G_OBJECT(priv->msg_view), "link_contextual",
819 G_CALLBACK (modest_ui_actions_on_msg_link_contextual), obj);
820 g_signal_connect (G_OBJECT(priv->msg_view), "limit_error",
821 G_CALLBACK (modest_ui_actions_on_limit_error), obj);
822 g_signal_connect (G_OBJECT (priv->msg_view), "fetch_image",
823 G_CALLBACK (on_fetch_image), obj);
825 g_signal_connect (G_OBJECT (obj), "key-release-event",
826 G_CALLBACK (modest_msg_view_window_key_event),
829 g_signal_connect (G_OBJECT (obj), "key-press-event",
830 G_CALLBACK (modest_msg_view_window_key_event),
833 g_signal_connect (G_OBJECT (obj), "move-focus",
834 G_CALLBACK (on_move_focus), obj);
836 g_signal_connect (G_OBJECT (obj), "map-event",
837 G_CALLBACK (_modest_msg_view_window_map_event),
840 /* Mail Operation Queue */
841 priv->queue_change_handler = g_signal_connect (G_OBJECT (modest_runtime_get_mail_operation_queue ()),
843 G_CALLBACK (on_queue_changed),
846 /* Account manager */
847 priv->account_removed_handler = g_signal_connect (G_OBJECT (modest_runtime_get_account_store ()),
849 G_CALLBACK(on_account_removed),
852 modest_window_set_active_account (MODEST_WINDOW(obj), modest_account_name);
853 modest_window_set_active_mailbox (MODEST_WINDOW(obj), mailbox);
855 /* First add out toolbar ... */
856 modest_msg_view_window_show_toolbar (MODEST_WINDOW (obj), TRUE);
858 /* ... and later the find toolbar. This way find toolbar will
859 be shown over the other */
860 priv->find_toolbar = hildon_find_toolbar_new (NULL);
861 hildon_window_add_toolbar (HILDON_WINDOW (obj), GTK_TOOLBAR (priv->find_toolbar));
862 gtk_widget_set_no_show_all (priv->find_toolbar, TRUE);
863 g_signal_connect (G_OBJECT (priv->find_toolbar), "close",
864 G_CALLBACK (modest_msg_view_window_find_toolbar_close), obj);
865 g_signal_connect (G_OBJECT (priv->find_toolbar), "search",
866 G_CALLBACK (modest_msg_view_window_find_toolbar_search), obj);
867 priv->last_search = NULL;
869 /* Init the clipboard actions dim status */
870 modest_msg_view_grab_focus(MODEST_MSG_VIEW (priv->msg_view));
872 update_window_title (MODEST_MSG_VIEW_WINDOW (obj));
877 /* FIXME: parameter checks */
879 modest_msg_view_window_new_with_header_model (TnyMsg *msg,
880 const gchar *modest_account_name,
881 const gchar *mailbox,
882 const gchar *msg_uid,
884 GtkTreeRowReference *row_reference)
886 ModestMsgViewWindow *window = NULL;
887 ModestMsgViewWindowPrivate *priv = NULL;
888 TnyFolder *header_folder = NULL;
889 ModestHeaderView *header_view = NULL;
890 ModestWindowMgr *mgr = NULL;
893 modest_tny_mime_part_to_string (TNY_MIME_PART (msg), 0);
896 mgr = modest_runtime_get_window_mgr ();
897 window = MODEST_MSG_VIEW_WINDOW (modest_window_mgr_get_msg_view_window (mgr));
898 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), NULL);
900 modest_msg_view_window_construct (window, modest_account_name, mailbox, msg_uid);
902 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
903 priv->top_msg = NULL;
905 /* Remember the message list's TreeModel so we can detect changes
906 * and change the list selection when necessary: */
907 header_folder = modest_header_view_get_folder (header_view);
909 priv->is_outbox = (modest_tny_folder_guess_folder_type (header_folder) ==
910 TNY_FOLDER_TYPE_OUTBOX);
911 priv->header_folder_id = tny_folder_get_id (header_folder);
912 g_object_unref(header_folder);
915 /* Setup row references and connect signals */
916 priv->header_model = g_object_ref (model);
918 if (row_reference && gtk_tree_row_reference_valid (row_reference)) {
919 priv->row_reference = gtk_tree_row_reference_copy (row_reference);
920 priv->next_row_reference = gtk_tree_row_reference_copy (row_reference);
921 select_next_valid_row (model, &(priv->next_row_reference), TRUE, priv->is_outbox);
923 priv->row_reference = NULL;
924 priv->next_row_reference = NULL;
927 /* Connect signals */
928 priv->row_changed_handler =
929 g_signal_connect (GTK_TREE_MODEL(model), "row-changed",
930 G_CALLBACK(modest_msg_view_window_on_row_changed),
932 priv->row_deleted_handler =
933 g_signal_connect (GTK_TREE_MODEL(model), "row-deleted",
934 G_CALLBACK(modest_msg_view_window_on_row_deleted),
936 priv->row_inserted_handler =
937 g_signal_connect (GTK_TREE_MODEL(model), "row-inserted",
938 G_CALLBACK(modest_msg_view_window_on_row_inserted),
940 priv->rows_reordered_handler =
941 g_signal_connect(GTK_TREE_MODEL(model), "rows-reordered",
942 G_CALLBACK(modest_msg_view_window_on_row_reordered),
945 if (header_view != NULL){
946 modest_header_view_add_observer(header_view,
947 MODEST_HEADER_VIEW_OBSERVER(window));
950 tny_msg_view_set_msg (TNY_MSG_VIEW (priv->msg_view), msg);
951 update_window_title (MODEST_MSG_VIEW_WINDOW (window));
952 update_branding (MODEST_MSG_VIEW_WINDOW (window));
954 /* gtk_widget_show_all (GTK_WIDGET (window)); */
955 modest_msg_view_window_update_priority (window);
956 /* Check dimming rules */
957 modest_ui_actions_check_toolbar_dimming_rules (MODEST_WINDOW (window));
958 modest_ui_actions_check_menu_dimming_rules (MODEST_WINDOW (window));
959 modest_window_check_dimming_rules_group (MODEST_WINDOW (window), MODEST_DIMMING_RULES_CLIPBOARD);
961 return MODEST_WINDOW(window);
965 modest_msg_view_window_new_from_uid (const gchar *modest_account_name,
966 const gchar *mailbox,
967 const gchar *msg_uid)
969 ModestMsgViewWindow *window = NULL;
970 ModestMsgViewWindowPrivate *priv = NULL;
971 ModestWindowMgr *mgr = NULL;
973 TnyAccount *account = NULL;
975 mgr = modest_runtime_get_window_mgr ();
976 window = MODEST_MSG_VIEW_WINDOW (modest_window_mgr_get_msg_view_window (mgr));
977 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), NULL);
979 modest_msg_view_window_construct (window, modest_account_name, mailbox, msg_uid);
981 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
982 priv->top_msg = NULL;
984 is_merge = g_str_has_prefix (msg_uid, "merge:");
986 /* Get the account */
988 account = tny_account_store_find_account (TNY_ACCOUNT_STORE (modest_runtime_get_account_store ()),
991 if (is_merge || account) {
992 TnyFolder *folder = NULL;
994 /* Try to get the message, if it's already downloaded
995 we don't need to connect */
997 folder = modest_tny_folder_store_find_folder_from_uri (TNY_FOLDER_STORE (account), msg_uid);
999 ModestTnyAccountStore *account_store;
1000 ModestTnyLocalFoldersAccount *local_folders_account;
1002 account_store = modest_runtime_get_account_store ();
1003 local_folders_account = MODEST_TNY_LOCAL_FOLDERS_ACCOUNT (
1004 modest_tny_account_store_get_local_folders_account (account_store));
1005 folder = modest_tny_local_folders_account_get_merged_outbox (local_folders_account);
1006 g_object_unref (local_folders_account);
1010 gboolean device_online;
1012 device = modest_runtime_get_device();
1013 device_online = tny_device_is_online (device);
1014 if (device_online) {
1015 message_reader (window, priv, NULL, msg_uid, folder, NULL);
1017 TnyMsg *msg = tny_folder_find_msg (folder, msg_uid, NULL);
1019 tny_msg_view_set_msg (TNY_MSG_VIEW (priv->msg_view), msg);
1020 update_window_title (MODEST_MSG_VIEW_WINDOW (window));
1021 update_branding (MODEST_MSG_VIEW_WINDOW (window));
1022 g_object_unref (msg);
1023 /* Sync flags to server */
1024 sync_flags (MODEST_MSG_VIEW_WINDOW (window));
1026 message_reader (window, priv, NULL, msg_uid, folder, NULL);
1029 g_object_unref (folder);
1034 /* Check dimming rules */
1035 modest_ui_actions_check_toolbar_dimming_rules (MODEST_WINDOW (window));
1036 modest_ui_actions_check_menu_dimming_rules (MODEST_WINDOW (window));
1037 modest_window_check_dimming_rules_group (MODEST_WINDOW (window), MODEST_DIMMING_RULES_CLIPBOARD);
1039 return MODEST_WINDOW(window);
1043 modest_msg_view_window_new_from_header_view (ModestHeaderView *header_view,
1044 const gchar *modest_account_name,
1045 const gchar *mailbox,
1046 const gchar *msg_uid,
1047 GtkTreeRowReference *row_reference)
1049 ModestMsgViewWindow *window = NULL;
1050 ModestMsgViewWindowPrivate *priv = NULL;
1051 TnyFolder *header_folder = NULL;
1052 ModestWindowMgr *mgr = NULL;
1056 mgr = modest_runtime_get_window_mgr ();
1057 window = MODEST_MSG_VIEW_WINDOW (modest_window_mgr_get_msg_view_window (mgr));
1058 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), NULL);
1060 modest_msg_view_window_construct (window, modest_account_name, mailbox, msg_uid);
1062 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1063 priv->top_msg = NULL;
1065 /* Remember the message list's TreeModel so we can detect changes
1066 * and change the list selection when necessary: */
1068 if (header_view != NULL){
1069 header_folder = modest_header_view_get_folder(header_view);
1070 /* This could happen if the header folder was
1071 unseleted before opening this msg window (for
1072 example if the user selects an account in the
1073 folder view of the main window */
1074 if (header_folder) {
1075 priv->is_outbox = (modest_tny_folder_guess_folder_type (header_folder) ==
1076 TNY_FOLDER_TYPE_OUTBOX);
1077 priv->header_folder_id = tny_folder_get_id(header_folder);
1078 g_object_unref(header_folder);
1082 /* Setup row references and connect signals */
1083 priv->header_model = gtk_tree_view_get_model (GTK_TREE_VIEW (header_view));
1084 g_object_ref (priv->header_model);
1086 if (row_reference && gtk_tree_row_reference_valid (row_reference)) {
1087 priv->row_reference = gtk_tree_row_reference_copy (row_reference);
1088 priv->next_row_reference = gtk_tree_row_reference_copy (row_reference);
1089 select_next_valid_row (priv->header_model, &(priv->next_row_reference), TRUE, priv->is_outbox);
1091 priv->row_reference = NULL;
1092 priv->next_row_reference = NULL;
1095 /* Connect signals */
1096 priv->row_changed_handler =
1097 g_signal_connect (GTK_TREE_MODEL(priv->header_model), "row-changed",
1098 G_CALLBACK(modest_msg_view_window_on_row_changed),
1100 priv->row_deleted_handler =
1101 g_signal_connect (GTK_TREE_MODEL(priv->header_model), "row-deleted",
1102 G_CALLBACK(modest_msg_view_window_on_row_deleted),
1104 priv->row_inserted_handler =
1105 g_signal_connect (GTK_TREE_MODEL(priv->header_model), "row-inserted",
1106 G_CALLBACK(modest_msg_view_window_on_row_inserted),
1108 priv->rows_reordered_handler =
1109 g_signal_connect(GTK_TREE_MODEL(priv->header_model), "rows-reordered",
1110 G_CALLBACK(modest_msg_view_window_on_row_reordered),
1113 if (header_view != NULL){
1114 modest_header_view_add_observer(header_view,
1115 MODEST_HEADER_VIEW_OBSERVER(window));
1118 tny_msg_view_set_msg (TNY_MSG_VIEW (priv->msg_view), NULL);
1119 update_branding (MODEST_MSG_VIEW_WINDOW (window));
1121 if (priv->row_reference) {
1122 path = gtk_tree_row_reference_get_path (priv->row_reference);
1123 if (gtk_tree_model_get_iter (priv->header_model, &iter, path)) {
1125 gtk_tree_model_get (priv->header_model, &iter,
1126 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
1128 message_reader (window, priv, header, NULL, NULL, priv->row_reference);
1129 g_object_unref (header);
1131 gtk_tree_path_free (path);
1133 /* Check dimming rules */
1134 modest_ui_actions_check_toolbar_dimming_rules (MODEST_WINDOW (window));
1135 modest_ui_actions_check_menu_dimming_rules (MODEST_WINDOW (window));
1136 modest_window_check_dimming_rules_group (MODEST_WINDOW (window), MODEST_DIMMING_RULES_CLIPBOARD);
1138 return MODEST_WINDOW(window);
1142 modest_msg_view_window_new_for_search_result (TnyMsg *msg,
1143 const gchar *modest_account_name,
1144 const gchar *mailbox,
1145 const gchar *msg_uid)
1147 ModestMsgViewWindow *window = NULL;
1148 ModestMsgViewWindowPrivate *priv = NULL;
1149 ModestWindowMgr *mgr = NULL;
1151 mgr = modest_runtime_get_window_mgr ();
1152 window = MODEST_MSG_VIEW_WINDOW (modest_window_mgr_get_msg_view_window (mgr));
1153 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), NULL);
1154 modest_msg_view_window_construct (window, modest_account_name, mailbox, msg_uid);
1156 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1157 priv->top_msg = NULL;
1159 /* Remember that this is a search result,
1160 * so we can disable some UI appropriately: */
1161 priv->is_search_result = TRUE;
1163 tny_msg_view_set_msg (TNY_MSG_VIEW (priv->msg_view), msg);
1164 update_branding (MODEST_MSG_VIEW_WINDOW (window));
1166 update_window_title (window);
1167 /* gtk_widget_show_all (GTK_WIDGET (window));*/
1168 modest_msg_view_window_update_priority (window);
1170 /* Check dimming rules */
1171 modest_ui_actions_check_toolbar_dimming_rules (MODEST_WINDOW (window));
1172 modest_ui_actions_check_menu_dimming_rules (MODEST_WINDOW (window));
1173 modest_window_check_dimming_rules_group (MODEST_WINDOW (window), MODEST_DIMMING_RULES_CLIPBOARD);
1175 return MODEST_WINDOW(window);
1179 modest_msg_view_window_is_other_body (ModestMsgViewWindow *self)
1181 ModestMsgViewWindowPrivate *priv = NULL;
1183 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self), FALSE);
1184 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
1186 return (priv->other_body != NULL);
1190 modest_msg_view_window_new_with_other_body (TnyMsg *msg,
1191 TnyMimePart *other_body,
1193 const gchar *modest_account_name,
1194 const gchar *mailbox,
1195 const gchar *msg_uid)
1197 GObject *obj = NULL;
1198 ModestMsgViewWindowPrivate *priv;
1199 ModestWindowMgr *mgr = NULL;
1201 g_return_val_if_fail (msg, NULL);
1202 mgr = modest_runtime_get_window_mgr ();
1203 obj = G_OBJECT (modest_window_mgr_get_msg_view_window (mgr));
1204 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (obj);
1205 modest_msg_view_window_construct (MODEST_MSG_VIEW_WINDOW (obj),
1206 modest_account_name, mailbox, msg_uid);
1209 priv->other_body = g_object_ref (other_body);
1210 modest_msg_view_set_msg_with_other_body (MODEST_MSG_VIEW (priv->msg_view), msg, other_body);
1212 tny_msg_view_set_msg (TNY_MSG_VIEW (priv->msg_view), msg);
1215 priv->top_msg = g_object_ref (top_msg);
1217 priv->top_msg = NULL;
1219 update_window_title (MODEST_MSG_VIEW_WINDOW (obj));
1220 update_branding (MODEST_MSG_VIEW_WINDOW (obj));
1222 /* gtk_widget_show_all (GTK_WIDGET (obj)); */
1224 /* Check dimming rules */
1225 modest_ui_actions_check_toolbar_dimming_rules (MODEST_WINDOW (obj));
1226 modest_ui_actions_check_menu_dimming_rules (MODEST_WINDOW (obj));
1227 modest_window_check_dimming_rules_group (MODEST_WINDOW (obj), MODEST_DIMMING_RULES_CLIPBOARD);
1229 return MODEST_WINDOW(obj);
1233 modest_msg_view_window_new_for_attachment (TnyMsg *msg,
1235 const gchar *modest_account_name,
1236 const gchar *mailbox,
1237 const gchar *msg_uid)
1239 return modest_msg_view_window_new_with_other_body (msg, NULL, top_msg, modest_account_name, mailbox, msg_uid);
1243 modest_msg_view_window_on_row_changed (GtkTreeModel *header_model,
1246 ModestMsgViewWindow *window)
1248 check_dimming_rules_after_change (window);
1252 modest_msg_view_window_on_row_deleted(GtkTreeModel *header_model,
1254 ModestMsgViewWindow *window)
1256 check_dimming_rules_after_change (window);
1258 /* The window could have dissapeared */
1261 check_dimming_rules_after_change (ModestMsgViewWindow *window)
1263 modest_ui_actions_check_menu_dimming_rules (MODEST_WINDOW (window));
1264 modest_ui_actions_check_toolbar_dimming_rules (MODEST_WINDOW (window));
1268 /* On insertions we check if the folder still has the message we are
1269 * showing or do not. If do not, we do nothing. Which means we are still
1270 * not attached to any header folder and thus next/prev buttons are
1271 * still dimmed. Once the message that is shown by msg-view is found, the
1272 * new model of header-view will be attached and the references will be set.
1273 * On each further insertions dimming rules will be checked. However
1274 * this requires extra CPU time at least works.
1275 * (An message might be deleted from TnyFolder and thus will not be
1276 * inserted into the model again for example if it is removed by the
1277 * imap server and the header view is refreshed.)
1280 modest_msg_view_window_on_row_inserted (GtkTreeModel *model,
1281 GtkTreePath *tree_path,
1282 GtkTreeIter *tree_iter,
1283 ModestMsgViewWindow *window)
1285 ModestMsgViewWindowPrivate *priv = NULL;
1286 TnyHeader *header = NULL;
1288 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window));
1289 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1291 g_assert (model == priv->header_model);
1293 /* Check if the newly inserted message is the same we are actually
1294 * showing. IF not, we should remain detached from the header model
1295 * and thus prev and next toolbar buttons should remain dimmed. */
1296 gtk_tree_model_get (model, tree_iter,
1297 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
1300 if (TNY_IS_HEADER (header)) {
1303 uid = modest_tny_folder_get_header_unique_id (header);
1304 if (!g_str_equal(priv->msg_uid, uid)) {
1305 check_dimming_rules_after_change (window);
1307 g_object_unref (G_OBJECT(header));
1311 g_object_unref(G_OBJECT(header));
1314 if (priv->row_reference) {
1315 gtk_tree_row_reference_free (priv->row_reference);
1318 /* Setup row_reference for the actual msg. */
1319 priv->row_reference = gtk_tree_row_reference_new (priv->header_model, tree_path);
1320 if (priv->row_reference == NULL) {
1321 g_warning("%s: No reference for msg header item.", __FUNCTION__);
1325 /* Now set up next_row_reference. */
1326 if (priv->next_row_reference) {
1327 gtk_tree_row_reference_free (priv->next_row_reference);
1330 priv->next_row_reference = gtk_tree_row_reference_copy (priv->row_reference);
1331 select_next_valid_row (priv->header_model,
1332 &(priv->next_row_reference), FALSE, priv->is_outbox);
1334 /* Connect the remaining callbacks to become able to detect
1335 * changes in header-view. */
1336 priv->row_changed_handler =
1337 g_signal_connect (priv->header_model, "row-changed",
1338 G_CALLBACK (modest_msg_view_window_on_row_changed),
1340 priv->row_deleted_handler =
1341 g_signal_connect (priv->header_model, "row-deleted",
1342 G_CALLBACK (modest_msg_view_window_on_row_deleted),
1344 priv->rows_reordered_handler =
1345 g_signal_connect (priv->header_model, "rows-reordered",
1346 G_CALLBACK (modest_msg_view_window_on_row_reordered),
1349 check_dimming_rules_after_change (window);
1353 modest_msg_view_window_on_row_reordered (GtkTreeModel *header_model,
1357 ModestMsgViewWindow *window)
1359 ModestMsgViewWindowPrivate *priv = NULL;
1360 gboolean already_changed = FALSE;
1362 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(window);
1364 /* If the current row was reordered select the proper next
1365 valid row. The same if the next row reference changes */
1366 if (!priv->row_reference ||
1367 !gtk_tree_row_reference_valid (priv->row_reference))
1370 if (priv->next_row_reference &&
1371 gtk_tree_row_reference_valid (priv->next_row_reference)) {
1372 GtkTreePath *cur, *next;
1373 /* Check that the order is still the correct one */
1374 cur = gtk_tree_row_reference_get_path (priv->row_reference);
1375 next = gtk_tree_row_reference_get_path (priv->next_row_reference);
1376 gtk_tree_path_next (cur);
1377 if (gtk_tree_path_compare (cur, next) != 0) {
1378 gtk_tree_row_reference_free (priv->next_row_reference);
1379 priv->next_row_reference = gtk_tree_row_reference_copy (priv->row_reference);
1380 select_next_valid_row (header_model, &(priv->next_row_reference), FALSE, priv->is_outbox);
1381 already_changed = TRUE;
1383 gtk_tree_path_free (cur);
1384 gtk_tree_path_free (next);
1386 if (priv->next_row_reference)
1387 gtk_tree_row_reference_free (priv->next_row_reference);
1388 /* Update next row reference */
1389 priv->next_row_reference = gtk_tree_row_reference_copy (priv->row_reference);
1390 select_next_valid_row (header_model, &(priv->next_row_reference), FALSE, priv->is_outbox);
1391 already_changed = TRUE;
1394 check_dimming_rules_after_change (window);
1397 /* The modest_msg_view_window_update_model_replaced implements update
1398 * function for ModestHeaderViewObserver. Checks whether the TnyFolder
1399 * actually belongs to the header-view is the same as the TnyFolder of
1400 * the message of msg-view or not. If they are different, there is
1401 * nothing to do. If they are the same, then the model has replaced and
1402 * the reference in msg-view shall be replaced from the old model to
1403 * the new model. In this case the view will be detached from it's
1404 * header folder. From this point the next/prev buttons are dimmed.
1407 modest_msg_view_window_update_model_replaced (ModestHeaderViewObserver *observer,
1408 GtkTreeModel *model,
1409 const gchar *tny_folder_id)
1411 ModestMsgViewWindowPrivate *priv = NULL;
1412 ModestMsgViewWindow *window = NULL;
1414 g_assert(MODEST_IS_HEADER_VIEW_OBSERVER(observer));
1415 g_assert(MODEST_IS_MSG_VIEW_WINDOW(observer));
1417 window = MODEST_MSG_VIEW_WINDOW(observer);
1418 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(window);
1420 /* If there is an other folder in the header-view then we do
1421 * not care about it's model (msg list). Else if the
1422 * header-view shows the folder the msg shown by us is in, we
1423 * shall replace our model reference and make some check. */
1424 if(model == NULL || tny_folder_id == NULL ||
1425 (priv->header_folder_id && !g_str_equal(tny_folder_id, priv->header_folder_id)))
1428 /* Model is changed(replaced), so we should forget the old
1429 * one. Because there might be other references and there
1430 * might be some change on the model even if we unreferenced
1431 * it, we need to disconnect our signals here. */
1432 if (priv->header_model) {
1433 if (g_signal_handler_is_connected(G_OBJECT (priv->header_model),
1434 priv->row_changed_handler))
1435 g_signal_handler_disconnect(G_OBJECT (priv->header_model),
1436 priv->row_changed_handler);
1437 if (g_signal_handler_is_connected(G_OBJECT (priv->header_model),
1438 priv->row_deleted_handler))
1439 g_signal_handler_disconnect(G_OBJECT (priv->header_model),
1440 priv->row_deleted_handler);
1441 if (g_signal_handler_is_connected(G_OBJECT (priv->header_model),
1442 priv->row_inserted_handler))
1443 g_signal_handler_disconnect(G_OBJECT (priv->header_model),
1444 priv->row_inserted_handler);
1445 if (g_signal_handler_is_connected(G_OBJECT (priv->header_model),
1446 priv->rows_reordered_handler))
1447 g_signal_handler_disconnect(G_OBJECT (priv->header_model),
1448 priv->rows_reordered_handler);
1451 if (priv->row_reference)
1452 gtk_tree_row_reference_free (priv->row_reference);
1453 if (priv->next_row_reference)
1454 gtk_tree_row_reference_free (priv->next_row_reference);
1455 g_object_unref(priv->header_model);
1458 priv->row_changed_handler = 0;
1459 priv->row_deleted_handler = 0;
1460 priv->row_inserted_handler = 0;
1461 priv->rows_reordered_handler = 0;
1462 priv->next_row_reference = NULL;
1463 priv->row_reference = NULL;
1464 priv->header_model = NULL;
1467 priv->header_model = g_object_ref (model);
1469 /* Also we must connect to the new model for row insertions.
1470 * Only for insertions now. We will need other ones only after
1471 * the msg is show by msg-view is added to the new model. */
1472 priv->row_inserted_handler =
1473 g_signal_connect (priv->header_model, "row-inserted",
1474 G_CALLBACK(modest_msg_view_window_on_row_inserted),
1477 modest_ui_actions_check_menu_dimming_rules(MODEST_WINDOW(window));
1478 modest_ui_actions_check_toolbar_dimming_rules(MODEST_WINDOW(window));
1482 modest_msg_view_window_toolbar_on_transfer_mode (ModestMsgViewWindow *self)
1484 ModestMsgViewWindowPrivate *priv= NULL;
1486 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self), FALSE);
1487 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
1489 return priv->progress_hint;
1493 modest_msg_view_window_get_header (ModestMsgViewWindow *self)
1495 ModestMsgViewWindowPrivate *priv= NULL;
1497 TnyHeader *header = NULL;
1498 GtkTreePath *path = NULL;
1501 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self), NULL);
1502 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
1504 /* If the message was not obtained from a treemodel,
1505 * for instance if it was opened directly by the search UI:
1507 if (priv->header_model == NULL ||
1508 priv->row_reference == NULL ||
1509 !gtk_tree_row_reference_valid (priv->row_reference)) {
1510 msg = modest_msg_view_window_get_message (self);
1512 header = tny_msg_get_header (msg);
1513 g_object_unref (msg);
1518 /* Get iter of the currently selected message in the header view: */
1519 path = gtk_tree_row_reference_get_path (priv->row_reference);
1520 g_return_val_if_fail (path != NULL, NULL);
1521 gtk_tree_model_get_iter (priv->header_model,
1525 /* Get current message header */
1526 gtk_tree_model_get (priv->header_model, &iter,
1527 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
1530 gtk_tree_path_free (path);
1535 modest_msg_view_window_get_message (ModestMsgViewWindow *self)
1537 ModestMsgViewWindowPrivate *priv;
1539 g_return_val_if_fail (self, NULL);
1541 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(self);
1543 return tny_msg_view_get_msg (TNY_MSG_VIEW (priv->msg_view));
1547 modest_msg_view_window_get_top_message (ModestMsgViewWindow *self)
1549 ModestMsgViewWindowPrivate *priv;
1551 g_return_val_if_fail (self, NULL);
1553 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(self);
1556 return g_object_ref (priv->top_msg);
1562 modest_msg_view_window_get_message_uid (ModestMsgViewWindow *self)
1564 ModestMsgViewWindowPrivate *priv;
1566 g_return_val_if_fail (self, NULL);
1568 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
1570 return (const gchar*) priv->msg_uid;
1573 /* Used for the Ctrl+F accelerator */
1575 modest_msg_view_window_toggle_find_toolbar (GtkWidget *obj,
1578 ModestMsgViewWindow *window = MODEST_MSG_VIEW_WINDOW (data);
1579 ModestMsgViewWindowPrivate *priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1581 if (GTK_WIDGET_VISIBLE (priv->find_toolbar)) {
1582 modest_msg_view_window_find_toolbar_close (obj, data);
1584 modest_msg_view_window_show_find_toolbar (obj, data);
1588 /* Handler for menu option */
1590 modest_msg_view_window_show_find_toolbar (GtkWidget *obj,
1593 ModestMsgViewWindow *window = MODEST_MSG_VIEW_WINDOW (data);
1594 ModestMsgViewWindowPrivate *priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1596 gtk_widget_show (priv->find_toolbar);
1597 hildon_find_toolbar_highlight_entry (HILDON_FIND_TOOLBAR (priv->find_toolbar), TRUE);
1600 /* Handler for click on the "X" close button in find toolbar */
1602 modest_msg_view_window_find_toolbar_close (GtkWidget *widget,
1603 ModestMsgViewWindow *obj)
1605 ModestMsgViewWindowPrivate *priv;
1607 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (obj);
1610 gtk_widget_hide (priv->find_toolbar);
1611 modest_msg_view_grab_focus (MODEST_MSG_VIEW (priv->msg_view));
1615 modest_msg_view_window_find_toolbar_search (GtkWidget *widget,
1616 ModestMsgViewWindow *obj)
1618 gchar *current_search;
1619 ModestMsgViewWindowPrivate *priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (obj);
1621 if (modest_mime_part_view_is_empty (MODEST_MIME_PART_VIEW (priv->msg_view))) {
1622 hildon_banner_show_information (NULL, NULL, _("mail_ib_nothing_to_find"));
1626 g_object_get (G_OBJECT (widget), "prefix", ¤t_search, NULL);
1628 if ((current_search == NULL) || (strcmp (current_search, "") == 0)) {
1629 g_free (current_search);
1630 hildon_banner_show_information (NULL, NULL, _CS("ecdg_ib_find_rep_enter_text"));
1634 if ((priv->last_search == NULL) || (strcmp (priv->last_search, current_search) != 0)) {
1636 g_free (priv->last_search);
1637 priv->last_search = g_strdup (current_search);
1638 result = modest_isearch_view_search (MODEST_ISEARCH_VIEW (priv->msg_view),
1641 hildon_banner_show_information (NULL, NULL,
1642 _HL("ckct_ib_find_no_matches"));
1643 g_free (priv->last_search);
1644 priv->last_search = NULL;
1646 hildon_find_toolbar_highlight_entry (HILDON_FIND_TOOLBAR (priv->find_toolbar), TRUE);
1649 if (!modest_isearch_view_search_next (MODEST_ISEARCH_VIEW (priv->msg_view))) {
1650 hildon_banner_show_information (NULL, NULL,
1651 _HL("ckct_ib_find_search_complete"));
1652 g_free (priv->last_search);
1653 priv->last_search = NULL;
1655 hildon_find_toolbar_highlight_entry (HILDON_FIND_TOOLBAR (priv->find_toolbar), TRUE);
1659 g_free (current_search);
1664 modest_msg_view_window_set_zoom (ModestWindow *window,
1667 ModestMsgViewWindowPrivate *priv;
1668 ModestWindowPrivate *parent_priv;
1670 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window));
1672 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1673 parent_priv = MODEST_WINDOW_GET_PRIVATE (window);
1674 modest_zoomable_set_zoom (MODEST_ZOOMABLE (priv->msg_view), zoom);
1679 modest_msg_view_window_get_zoom (ModestWindow *window)
1681 ModestMsgViewWindowPrivate *priv;
1683 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), 1.0);
1685 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1686 return modest_zoomable_get_zoom (MODEST_ZOOMABLE (priv->msg_view));
1690 modest_msg_view_window_zoom_plus (ModestWindow *window)
1693 ModestMsgViewWindowPrivate *priv;
1697 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), 1.0);
1698 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1700 zoom_level = modest_zoomable_get_zoom (MODEST_ZOOMABLE (priv->msg_view));
1702 if (zoom_level >= 2.0) {
1703 hildon_banner_show_information (NULL, NULL,
1704 _CS("ckct_ib_max_zoom_level_reached"));
1706 } else if (zoom_level >= 1.5) {
1708 } else if (zoom_level >= 1.2) {
1710 } else if (zoom_level >= 1.0) {
1712 } else if (zoom_level >= 0.8) {
1714 } else if (zoom_level >= 0.5) {
1720 /* set zoom level */
1721 int_zoom = (gint) rint (zoom_level*100.0+0.1);
1722 banner_text = g_strdup_printf (_HL("wdgt_ib_zoom"), int_zoom);
1723 modest_platform_information_banner (GTK_WIDGET (window), NULL, banner_text);
1724 g_free (banner_text);
1725 modest_zoomable_set_zoom (MODEST_ZOOMABLE (priv->msg_view), zoom_level);
1731 modest_msg_view_window_zoom_minus (ModestWindow *window)
1734 ModestMsgViewWindowPrivate *priv;
1738 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), 1.0);
1739 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1741 zoom_level = modest_zoomable_get_zoom (MODEST_ZOOMABLE (priv->msg_view));
1743 if (zoom_level <= 0.5) {
1744 hildon_banner_show_information (NULL, NULL,
1745 _CS("ckct_ib_min_zoom_level_reached"));
1747 } else if (zoom_level <= 0.8) {
1749 } else if (zoom_level <= 1.0) {
1751 } else if (zoom_level <= 1.2) {
1753 } else if (zoom_level <= 1.5) {
1755 } else if (zoom_level <= 2.0) {
1761 /* set zoom level */
1762 int_zoom = (gint) rint (zoom_level*100.0+0.1);
1763 banner_text = g_strdup_printf (_HL("wdgt_ib_zoom"), int_zoom);
1764 modest_platform_information_banner (GTK_WIDGET (window), NULL, banner_text);
1765 g_free (banner_text);
1766 modest_zoomable_set_zoom (MODEST_ZOOMABLE (priv->msg_view), zoom_level);
1772 modest_msg_view_window_key_event (GtkWidget *window,
1778 focus = gtk_window_get_focus (GTK_WINDOW (window));
1780 /* for the find toolbar case */
1781 if (focus && GTK_IS_ENTRY (focus)) {
1782 if (event->keyval == GDK_BackSpace) {
1784 copy = gdk_event_copy ((GdkEvent *) event);
1785 gtk_widget_event (focus, copy);
1786 gdk_event_free (copy);
1796 modest_msg_view_window_last_message_selected (ModestMsgViewWindow *window)
1799 ModestMsgViewWindowPrivate *priv;
1800 GtkTreeIter tmp_iter;
1801 gboolean is_last_selected;
1803 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), TRUE);
1804 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1806 /*if no model (so no rows at all), then virtually we are the last*/
1807 if (!priv->header_model || !priv->row_reference)
1810 if (!gtk_tree_row_reference_valid (priv->row_reference))
1813 path = gtk_tree_row_reference_get_path (priv->row_reference);
1817 is_last_selected = TRUE;
1818 while (is_last_selected) {
1820 gtk_tree_path_next (path);
1821 if (!gtk_tree_model_get_iter (priv->header_model, &tmp_iter, path))
1823 gtk_tree_model_get (priv->header_model, &tmp_iter,
1824 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
1827 if (msg_is_visible (header, priv->is_outbox))
1828 is_last_selected = FALSE;
1829 g_object_unref(G_OBJECT(header));
1832 gtk_tree_path_free (path);
1833 return is_last_selected;
1837 modest_msg_view_window_has_headers_model (ModestMsgViewWindow *window)
1839 ModestMsgViewWindowPrivate *priv;
1841 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), TRUE);
1842 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1844 return priv->header_model != NULL;
1848 modest_msg_view_window_is_search_result (ModestMsgViewWindow *window)
1850 ModestMsgViewWindowPrivate *priv;
1852 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), TRUE);
1853 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1855 return priv->is_search_result;
1859 msg_is_visible (TnyHeader *header, gboolean check_outbox)
1861 if ((tny_header_get_flags(header) & TNY_HEADER_FLAG_DELETED))
1863 if (!check_outbox) {
1866 ModestTnySendQueueStatus status;
1867 status = modest_tny_all_send_queues_get_msg_status (header);
1868 return ((status != MODEST_TNY_SEND_QUEUE_FAILED) &&
1869 (status != MODEST_TNY_SEND_QUEUE_SENDING));
1874 modest_msg_view_window_first_message_selected (ModestMsgViewWindow *window)
1877 ModestMsgViewWindowPrivate *priv;
1878 gboolean is_first_selected;
1879 GtkTreeIter tmp_iter;
1881 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), TRUE);
1882 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1884 /*if no model (so no rows at all), then virtually we are the first*/
1885 if (!priv->header_model || !priv->row_reference)
1888 if (!gtk_tree_row_reference_valid (priv->row_reference))
1891 path = gtk_tree_row_reference_get_path (priv->row_reference);
1895 is_first_selected = TRUE;
1896 while (is_first_selected) {
1898 if(!gtk_tree_path_prev (path))
1900 /* Here the 'if' is needless for logic, but let make sure
1901 * iter is valid for gtk_tree_model_get. */
1902 if (!gtk_tree_model_get_iter (priv->header_model, &tmp_iter, path))
1904 gtk_tree_model_get (priv->header_model, &tmp_iter,
1905 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
1908 if (msg_is_visible (header, priv->is_outbox))
1909 is_first_selected = FALSE;
1910 g_object_unref(G_OBJECT(header));
1913 gtk_tree_path_free (path);
1914 return is_first_selected;
1921 GtkTreeRowReference *row_reference;
1925 message_reader_performer (gboolean canceled,
1927 GtkWindow *parent_window,
1928 TnyAccount *account,
1931 ModestMailOperation *mail_op = NULL;
1932 MsgReaderInfo *info;
1934 info = (MsgReaderInfo *) user_data;
1935 if (canceled || err) {
1936 update_window_title (MODEST_MSG_VIEW_WINDOW (parent_window));
1937 modest_ui_actions_on_close_window (NULL, MODEST_WINDOW (parent_window));
1941 /* Register the header - it'll be unregistered in the callback */
1943 modest_window_mgr_register_header (modest_runtime_get_window_mgr (), info->header, NULL);
1945 /* New mail operation */
1946 mail_op = modest_mail_operation_new_with_error_handling (G_OBJECT(parent_window),
1947 modest_ui_actions_disk_operations_error_handler,
1950 modest_mail_operation_queue_add (modest_runtime_get_mail_operation_queue (), mail_op);
1952 modest_mail_operation_get_msg (mail_op, info->header, TRUE, view_msg_cb, info->row_reference);
1954 modest_mail_operation_find_msg (mail_op, info->folder, info->msg_uid, TRUE, view_msg_cb, NULL);
1955 g_object_unref (mail_op);
1957 /* Update dimming rules */
1958 modest_ui_actions_check_toolbar_dimming_rules (MODEST_WINDOW (parent_window));
1959 modest_ui_actions_check_menu_dimming_rules (MODEST_WINDOW (parent_window));
1962 /* Frees. The row_reference will be freed by the view_msg_cb callback */
1963 g_free (info->msg_uid);
1965 g_object_unref (info->folder);
1967 g_object_unref (info->header);
1968 g_slice_free (MsgReaderInfo, info);
1973 * Reads the message whose summary item is @header. It takes care of
1974 * several things, among others:
1976 * If the message was not previously downloaded then ask the user
1977 * before downloading. If there is no connection launch the connection
1978 * dialog. Update toolbar dimming rules.
1980 * Returns: TRUE if the mail operation was started, otherwise if the
1981 * user do not want to download the message, or if the user do not
1982 * want to connect, then the operation is not issued
1985 message_reader (ModestMsgViewWindow *window,
1986 ModestMsgViewWindowPrivate *priv,
1988 const gchar *msg_uid,
1990 GtkTreeRowReference *row_reference)
1992 ModestWindowMgr *mgr;
1993 TnyAccount *account = NULL;
1994 MsgReaderInfo *info;
1996 /* We set the header from model while we're loading */
1997 tny_header_view_set_header (TNY_HEADER_VIEW (priv->msg_view), header);
1998 gtk_window_set_title (GTK_WINDOW (window), _CS("ckdg_pb_updating"));
2004 g_object_ref (folder);
2006 mgr = modest_runtime_get_window_mgr ();
2007 /* Msg download completed */
2008 if (!header || !(tny_header_get_flags (header) & TNY_HEADER_FLAG_CACHED)) {
2010 /* Ask the user if he wants to download the message if
2012 if (!tny_device_is_online (modest_runtime_get_device())) {
2013 GtkResponseType response;
2015 response = modest_platform_run_confirmation_dialog (GTK_WINDOW (window),
2016 _("mcen_nc_get_msg"));
2017 if (response == GTK_RESPONSE_CANCEL) {
2018 update_window_title (window);
2023 folder = tny_header_get_folder (header);
2025 info = g_slice_new (MsgReaderInfo);
2026 info->msg_uid = g_strdup (msg_uid);
2028 info->header = g_object_ref (header);
2030 info->header = NULL;
2032 info->folder = g_object_ref (folder);
2034 info->folder = NULL;
2035 if (row_reference) {
2036 info->row_reference = gtk_tree_row_reference_copy (row_reference);
2038 info->row_reference = NULL;
2041 /* Offer the connection dialog if necessary */
2042 modest_platform_connect_if_remote_and_perform ((GtkWindow *) window,
2044 TNY_FOLDER_STORE (folder),
2045 message_reader_performer,
2048 g_object_unref (folder);
2054 folder = tny_header_get_folder (header);
2057 account = tny_folder_get_account (folder);
2059 info = g_slice_new (MsgReaderInfo);
2060 info->msg_uid = g_strdup (msg_uid);
2062 info->folder = g_object_ref (folder);
2064 info->folder = NULL;
2066 info->header = g_object_ref (header);
2068 info->header = NULL;
2070 info->row_reference = gtk_tree_row_reference_copy (row_reference);
2072 info->row_reference = NULL;
2074 message_reader_performer (FALSE, NULL, (GtkWindow *) window, account, info);
2076 g_object_unref (account);
2078 g_object_unref (folder);
2084 modest_msg_view_window_select_next_message (ModestMsgViewWindow *window)
2086 ModestMsgViewWindowPrivate *priv;
2087 GtkTreePath *path= NULL;
2088 GtkTreeIter tmp_iter;
2090 gboolean retval = TRUE;
2091 GtkTreeRowReference *row_reference = NULL;
2093 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), FALSE);
2094 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
2096 if (!priv->row_reference)
2099 /* Update the next row reference if it's not valid. This could
2100 happen if for example the header which it was pointing to,
2101 was deleted. The best place to do it is in the row-deleted
2102 handler but the tinymail model do not work like the glib
2103 tree models and reports the deletion when the row is still
2105 if (!gtk_tree_row_reference_valid (priv->next_row_reference)) {
2106 if (priv->next_row_reference) {
2107 gtk_tree_row_reference_free (priv->next_row_reference);
2109 if (gtk_tree_row_reference_valid (priv->row_reference)) {
2110 priv->next_row_reference = gtk_tree_row_reference_copy (priv->row_reference);
2111 select_next_valid_row (priv->header_model, &(priv->next_row_reference), FALSE, priv->is_outbox);
2113 priv->next_row_reference = NULL;
2116 if (priv->next_row_reference)
2117 path = gtk_tree_row_reference_get_path (priv->next_row_reference);
2121 row_reference = gtk_tree_row_reference_copy (priv->next_row_reference);
2123 gtk_tree_model_get_iter (priv->header_model,
2126 gtk_tree_path_free (path);
2128 gtk_tree_model_get (priv->header_model, &tmp_iter,
2129 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
2132 /* Read the message & show it */
2133 if (!message_reader (window, priv, header, NULL, NULL, row_reference)) {
2136 gtk_tree_row_reference_free (row_reference);
2139 g_object_unref (header);
2145 modest_msg_view_window_select_previous_message (ModestMsgViewWindow *window)
2147 ModestMsgViewWindowPrivate *priv = NULL;
2149 gboolean finished = FALSE;
2150 gboolean retval = FALSE;
2152 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), FALSE);
2153 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
2155 if (priv->row_reference && !gtk_tree_row_reference_valid (priv->row_reference)) {
2156 gtk_tree_row_reference_free (priv->row_reference);
2157 priv->row_reference = NULL;
2160 /* Return inmediatly if there is no header model */
2161 if (!priv->header_model || !priv->row_reference)
2164 path = gtk_tree_row_reference_get_path (priv->row_reference);
2165 while (!finished && gtk_tree_path_prev (path)) {
2169 gtk_tree_model_get_iter (priv->header_model, &iter, path);
2170 gtk_tree_model_get (priv->header_model, &iter,
2171 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
2175 if (msg_is_visible (header, priv->is_outbox)) {
2176 GtkTreeRowReference *row_reference;
2177 row_reference = gtk_tree_row_reference_new (priv->header_model, path);
2178 /* Read the message & show it */
2179 retval = message_reader (window, priv, header, NULL, NULL, row_reference);
2180 gtk_tree_row_reference_free (row_reference);
2184 g_object_unref (header);
2188 gtk_tree_path_free (path);
2193 view_msg_cb (ModestMailOperation *mail_op,
2200 ModestMsgViewWindow *self = NULL;
2201 ModestMsgViewWindowPrivate *priv = NULL;
2202 GtkTreeRowReference *row_reference = NULL;
2204 /* Unregister the header (it was registered before creating the mail operation) */
2205 modest_window_mgr_unregister_header (modest_runtime_get_window_mgr (), header);
2207 row_reference = (GtkTreeRowReference *) user_data;
2210 gtk_tree_row_reference_free (row_reference);
2211 self = (ModestMsgViewWindow *) modest_mail_operation_get_source (mail_op);
2213 /* Restore window title */
2214 update_window_title (self);
2215 modest_ui_actions_on_close_window (NULL, MODEST_WINDOW (self));
2216 g_object_unref (self);
2221 /* If there was any error */
2222 if (!modest_ui_actions_msg_retrieval_check (mail_op, header, msg)) {
2224 gtk_tree_row_reference_free (row_reference);
2225 self = (ModestMsgViewWindow *) modest_mail_operation_get_source (mail_op);
2227 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
2228 /* First we check if the parent is a folder window */
2229 if (priv->msg_uid && !modest_hildon2_window_mgr_get_folder_window (MODEST_HILDON2_WINDOW_MGR (modest_runtime_get_window_mgr ()))) {
2231 TnyAccount *account = NULL;
2232 GtkWidget *header_window = NULL;
2234 is_merge = g_str_has_prefix (priv->msg_uid, "merge:");
2236 /* Get the account */
2238 account = tny_account_store_find_account (TNY_ACCOUNT_STORE (modest_runtime_get_account_store ()),
2241 if (is_merge || account) {
2242 TnyFolder *folder = NULL;
2244 /* Try to get the message, if it's already downloaded
2245 we don't need to connect */
2247 folder = modest_tny_folder_store_find_folder_from_uri (TNY_FOLDER_STORE (account),
2250 ModestTnyAccountStore *account_store;
2251 ModestTnyLocalFoldersAccount *local_folders_account;
2253 account_store = modest_runtime_get_account_store ();
2254 local_folders_account = MODEST_TNY_LOCAL_FOLDERS_ACCOUNT (
2255 modest_tny_account_store_get_local_folders_account (account_store));
2256 folder = modest_tny_local_folders_account_get_merged_outbox (local_folders_account);
2257 g_object_unref (local_folders_account);
2259 if (account) g_object_unref (account);
2262 header_window = (GtkWidget *)
2263 modest_header_window_new (
2265 modest_window_get_active_account (MODEST_WINDOW (self)),
2266 modest_window_get_active_mailbox (MODEST_WINDOW (self)));
2267 if (!modest_window_mgr_register_window (modest_runtime_get_window_mgr (),
2268 MODEST_WINDOW (header_window),
2270 gtk_widget_destroy (GTK_WIDGET (header_window));
2272 gtk_widget_show_all (GTK_WIDGET (header_window));
2274 g_object_unref (folder);
2280 /* Restore window title */
2281 update_window_title (self);
2282 modest_ui_actions_on_close_window (NULL, MODEST_WINDOW (self));
2283 g_object_unref (self);
2288 /* Get the window */
2289 self = (ModestMsgViewWindow *) modest_mail_operation_get_source (mail_op);
2290 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self));
2291 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
2293 /* Update the row reference */
2294 if (priv->row_reference != NULL) {
2295 gtk_tree_row_reference_free (priv->row_reference);
2296 priv->row_reference = (row_reference && gtk_tree_row_reference_valid (row_reference))?gtk_tree_row_reference_copy (row_reference):NULL;
2297 if (priv->next_row_reference != NULL) {
2298 gtk_tree_row_reference_free (priv->next_row_reference);
2300 if (priv->row_reference) {
2301 priv->next_row_reference = gtk_tree_row_reference_copy (priv->row_reference);
2302 select_next_valid_row (priv->header_model, &(priv->next_row_reference), TRUE, priv->is_outbox);
2304 priv->next_row_reference = NULL;
2308 /* Mark header as read */
2309 if (!(tny_header_get_flags (header) & TNY_HEADER_FLAG_SEEN))
2310 tny_header_set_flag (header, TNY_HEADER_FLAG_SEEN);
2312 /* Set new message */
2313 if (priv->msg_view != NULL && TNY_IS_MSG_VIEW (priv->msg_view)) {
2314 tny_msg_view_set_msg (TNY_MSG_VIEW (priv->msg_view), msg);
2315 modest_msg_view_window_update_priority (self);
2316 update_window_title (MODEST_MSG_VIEW_WINDOW (self));
2317 update_branding (MODEST_MSG_VIEW_WINDOW (self));
2318 modest_msg_view_grab_focus (MODEST_MSG_VIEW (priv->msg_view));
2321 /* Set the new message uid of the window */
2322 if (priv->msg_uid) {
2323 g_free (priv->msg_uid);
2324 priv->msg_uid = modest_tny_folder_get_header_unique_id (header);
2327 /* Notify the observers */
2328 g_signal_emit (G_OBJECT (self), signals[MSG_CHANGED_SIGNAL],
2329 0, priv->header_model, priv->row_reference);
2331 /* Sync the flags if the message is not opened from a header
2332 model, i.e, if it's opened from a notification */
2333 if (!priv->header_model)
2337 g_object_unref (self);
2339 gtk_tree_row_reference_free (row_reference);
2343 modest_msg_view_window_get_folder_type (ModestMsgViewWindow *window)
2345 ModestMsgViewWindowPrivate *priv;
2347 TnyFolderType folder_type;
2349 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
2351 folder_type = TNY_FOLDER_TYPE_UNKNOWN;
2353 msg = tny_msg_view_get_msg (TNY_MSG_VIEW (priv->msg_view));
2357 folder = tny_msg_get_folder (msg);
2359 folder_type = modest_tny_folder_guess_folder_type (folder);
2360 g_object_unref (folder);
2362 g_object_unref (msg);
2370 modest_msg_view_window_update_priority (ModestMsgViewWindow *window)
2372 ModestMsgViewWindowPrivate *priv;
2373 TnyHeader *header = NULL;
2374 TnyHeaderFlags flags = 0;
2376 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
2378 if (priv->header_model && priv->row_reference && gtk_tree_row_reference_valid (priv->row_reference)) {
2380 GtkTreePath *path = NULL;
2382 path = gtk_tree_row_reference_get_path (priv->row_reference);
2383 g_return_if_fail (path != NULL);
2384 gtk_tree_model_get_iter (priv->header_model,
2386 gtk_tree_row_reference_get_path (priv->row_reference));
2388 gtk_tree_model_get (priv->header_model, &iter, TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
2390 gtk_tree_path_free (path);
2393 msg = tny_msg_view_get_msg (TNY_MSG_VIEW (priv->msg_view));
2395 header = tny_msg_get_header (msg);
2396 g_object_unref (msg);
2401 flags = tny_header_get_flags (header);
2402 g_object_unref(G_OBJECT(header));
2405 modest_msg_view_set_priority (MODEST_MSG_VIEW(priv->msg_view), flags);
2410 toolbar_resize (ModestMsgViewWindow *self)
2412 ModestMsgViewWindowPrivate *priv = NULL;
2413 ModestWindowPrivate *parent_priv = NULL;
2415 gint static_button_size;
2416 ModestWindowMgr *mgr;
2418 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self));
2419 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
2420 parent_priv = MODEST_WINDOW_GET_PRIVATE(self);
2422 mgr = modest_runtime_get_window_mgr ();
2423 static_button_size = modest_window_mgr_get_fullscreen_mode (mgr)?120:120;
2425 if (parent_priv->toolbar) {
2426 /* Set expandable and homogeneous tool buttons */
2427 widget = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/ToolbarMessageReply");
2428 gtk_tool_item_set_expand (GTK_TOOL_ITEM (widget), TRUE);
2429 gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (widget), TRUE);
2430 widget = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/ToolbarMessageReplyAll");
2431 gtk_tool_item_set_expand (GTK_TOOL_ITEM (widget), TRUE);
2432 gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (widget), TRUE);
2433 widget = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/ToolbarMessageForward");
2434 gtk_tool_item_set_expand (GTK_TOOL_ITEM (widget), TRUE);
2435 gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (widget), TRUE);
2436 widget = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/ToolbarDeleteMessage");
2437 gtk_tool_item_set_expand (GTK_TOOL_ITEM (widget), TRUE);
2438 gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (widget), TRUE);
2439 widget = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/ToolbarDownloadExternalImages");
2440 gtk_tool_item_set_expand (GTK_TOOL_ITEM (widget), TRUE);
2441 gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (widget), TRUE);
2442 gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (priv->next_toolitem), TRUE);
2443 gtk_tool_item_set_expand (GTK_TOOL_ITEM (priv->next_toolitem), TRUE);
2444 gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (priv->prev_toolitem), TRUE);
2445 gtk_tool_item_set_expand (GTK_TOOL_ITEM (priv->prev_toolitem), TRUE);
2450 modest_msg_view_window_show_toolbar (ModestWindow *self,
2451 gboolean show_toolbar)
2453 ModestMsgViewWindowPrivate *priv = NULL;
2454 ModestWindowPrivate *parent_priv;
2456 parent_priv = MODEST_WINDOW_GET_PRIVATE(self);
2457 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(self);
2459 /* Set optimized view status */
2460 priv->optimized_view = !show_toolbar;
2462 if (!parent_priv->toolbar) {
2463 parent_priv->toolbar = gtk_ui_manager_get_widget (parent_priv->ui_manager,
2465 gtk_toolbar_set_icon_size (GTK_TOOLBAR (parent_priv->toolbar), HILDON_ICON_SIZE_FINGER);
2466 gtk_widget_set_no_show_all (parent_priv->toolbar, TRUE);
2468 priv->next_toolitem = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/ToolbarMessageNext");
2469 priv->prev_toolitem = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/ToolbarMessageBack");
2470 toolbar_resize (MODEST_MSG_VIEW_WINDOW (self));
2473 hildon_window_add_toolbar (HILDON_WINDOW (self),
2474 GTK_TOOLBAR (parent_priv->toolbar));
2479 /* Quick hack: this prevents toolbar icons "dance" when progress bar show status is changed */
2480 /* TODO: resize mode migth be GTK_RESIZE_QUEUE, in order to avoid unneccesary shows */
2481 gtk_container_set_resize_mode (GTK_CONTAINER(parent_priv->toolbar), GTK_RESIZE_IMMEDIATE);
2483 gtk_widget_show (GTK_WIDGET (parent_priv->toolbar));
2484 if (modest_msg_view_window_transfer_mode_enabled (MODEST_MSG_VIEW_WINDOW (self)))
2485 set_progress_hint (MODEST_MSG_VIEW_WINDOW (self), TRUE);
2487 set_progress_hint (MODEST_MSG_VIEW_WINDOW (self), FALSE);
2490 gtk_widget_set_no_show_all (parent_priv->toolbar, TRUE);
2491 gtk_widget_hide (GTK_WIDGET (parent_priv->toolbar));
2496 modest_msg_view_window_clipboard_owner_change (GtkClipboard *clipboard,
2498 ModestMsgViewWindow *window)
2500 if (!GTK_WIDGET_VISIBLE (window))
2503 modest_window_check_dimming_rules_group (MODEST_WINDOW (window), MODEST_DIMMING_RULES_CLIPBOARD);
2507 modest_msg_view_window_transfer_mode_enabled (ModestMsgViewWindow *self)
2509 ModestMsgViewWindowPrivate *priv;
2511 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self), FALSE);
2512 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(self);
2514 return priv->progress_hint;
2518 observers_empty (ModestMsgViewWindow *self)
2521 ModestMsgViewWindowPrivate *priv;
2522 gboolean is_empty = TRUE;
2523 guint pending_ops = 0;
2525 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(self);
2526 tmp = priv->progress_widgets;
2528 /* Check all observers */
2529 while (tmp && is_empty) {
2530 pending_ops = modest_progress_object_num_pending_operations (MODEST_PROGRESS_OBJECT(tmp->data));
2531 is_empty = pending_ops == 0;
2533 tmp = g_slist_next(tmp);
2540 on_account_removed (TnyAccountStore *account_store,
2541 TnyAccount *account,
2544 /* Do nothing if it's a transport account, because we only
2545 show the messages of a store account */
2546 if (tny_account_get_account_type(account) == TNY_ACCOUNT_TYPE_STORE) {
2547 const gchar *parent_acc = NULL;
2548 const gchar *our_acc = NULL;
2550 our_acc = modest_window_get_active_account (MODEST_WINDOW (user_data));
2551 parent_acc = modest_tny_account_get_parent_modest_account_name_for_server_account (account);
2553 /* Close this window if I'm showing a message of the removed account */
2554 if (our_acc && parent_acc && strcmp (parent_acc, our_acc) == 0)
2555 modest_ui_actions_on_close_window (NULL, MODEST_WINDOW (user_data));
2560 on_mail_operation_started (ModestMailOperation *mail_op,
2563 ModestMsgViewWindow *self;
2564 ModestMailOperationTypeOperation op_type;
2566 ModestMsgViewWindowPrivate *priv;
2567 GObject *source = NULL;
2569 self = MODEST_MSG_VIEW_WINDOW (user_data);
2570 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
2571 op_type = modest_mail_operation_get_type_operation (mail_op);
2572 tmp = priv->progress_widgets;
2573 source = modest_mail_operation_get_source(mail_op);
2574 if (G_OBJECT (self) == source) {
2575 if (op_type == MODEST_MAIL_OPERATION_TYPE_RECEIVE ||
2576 op_type == MODEST_MAIL_OPERATION_TYPE_OPEN ||
2577 op_type == MODEST_MAIL_OPERATION_TYPE_DELETE) {
2578 set_progress_hint (self, TRUE);
2580 modest_progress_object_add_operation (
2581 MODEST_PROGRESS_OBJECT (tmp->data),
2583 tmp = g_slist_next (tmp);
2587 g_object_unref (source);
2589 /* Update dimming rules */
2590 check_dimming_rules_after_change (self);
2594 on_mail_operation_finished (ModestMailOperation *mail_op,
2597 ModestMsgViewWindow *self;
2598 ModestMailOperationTypeOperation op_type;
2600 ModestMsgViewWindowPrivate *priv;
2602 self = MODEST_MSG_VIEW_WINDOW (user_data);
2603 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
2604 op_type = modest_mail_operation_get_type_operation (mail_op);
2605 tmp = priv->progress_widgets;
2607 if (op_type == MODEST_MAIL_OPERATION_TYPE_RECEIVE ||
2608 op_type == MODEST_MAIL_OPERATION_TYPE_OPEN ||
2609 op_type == MODEST_MAIL_OPERATION_TYPE_DELETE) {
2611 modest_progress_object_remove_operation (MODEST_PROGRESS_OBJECT (tmp->data),
2613 tmp = g_slist_next (tmp);
2616 /* If no more operations are being observed, NORMAL mode is enabled again */
2617 if (observers_empty (self)) {
2618 set_progress_hint (self, FALSE);
2622 /* Update dimming rules. We have to do this right here
2623 and not in view_msg_cb because at that point the
2624 transfer mode is still enabled so the dimming rule
2625 won't let the user delete the message that has been
2626 readed for example */
2627 check_dimming_rules_after_change (self);
2631 on_queue_changed (ModestMailOperationQueue *queue,
2632 ModestMailOperation *mail_op,
2633 ModestMailOperationQueueNotification type,
2634 ModestMsgViewWindow *self)
2636 ModestMsgViewWindowPrivate *priv;
2638 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
2640 /* If this operations was created by another window, do nothing */
2641 if (!modest_mail_operation_is_mine (mail_op, G_OBJECT(self)))
2644 if (type == MODEST_MAIL_OPERATION_QUEUE_OPERATION_ADDED) {
2645 priv->sighandlers = modest_signal_mgr_connect (priv->sighandlers,
2647 "operation-started",
2648 G_CALLBACK (on_mail_operation_started),
2650 priv->sighandlers = modest_signal_mgr_connect (priv->sighandlers,
2652 "operation-finished",
2653 G_CALLBACK (on_mail_operation_finished),
2655 } else if (type == MODEST_MAIL_OPERATION_QUEUE_OPERATION_REMOVED) {
2656 priv->sighandlers = modest_signal_mgr_disconnect (priv->sighandlers,
2658 "operation-started");
2659 priv->sighandlers = modest_signal_mgr_disconnect (priv->sighandlers,
2661 "operation-finished");
2666 modest_msg_view_window_get_attachments (ModestMsgViewWindow *win)
2668 ModestMsgViewWindowPrivate *priv;
2669 TnyList *selected_attachments = NULL;
2671 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (win), NULL);
2672 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (win);
2674 /* In Hildon 2.2 as there's no selection we assume we have all attachments selected */
2675 selected_attachments = modest_msg_view_get_attachments (MODEST_MSG_VIEW (priv->msg_view));
2677 return selected_attachments;
2681 ModestMsgViewWindow *self;
2683 gchar *attachment_uid;
2684 } DecodeAsyncHelper;
2687 on_decode_to_stream_async_handler (TnyMimePart *mime_part,
2693 DecodeAsyncHelper *helper = (DecodeAsyncHelper *) user_data;
2694 const gchar *content_type;
2695 ModestMsgViewWindowPrivate *priv;
2697 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (helper->self);
2699 if (cancelled || err) {
2702 if ((err->domain == TNY_ERROR_DOMAIN) &&
2703 (err->code == TNY_IO_ERROR_WRITE) &&
2704 (errno == ENOSPC)) {
2705 msg = g_strdup_printf (_KR("cerm_device_memory_full"), "");
2707 msg = g_strdup (_("mail_ib_file_operation_failed"));
2709 modest_platform_information_banner (NULL, NULL, msg);
2715 /* It could happen that the window was closed. So we
2716 assume it is a cancelation */
2717 if (!GTK_WIDGET_VISIBLE (helper->self))
2720 /* Remove the progress hint */
2721 set_progress_hint (helper->self, FALSE);
2723 content_type = tny_mime_part_get_content_type (mime_part);
2724 if (content_type && g_str_has_prefix (content_type, "message/rfc822")) {
2725 ModestWindowMgr *mgr;
2726 ModestWindow *msg_win = NULL;
2729 const gchar *mailbox;
2730 TnyStream *file_stream;
2733 fd = g_open (helper->file_path, O_RDONLY, 0644);
2736 file_stream = tny_fs_stream_new (fd);
2738 mgr = modest_runtime_get_window_mgr ();
2740 account = g_strdup (modest_window_get_active_account (MODEST_WINDOW (helper->self)));
2741 mailbox = modest_window_get_active_mailbox (MODEST_WINDOW (helper->self));
2744 account = modest_account_mgr_get_default_account (modest_runtime_get_account_mgr ());
2746 msg = tny_camel_msg_new ();
2747 tny_camel_msg_parse (msg, file_stream);
2750 top_msg = g_object_ref (priv->top_msg);
2752 top_msg = tny_msg_view_get_msg (TNY_MSG_VIEW (priv->msg_view));
2754 msg_win = modest_msg_view_window_new_for_attachment (TNY_MSG (msg), top_msg,
2755 account, mailbox, helper->attachment_uid);
2756 if (top_msg) g_object_unref (top_msg);
2757 modest_window_set_zoom (MODEST_WINDOW (msg_win),
2758 modest_window_get_zoom (MODEST_WINDOW (helper->self)));
2759 if (modest_window_mgr_register_window (mgr, msg_win, MODEST_WINDOW (helper->self)))
2760 gtk_widget_show_all (GTK_WIDGET (msg_win));
2762 gtk_widget_destroy (GTK_WIDGET (msg_win));
2763 g_object_unref (msg);
2764 g_object_unref (file_stream);
2766 modest_platform_information_banner (NULL, NULL, _("mail_ib_file_operation_failed"));
2771 /* make the file read-only */
2772 g_chmod(helper->file_path, 0444);
2774 /* Activate the file */
2775 modest_platform_activate_file (helper->file_path, content_type);
2780 g_object_unref (helper->self);
2781 g_free (helper->file_path);
2782 g_free (helper->attachment_uid);
2783 g_slice_free (DecodeAsyncHelper, helper);
2787 view_attachment_connect_handler (gboolean canceled,
2789 GtkWindow *parent_window,
2790 TnyAccount *account,
2794 if (canceled || err) {
2795 g_object_unref (part);
2799 modest_msg_view_window_view_attachment (MODEST_MSG_VIEW_WINDOW (parent_window),
2801 g_object_unref (part);
2805 modest_msg_view_window_view_attachment (ModestMsgViewWindow *window,
2806 TnyMimePart *mime_part)
2808 ModestMsgViewWindowPrivate *priv;
2809 const gchar *msg_uid;
2810 gchar *attachment_uid = NULL;
2811 gint attachment_index = 0;
2812 TnyList *attachments;
2814 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window));
2815 g_return_if_fail (TNY_IS_MIME_PART (mime_part) || (mime_part == NULL));
2816 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
2818 msg_uid = modest_msg_view_window_get_message_uid (MODEST_MSG_VIEW_WINDOW (window));
2819 attachments = modest_msg_view_get_attachments (MODEST_MSG_VIEW (priv->msg_view));
2820 attachment_index = modest_list_index (attachments, (GObject *) mime_part);
2821 g_object_unref (attachments);
2823 if (msg_uid && attachment_index >= 0) {
2824 attachment_uid = g_strdup_printf ("%s/%d", msg_uid, attachment_index);
2827 if (mime_part == NULL) {
2828 gboolean error = FALSE;
2829 TnyList *selected_attachments = modest_msg_view_get_selected_attachments (MODEST_MSG_VIEW (priv->msg_view));
2830 if (selected_attachments == NULL || tny_list_get_length (selected_attachments) == 0) {
2832 } else if (tny_list_get_length (selected_attachments) > 1) {
2833 hildon_banner_show_information (NULL, NULL, _("mcen_ib_unable_to_display_more"));
2837 iter = tny_list_create_iterator (selected_attachments);
2838 mime_part = (TnyMimePart *) tny_iterator_get_current (iter);
2839 g_object_unref (iter);
2841 if (selected_attachments)
2842 g_object_unref (selected_attachments);
2847 g_object_ref (mime_part);
2850 if (tny_mime_part_is_purged (mime_part))
2853 if (TNY_IS_CAMEL_BS_MIME_PART (mime_part) &&
2854 !tny_camel_bs_mime_part_is_fetched (TNY_CAMEL_BS_MIME_PART (mime_part))) {
2856 TnyAccount *account;
2858 is_merge = g_str_has_prefix (priv->msg_uid, "merge:");
2860 /* Get the account */
2862 account = tny_account_store_find_account (TNY_ACCOUNT_STORE (modest_runtime_get_account_store ()),
2865 if (!tny_device_is_online (modest_runtime_get_device())) {
2866 modest_platform_connect_and_perform (GTK_WINDOW (window),
2868 TNY_ACCOUNT (account),
2869 (ModestConnectedPerformer) view_attachment_connect_handler,
2870 g_object_ref (mime_part));
2875 if (!modest_tny_mime_part_is_msg (mime_part) && tny_mime_part_get_filename (mime_part)) {
2876 gchar *filepath = NULL;
2877 const gchar *att_filename = tny_mime_part_get_filename (mime_part);
2878 gboolean show_error_banner = FALSE;
2879 TnyFsStream *temp_stream = NULL;
2880 temp_stream = modest_utils_create_temp_stream (att_filename, attachment_uid,
2883 if (temp_stream != NULL) {
2884 ModestAccountMgr *mgr;
2885 DecodeAsyncHelper *helper;
2886 gboolean decode_in_provider;
2887 ModestProtocol *protocol;
2888 const gchar *account;
2890 /* Activate progress hint */
2891 set_progress_hint (window, TRUE);
2893 helper = g_slice_new0 (DecodeAsyncHelper);
2894 helper->self = g_object_ref (window);
2895 helper->file_path = g_strdup (filepath);
2896 helper->attachment_uid = g_strdup (attachment_uid);
2898 decode_in_provider = FALSE;
2899 mgr = modest_runtime_get_account_mgr ();
2900 account = modest_window_get_active_account (MODEST_WINDOW (window));
2901 if (modest_account_mgr_account_is_multimailbox (mgr, account, &protocol)) {
2902 if (MODEST_IS_ACCOUNT_PROTOCOL (protocol)) {
2904 uri = g_strconcat ("file://", filepath, NULL);
2905 decode_in_provider =
2906 modest_account_protocol_decode_part_to_stream_async (
2907 MODEST_ACCOUNT_PROTOCOL (protocol),
2910 TNY_STREAM (temp_stream),
2911 on_decode_to_stream_async_handler,
2918 if (!decode_in_provider)
2919 tny_mime_part_decode_to_stream_async (mime_part, TNY_STREAM (temp_stream),
2920 on_decode_to_stream_async_handler,
2923 g_object_unref (temp_stream);
2924 /* NOTE: files in the temporary area will be automatically
2925 * cleaned after some time if they are no longer in use */
2928 const gchar *content_type;
2929 /* the file may already exist but it isn't writable,
2930 * let's try to open it anyway */
2931 content_type = tny_mime_part_get_content_type (mime_part);
2932 modest_platform_activate_file (filepath, content_type);
2934 g_warning ("%s: modest_utils_create_temp_stream failed", __FUNCTION__);
2935 show_error_banner = TRUE;
2940 if (show_error_banner)
2941 modest_platform_information_banner (NULL, NULL, _("mail_ib_file_operation_failed"));
2942 } else if (!modest_tny_mime_part_is_msg (mime_part)) {
2943 ModestWindowMgr *mgr;
2944 ModestWindow *msg_win = NULL;
2945 TnyMsg *current_msg;
2949 current_msg = modest_msg_view_window_get_message (MODEST_MSG_VIEW_WINDOW (window));
2950 mgr = modest_runtime_get_window_mgr ();
2951 header = tny_msg_get_header (TNY_MSG (current_msg));
2952 found = modest_window_mgr_find_registered_message_uid (mgr,
2957 g_debug ("window for this body is already being created");
2961 /* it's not found, so create a new window for it */
2962 modest_window_mgr_register_header (mgr, header, attachment_uid); /* register the uid before building the window */
2963 gchar *account = g_strdup (modest_window_get_active_account (MODEST_WINDOW (window)));
2964 const gchar *mailbox = modest_window_get_active_mailbox (MODEST_WINDOW (window));
2966 account = modest_account_mgr_get_default_account (modest_runtime_get_account_mgr ());
2969 top_msg = g_object_ref (priv->top_msg);
2971 top_msg = tny_msg_view_get_msg (TNY_MSG_VIEW (priv->msg_view));
2973 msg_win = modest_msg_view_window_new_with_other_body (TNY_MSG (current_msg), TNY_MIME_PART (mime_part), top_msg,
2974 account, mailbox, attachment_uid);
2976 if (top_msg) g_object_unref (top_msg);
2978 modest_window_set_zoom (MODEST_WINDOW (msg_win),
2979 modest_window_get_zoom (MODEST_WINDOW (window)));
2980 if (modest_window_mgr_register_window (mgr, msg_win, MODEST_WINDOW (window)))
2981 gtk_widget_show_all (GTK_WIDGET (msg_win));
2983 gtk_widget_destroy (GTK_WIDGET (msg_win));
2985 g_object_unref (current_msg);
2987 /* message attachment */
2988 TnyHeader *header = NULL;
2989 ModestWindowMgr *mgr;
2990 ModestWindow *msg_win = NULL;
2993 header = tny_msg_get_header (TNY_MSG (mime_part));
2994 mgr = modest_runtime_get_window_mgr ();
2995 found = modest_window_mgr_find_registered_header (mgr, header, &msg_win);
2998 /* if it's found, but there is no msg_win, it's probably in the process of being created;
2999 * thus, we don't do anything */
3000 g_debug ("window for is already being created");
3003 /* it's not found, so create a new window for it */
3004 modest_window_mgr_register_header (mgr, header, attachment_uid); /* register the uid before building the window */
3005 gchar *account = g_strdup (modest_window_get_active_account (MODEST_WINDOW (window)));
3006 const gchar *mailbox = modest_window_get_active_mailbox (MODEST_WINDOW (window));
3008 account = modest_account_mgr_get_default_account (modest_runtime_get_account_mgr ());
3010 top_msg = g_object_ref (priv->top_msg);
3012 top_msg = tny_msg_view_get_msg (TNY_MSG_VIEW (priv->msg_view));
3013 msg_win = modest_msg_view_window_new_for_attachment (
3014 TNY_MSG (mime_part), top_msg, account,
3015 mailbox, attachment_uid);
3016 modest_window_set_zoom (MODEST_WINDOW (msg_win),
3017 modest_window_get_zoom (MODEST_WINDOW (window)));
3018 if (modest_window_mgr_register_window (mgr, msg_win, MODEST_WINDOW (window)))
3019 gtk_widget_show_all (GTK_WIDGET (msg_win));
3021 gtk_widget_destroy (GTK_WIDGET (msg_win));
3027 g_free (attachment_uid);
3029 g_object_unref (mime_part);
3041 GnomeVFSResult result;
3043 ModestMsgViewWindow *window;
3046 static void save_mime_part_info_free (SaveMimePartInfo *info, gboolean with_struct);
3047 static gboolean idle_save_mime_part_show_result (SaveMimePartInfo *info);
3048 static gpointer save_mime_part_to_file (SaveMimePartInfo *info);
3049 static void save_mime_parts_to_file_with_checks (GtkWindow *parent, SaveMimePartInfo *info);
3052 save_mime_part_info_free (SaveMimePartInfo *info, gboolean with_struct)
3056 for (node = info->pairs; node != NULL; node = g_list_next (node)) {
3057 SaveMimePartPair *pair = (SaveMimePartPair *) node->data;
3058 g_free (pair->filename);
3059 g_object_unref (pair->part);
3060 g_slice_free (SaveMimePartPair, pair);
3062 g_list_free (info->pairs);
3065 g_object_unref (info->window);
3066 info->window = NULL;
3068 g_slice_free (SaveMimePartInfo, info);
3073 idle_save_mime_part_show_result (SaveMimePartInfo *info)
3075 /* This is a GDK lock because we are an idle callback and
3076 * hildon_banner_show_information is or does Gtk+ code */
3078 gdk_threads_enter (); /* CHECKED */
3079 if (info->result == GNOME_VFS_ERROR_CANCELLED) {
3081 } else if (info->result == GNOME_VFS_OK) {
3082 hildon_banner_show_information (NULL, NULL, _CS("sfil_ib_saved"));
3083 } else if (info->result == GNOME_VFS_ERROR_NO_SPACE) {
3086 /* Check if the uri belongs to the external mmc */
3087 if (g_str_has_prefix (info->uri, g_getenv (MODEST_MMC1_VOLUMEPATH_ENV)))
3088 msg = g_strdup_printf (_KR("cerm_device_memory_full"), "");
3090 msg = g_strdup (_KR("cerm_memory_card_full"));
3091 modest_platform_information_banner (NULL, NULL, msg);
3094 hildon_banner_show_information (NULL, NULL, _("mail_ib_file_operation_failed"));
3096 save_mime_part_info_free (info, FALSE);
3097 gdk_threads_leave (); /* CHECKED */
3103 save_mime_part_to_file_connect_handler (gboolean canceled,
3105 GtkWindow *parent_window,
3106 TnyAccount *account,
3107 SaveMimePartInfo *info)
3109 if (canceled || err) {
3110 if (canceled && !err) {
3111 info->result = GNOME_VFS_ERROR_CANCELLED;
3113 g_idle_add ((GSourceFunc) idle_save_mime_part_show_result, info);
3115 g_thread_create ((GThreadFunc)save_mime_part_to_file, info, FALSE, NULL);
3120 save_mime_part_to_file_connect_idle (SaveMimePartInfo *info)
3123 TnyAccount *account;
3124 ModestMsgViewWindowPrivate *priv;
3126 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (info->window);
3128 is_merge = g_str_has_prefix (priv->msg_uid, "merge:");
3131 /* Get the account */
3133 account = tny_account_store_find_account (TNY_ACCOUNT_STORE (modest_runtime_get_account_store ()),
3136 modest_platform_connect_and_perform (GTK_WINDOW (info->window),
3138 TNY_ACCOUNT (account),
3139 (ModestConnectedPerformer) save_mime_part_to_file_connect_handler,
3143 g_object_unref (account);
3149 save_mime_part_to_file (SaveMimePartInfo *info)
3151 GnomeVFSHandle *handle;
3153 SaveMimePartPair *pair = (SaveMimePartPair *) info->pairs->data;
3155 if (TNY_IS_CAMEL_BS_MIME_PART (pair->part) &&
3156 !tny_camel_bs_mime_part_is_fetched (TNY_CAMEL_BS_MIME_PART (pair->part))) {
3157 gboolean check_online = TRUE;
3158 ModestMsgViewWindowPrivate *priv = NULL;
3160 /* Check if we really need to connect to save the mime part */
3161 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (info->window);
3162 if (g_str_has_prefix (priv->msg_uid, "merge:")) {
3163 check_online = FALSE;
3165 TnyAccountStore *acc_store;
3166 TnyAccount *account = NULL;
3168 acc_store = (TnyAccountStore*) modest_runtime_get_account_store ();
3169 account = tny_account_store_find_account (acc_store, priv->msg_uid);
3172 if (tny_account_get_connection_status (account) ==
3173 TNY_CONNECTION_STATUS_CONNECTED)
3174 check_online = FALSE;
3175 g_object_unref (account);
3177 check_online = !tny_device_is_online (tny_account_store_get_device (acc_store));
3182 g_idle_add ((GSourceFunc) save_mime_part_to_file_connect_idle, info);
3187 info->result = gnome_vfs_create (&handle, pair->filename, GNOME_VFS_OPEN_WRITE, FALSE, 0644);
3188 if (info->result == GNOME_VFS_OK) {
3189 GError *error = NULL;
3190 gboolean decode_in_provider;
3192 ModestAccountMgr *mgr;
3193 const gchar *account;
3194 ModestProtocol *protocol = NULL;
3196 stream = tny_vfs_stream_new (handle);
3198 decode_in_provider = FALSE;
3199 mgr = modest_runtime_get_account_mgr ();
3200 account = modest_window_get_active_account (MODEST_WINDOW (info->window));
3201 if (modest_account_mgr_account_is_multimailbox (mgr, account, &protocol)) {
3202 if (MODEST_IS_ACCOUNT_PROTOCOL (protocol)) {
3203 decode_in_provider =
3204 modest_account_protocol_decode_part_to_stream (
3205 MODEST_ACCOUNT_PROTOCOL (protocol),
3213 if (!decode_in_provider)
3214 written = tny_mime_part_decode_to_stream (pair->part, stream, &error);
3217 g_warning ("modest: could not save attachment %s: %d (%s)\n", pair->filename, error?error->code:-1, error?error->message:"Unknown error");
3219 if ((error->domain == TNY_ERROR_DOMAIN) &&
3220 (error->code == TNY_IO_ERROR_WRITE) &&
3221 (errno == ENOSPC)) {
3222 info->result = GNOME_VFS_ERROR_NO_SPACE;
3224 info->result = GNOME_VFS_ERROR_IO;
3227 g_object_unref (G_OBJECT (stream));
3229 g_warning ("Could not create save attachment %s: %s\n",
3230 pair->filename, gnome_vfs_result_to_string (info->result));
3233 /* Go on saving remaining files */
3234 info->pairs = g_list_remove_link (info->pairs, info->pairs);
3235 if (info->pairs != NULL) {
3236 save_mime_part_to_file (info);
3238 g_idle_add ((GSourceFunc) idle_save_mime_part_show_result, info);
3245 save_mime_parts_to_file_with_checks (GtkWindow *parent,
3246 SaveMimePartInfo *info)
3248 gboolean is_ok = TRUE;
3249 gint replaced_files = 0;
3250 const GList *files = info->pairs;
3251 const GList *iter, *to_replace = NULL;
3253 for (iter = files; (iter != NULL) && (replaced_files < 2); iter = g_list_next(iter)) {
3254 SaveMimePartPair *pair = iter->data;
3255 gchar *unescaped = g_uri_unescape_string (pair->filename, NULL);
3257 if (modest_utils_file_exists (unescaped)) {
3259 if (replaced_files == 1)
3264 if (replaced_files) {
3267 if (replaced_files == 1) {
3268 SaveMimePartPair *pair = to_replace->data;
3269 const gchar *basename = strrchr (pair->filename, G_DIR_SEPARATOR) + 1;
3270 gchar *escaped_basename, *message;
3272 escaped_basename = g_uri_unescape_string (basename, NULL);
3273 message = g_strdup_printf ("%s\n%s",
3274 _FM("docm_nc_replace_file"),
3275 (escaped_basename) ? escaped_basename : "");
3276 response = modest_platform_run_confirmation_dialog (parent, message);
3278 g_free (escaped_basename);
3280 response = modest_platform_run_confirmation_dialog (parent,
3281 _FM("docm_nc_replace_multiple"));
3283 if (response != GTK_RESPONSE_OK)
3288 save_mime_part_info_free (info, TRUE);
3290 g_thread_create ((GThreadFunc)save_mime_part_to_file, info, FALSE, NULL);
3295 typedef struct _SaveAttachmentsInfo {
3296 TnyList *attachments_list;
3297 ModestMsgViewWindow *window;
3298 } SaveAttachmentsInfo;
3301 save_attachments_response (GtkDialog *dialog,
3305 TnyList *mime_parts;
3307 GList *files_to_save = NULL;
3308 gchar *current_folder;
3309 SaveAttachmentsInfo *sa_info = (SaveAttachmentsInfo *) user_data;
3311 mime_parts = TNY_LIST (sa_info->attachments_list);
3313 if (arg1 != GTK_RESPONSE_OK)
3316 chooser_uri = gtk_file_chooser_get_uri (GTK_FILE_CHOOSER (dialog));
3317 current_folder = gtk_file_chooser_get_current_folder_uri (GTK_FILE_CHOOSER (dialog));
3318 if (current_folder && *current_folder != '\0') {
3320 modest_conf_set_string (modest_runtime_get_conf (), MODEST_CONF_LATEST_SAVE_ATTACHMENT_PATH,
3321 current_folder,&err);
3323 g_debug ("Error storing latest used folder: %s", err->message);
3327 g_free (current_folder);
3329 if (!modest_utils_folder_writable (chooser_uri)) {
3330 const gchar *err_msg;
3332 #ifdef MODEST_PLATFORM_MAEMO
3333 if (modest_maemo_utils_in_usb_mode ()) {
3334 err_msg = dgettext ("hildon-status-bar-usb", "usbh_ib_mmc_usb_connected");
3336 err_msg = _FM("sfil_ib_readonly_location");
3339 err_msg = _FM("sfil_ib_readonly_location");
3341 hildon_banner_show_information (NULL, NULL, err_msg);
3345 iter = tny_list_create_iterator (mime_parts);
3346 while (!tny_iterator_is_done (iter)) {
3347 TnyMimePart *mime_part = (TnyMimePart *) tny_iterator_get_current (iter);
3349 if ((modest_tny_mime_part_is_attachment_for_modest (mime_part)) &&
3350 !tny_mime_part_is_purged (mime_part) &&
3351 (tny_mime_part_get_filename (mime_part) != NULL)) {
3352 SaveMimePartPair *pair;
3354 pair = g_slice_new0 (SaveMimePartPair);
3356 if (tny_list_get_length (mime_parts) > 1) {
3358 gnome_vfs_escape_slashes (tny_mime_part_get_filename (mime_part));
3359 pair->filename = g_build_filename (chooser_uri, escaped, NULL);
3362 pair->filename = g_strdup (chooser_uri);
3364 pair->part = mime_part;
3365 files_to_save = g_list_prepend (files_to_save, pair);
3367 tny_iterator_next (iter);
3369 g_object_unref (iter);
3372 if (files_to_save != NULL) {
3373 SaveMimePartInfo *info = g_slice_new0 (SaveMimePartInfo);
3374 info->pairs = files_to_save;
3375 info->result = TRUE;
3376 info->uri = g_strdup (chooser_uri);
3377 info->window = g_object_ref (sa_info->window);
3378 save_mime_parts_to_file_with_checks ((GtkWindow *) dialog, info);
3380 g_free (chooser_uri);
3383 /* Free and close the dialog */
3384 g_object_unref (mime_parts);
3385 g_object_unref (sa_info->window);
3386 g_slice_free (SaveAttachmentsInfo, sa_info);
3387 gtk_widget_destroy (GTK_WIDGET (dialog));
3391 msg_is_attachment (TnyList *mime_parts)
3394 gboolean retval = FALSE;
3396 if (tny_list_get_length (mime_parts) > 1)
3399 iter = tny_list_create_iterator (mime_parts);
3401 TnyMimePart *part = TNY_MIME_PART (tny_iterator_get_current (iter));
3403 if (TNY_IS_MSG (part))
3405 g_object_unref (part);
3407 g_object_unref (iter);
3413 modest_msg_view_window_save_attachments (ModestMsgViewWindow *window,
3414 TnyList *mime_parts)
3416 ModestMsgViewWindowPrivate *priv;
3417 GtkWidget *save_dialog = NULL;
3418 gchar *conf_folder = NULL;
3419 gchar *filename = NULL;
3420 gchar *save_multiple_str = NULL;
3421 const gchar *root_folder = "file:///";
3423 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window));
3424 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
3426 if (mime_parts == NULL) {
3427 gboolean allow_msgs = FALSE;
3429 /* In Hildon 2.2 save and delete operate over all the attachments as there's no
3430 * selection available */
3431 mime_parts = modest_msg_view_get_attachments (MODEST_MSG_VIEW (priv->msg_view));
3433 /* Check if the message is composed by an unique MIME
3434 part whose content disposition is attachment. There
3435 could be messages like this:
3437 Date: Tue, 12 Jan 2010 20:40:59 +0000
3438 From: <sender@example.org>
3439 To: <recipient@example.org>
3441 Content-Type: image/jpeg
3442 Content-Disposition: attachment; filename="bug7718.jpeg"
3444 whose unique MIME part is the message itself whose
3445 content disposition is attachment
3447 if (mime_parts && msg_is_attachment (mime_parts))
3451 !modest_maemo_utils_select_attachments (GTK_WINDOW (window), mime_parts, allow_msgs)) {
3452 g_object_unref (mime_parts);
3455 if (mime_parts == NULL || tny_list_get_length (mime_parts) == 0) {
3457 g_object_unref (mime_parts);
3463 g_object_ref (mime_parts);
3466 /* prepare dialog */
3467 if (tny_list_get_length (mime_parts) == 1) {
3469 /* only one attachment selected */
3470 iter = tny_list_create_iterator (mime_parts);
3471 TnyMimePart *mime_part = (TnyMimePart *) tny_iterator_get_current (iter);
3472 g_object_unref (iter);
3473 if (!modest_tny_mime_part_is_msg (mime_part) &&
3474 modest_tny_mime_part_is_attachment_for_modest (mime_part) &&
3475 !tny_mime_part_is_purged (mime_part)) {
3476 filename = g_strdup (tny_mime_part_get_filename (mime_part));
3478 /* TODO: show any error? */
3479 g_warning ("%s: Tried to save a non-file attachment", __FUNCTION__);
3480 g_object_unref (mime_parts);
3483 g_object_unref (mime_part);
3485 gint num = tny_list_get_length (mime_parts);
3486 save_multiple_str = g_strdup_printf (dngettext("hildon-fm",
3487 "sfil_va_number_of_objects_attachment",
3488 "sfil_va_number_of_objects_attachments",
3492 save_dialog = hildon_file_chooser_dialog_new (GTK_WINDOW (window),
3493 GTK_FILE_CHOOSER_ACTION_SAVE);
3495 /* Get last used folder */
3496 conf_folder = modest_conf_get_string (modest_runtime_get_conf (),
3497 MODEST_CONF_LATEST_SAVE_ATTACHMENT_PATH, NULL);
3499 /* File chooser stops working if we select "file:///" as current folder */
3500 if (conf_folder && g_ascii_strcasecmp (root_folder, conf_folder) != 0) {
3501 g_free (conf_folder);
3505 if (conf_folder && conf_folder[0] != '\0') {
3506 gtk_file_chooser_set_current_folder_uri (GTK_FILE_CHOOSER (save_dialog), conf_folder);
3509 /* Set the default folder to documents folder */
3510 docs_folder = (gchar *) g_strdup(g_get_user_special_dir (G_USER_DIRECTORY_DOCUMENTS));
3513 docs_folder = g_build_filename (g_getenv (MYDOCS_ENV), DOCS_FOLDER, NULL);
3515 gtk_file_chooser_set_current_folder (GTK_FILE_CHOOSER (save_dialog), docs_folder);
3516 g_free (docs_folder);
3518 g_free (conf_folder);
3522 gtk_file_chooser_set_current_name (GTK_FILE_CHOOSER (save_dialog),
3527 /* if multiple, set multiple string */
3528 if (save_multiple_str) {
3529 g_object_set (G_OBJECT (save_dialog), "save-multiple", save_multiple_str, NULL);
3530 gtk_window_set_title (GTK_WINDOW (save_dialog), _FM("sfil_ti_save_objects_files"));
3531 g_free (save_multiple_str);
3534 /* We must run this asynchronously, because the hildon dialog
3535 performs a gtk_dialog_run by itself which leads to gdk
3537 SaveAttachmentsInfo *sa_info;
3538 sa_info = g_slice_new (SaveAttachmentsInfo);
3539 sa_info->attachments_list = mime_parts;
3540 sa_info->window = g_object_ref (window);
3541 g_signal_connect (save_dialog, "response",
3542 G_CALLBACK (save_attachments_response), sa_info);
3544 gtk_widget_show_all (save_dialog);
3548 show_remove_attachment_information (gpointer userdata)
3550 ModestMsgViewWindow *window = (ModestMsgViewWindow *) userdata;
3551 ModestMsgViewWindowPrivate *priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
3553 /* We're outside the main lock */
3554 gdk_threads_enter ();
3556 if (priv->remove_attachment_banner != NULL) {
3557 gtk_widget_destroy (priv->remove_attachment_banner);
3558 g_object_unref (priv->remove_attachment_banner);
3561 priv->remove_attachment_banner = g_object_ref (
3562 hildon_banner_show_animation (NULL, NULL, _("mcen_me_inbox_remove_attachments")));
3564 gdk_threads_leave ();
3570 modest_msg_view_window_remove_attachments (ModestMsgViewWindow *window, gboolean get_all)
3572 ModestMsgViewWindowPrivate *priv;
3573 TnyList *mime_parts = NULL, *tmp;
3574 gchar *confirmation_message;
3580 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window));
3581 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
3583 /* In hildon 2.2 we ignore the get_all flag as we always get all attachments. This is
3584 * because we don't have selection
3586 mime_parts = modest_msg_view_get_attachments (MODEST_MSG_VIEW (priv->msg_view));
3588 /* Remove already purged messages from mime parts list. We use
3589 a copy of the list to remove items in the original one */
3590 tmp = tny_list_copy (mime_parts);
3591 iter = tny_list_create_iterator (tmp);
3592 while (!tny_iterator_is_done (iter)) {
3593 TnyMimePart *part = TNY_MIME_PART (tny_iterator_get_current (iter));
3594 if (tny_mime_part_is_purged (part))
3595 tny_list_remove (mime_parts, (GObject *) part);
3597 g_object_unref (part);
3598 tny_iterator_next (iter);
3600 g_object_unref (tmp);
3601 g_object_unref (iter);
3603 if (!modest_maemo_utils_select_attachments (GTK_WINDOW (window), mime_parts, TRUE) ||
3604 tny_list_get_length (mime_parts) == 0) {
3605 g_object_unref (mime_parts);
3609 n_attachments = tny_list_get_length (mime_parts);
3610 if (n_attachments == 1) {
3614 iter = tny_list_create_iterator (mime_parts);
3615 part = (TnyMimePart *) tny_iterator_get_current (iter);
3616 g_object_unref (iter);
3617 if (modest_tny_mime_part_is_msg (part)) {
3619 header = tny_msg_get_header (TNY_MSG (part));
3620 filename = tny_header_dup_subject (header);
3621 g_object_unref (header);
3622 if (filename == NULL)
3623 filename = g_strdup (_("mail_va_no_subject"));
3625 filename = g_strdup (tny_mime_part_get_filename (TNY_MIME_PART (part)));
3627 confirmation_message = g_strdup_printf (_("mcen_nc_purge_file_text"), filename);
3629 g_object_unref (part);
3631 confirmation_message = g_strdup_printf (ngettext("mcen_nc_purge_file_text",
3632 "mcen_nc_purge_files_text",
3633 n_attachments), n_attachments);
3635 response = modest_platform_run_confirmation_dialog (GTK_WINDOW (window),
3636 confirmation_message);
3637 g_free (confirmation_message);
3639 if (response != GTK_RESPONSE_OK) {
3640 g_object_unref (mime_parts);
3644 priv->purge_timeout = g_timeout_add (2000, show_remove_attachment_information, window);
3646 iter = tny_list_create_iterator (mime_parts);
3647 while (!tny_iterator_is_done (iter)) {
3650 part = (TnyMimePart *) tny_iterator_get_current (iter);
3651 tny_mime_part_set_purged (TNY_MIME_PART (part));
3652 g_object_unref (part);
3653 tny_iterator_next (iter);
3655 g_object_unref (iter);
3657 msg = tny_msg_view_get_msg (TNY_MSG_VIEW (priv->msg_view));
3658 tny_msg_view_clear (TNY_MSG_VIEW (priv->msg_view));
3659 tny_msg_rewrite_cache (msg);
3660 tny_msg_view_set_msg (TNY_MSG_VIEW (priv->msg_view), msg);
3661 g_object_unref (msg);
3662 update_branding (MODEST_MSG_VIEW_WINDOW (window));
3664 g_object_unref (mime_parts);
3666 if (priv->purge_timeout > 0) {
3667 g_source_remove (priv->purge_timeout);
3668 priv->purge_timeout = 0;
3671 if (priv->remove_attachment_banner) {
3672 gtk_widget_destroy (priv->remove_attachment_banner);
3673 g_object_unref (priv->remove_attachment_banner);
3674 priv->remove_attachment_banner = NULL;
3680 update_window_title (ModestMsgViewWindow *window)
3682 ModestMsgViewWindowPrivate *priv;
3684 TnyHeader *header = NULL;
3685 gchar *subject = NULL;
3687 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
3689 /* Note that if the window is closed while we're retrieving
3690 the message, this widget could de deleted */
3691 if (!priv->msg_view)
3694 msg = tny_msg_view_get_msg (TNY_MSG_VIEW (priv->msg_view));
3696 if (priv->other_body) {
3699 description = modest_tny_mime_part_get_header_value (priv->other_body, "Content-Description");
3701 g_strstrip (description);
3702 subject = description;
3704 } else if (msg != NULL) {
3705 header = tny_msg_get_header (msg);
3706 subject = tny_header_dup_subject (header);
3707 g_object_unref (header);
3708 g_object_unref (msg);
3711 if ((subject == NULL)||(subject[0] == '\0')) {
3713 subject = g_strdup (_("mail_va_no_subject"));
3716 gtk_window_set_title (GTK_WINDOW (window), subject);
3721 on_move_focus (GtkWidget *widget,
3722 GtkDirectionType direction,
3725 g_signal_stop_emission_by_name (G_OBJECT (widget), "move-focus");
3729 fetch_image_open_stream (TnyStreamCache *self, gint64 *expected_size, gchar *uri)
3731 GnomeVFSResult result;
3732 GnomeVFSHandle *handle = NULL;
3733 GnomeVFSFileInfo *info = NULL;
3736 result = gnome_vfs_open (&handle, uri, GNOME_VFS_OPEN_READ);
3737 if (result != GNOME_VFS_OK) {
3742 info = gnome_vfs_file_info_new ();
3743 result = gnome_vfs_get_file_info_from_handle (handle, info, GNOME_VFS_FILE_INFO_DEFAULT);
3744 if (result != GNOME_VFS_OK || ! (info->valid_fields & GNOME_VFS_FILE_INFO_FIELDS_SIZE)) {
3745 /* We put a "safe" default size for going to cache */
3746 *expected_size = (300*1024);
3748 *expected_size = info->size;
3750 gnome_vfs_file_info_unref (info);
3752 stream = tny_vfs_stream_new (handle);
3761 TnyStream *output_stream;
3762 GtkWidget *msg_view;
3767 on_fetch_image_timeout_refresh_view (gpointer userdata)
3769 ModestMsgViewWindowPrivate *priv;
3771 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (userdata);
3772 update_progress_hint (MODEST_MSG_VIEW_WINDOW (userdata));
3773 /* Note that priv->msg_view is set to NULL when this window is
3775 if (priv->msg_view && GTK_WIDGET_DRAWABLE (priv->msg_view)) {
3776 gtk_widget_queue_draw (GTK_WIDGET (priv->msg_view));
3778 priv->fetch_image_redraw_handler = 0;
3779 g_object_unref (userdata);
3784 on_fetch_image_idle_refresh_view (gpointer userdata)
3787 FetchImageData *fidata = (FetchImageData *) userdata;
3789 gdk_threads_enter ();
3790 if (GTK_WIDGET_DRAWABLE (fidata->msg_view)) {
3791 ModestMsgViewWindowPrivate *priv;
3793 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (fidata->window);
3794 priv->fetching_images--;
3795 if (priv->fetch_image_redraw_handler == 0) {
3796 priv->fetch_image_redraw_handler = g_timeout_add (500, on_fetch_image_timeout_refresh_view, g_object_ref (fidata->window));
3800 gdk_threads_leave ();
3802 g_object_unref (fidata->msg_view);
3803 g_object_unref (fidata->window);
3804 g_slice_free (FetchImageData, fidata);
3809 on_fetch_image_thread (gpointer userdata)
3811 FetchImageData *fidata = (FetchImageData *) userdata;
3812 TnyStreamCache *cache;
3813 TnyStream *cache_stream;
3815 cache = modest_runtime_get_images_cache ();
3817 tny_stream_cache_get_stream (cache,
3819 (TnyStreamCacheOpenStreamFetcher) fetch_image_open_stream,
3820 (gpointer) fidata->uri);
3821 g_free (fidata->cache_id);
3822 g_free (fidata->uri);
3824 if (cache_stream != NULL) {
3827 while (G_LIKELY (!tny_stream_is_eos (cache_stream))) {
3830 nb_read = tny_stream_read (cache_stream, buffer, sizeof (buffer));
3831 if (G_UNLIKELY (nb_read < 0)) {
3833 } else if (G_LIKELY (nb_read > 0)) {
3834 gssize nb_written = 0;
3836 while (G_UNLIKELY (nb_written < nb_read)) {
3839 len = tny_stream_write (fidata->output_stream, buffer + nb_written,
3840 nb_read - nb_written);
3841 if (G_UNLIKELY (len < 0))
3847 tny_stream_close (cache_stream);
3848 g_object_unref (cache_stream);
3851 tny_stream_close (fidata->output_stream);
3852 g_object_unref (fidata->output_stream);
3854 g_idle_add (on_fetch_image_idle_refresh_view, fidata);
3860 on_fetch_image (ModestMsgView *msgview,
3863 ModestMsgViewWindow *window)
3865 const gchar *current_account;
3866 ModestMsgViewWindowPrivate *priv;
3867 FetchImageData *fidata;
3869 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
3871 current_account = modest_window_get_active_account (MODEST_WINDOW (window));
3873 fidata = g_slice_new0 (FetchImageData);
3874 fidata->msg_view = g_object_ref (msgview);
3875 fidata->window = g_object_ref (window);
3876 fidata->uri = g_strdup (uri);
3877 fidata->cache_id = modest_images_cache_get_id (current_account, uri);
3878 fidata->output_stream = g_object_ref (stream);
3880 priv->fetching_images++;
3881 if (g_thread_create (on_fetch_image_thread, fidata, FALSE, NULL) == NULL) {
3882 g_object_unref (fidata->output_stream);
3883 g_free (fidata->cache_id);
3884 g_free (fidata->uri);
3885 g_object_unref (fidata->msg_view);
3886 g_slice_free (FetchImageData, fidata);
3887 tny_stream_close (stream);
3888 priv->fetching_images--;
3889 update_progress_hint (window);
3892 update_progress_hint (window);
3898 setup_menu (ModestMsgViewWindow *self)
3900 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW(self));
3902 /* Settings menu buttons */
3903 modest_hildon2_window_add_to_menu (MODEST_HILDON2_WINDOW (self), _("mcen_me_viewer_find"), NULL,
3904 APP_MENU_CALLBACK (modest_msg_view_window_show_find_toolbar),
3905 MODEST_DIMMING_CALLBACK (modest_ui_dimming_rules_on_find_in_msg));
3907 modest_hildon2_window_add_to_menu (MODEST_HILDON2_WINDOW (self),
3908 dngettext(GETTEXT_PACKAGE,
3909 "mcen_me_move_message",
3910 "mcen_me_move_messages",
3913 APP_MENU_CALLBACK (modest_ui_actions_on_move_to),
3914 MODEST_DIMMING_CALLBACK (modest_ui_dimming_rules_on_move_to));
3916 modest_hildon2_window_add_to_menu (MODEST_HILDON2_WINDOW (self), _("mcen_me_inbox_mark_as_read"), NULL,
3917 APP_MENU_CALLBACK (modest_ui_actions_on_mark_as_read),
3918 MODEST_DIMMING_CALLBACK (modest_ui_dimming_rules_on_mark_as_read_msg_in_view));
3920 modest_hildon2_window_add_to_menu (MODEST_HILDON2_WINDOW (self), _("mcen_me_inbox_mark_as_unread"), NULL,
3921 APP_MENU_CALLBACK (modest_ui_actions_on_mark_as_unread),
3922 MODEST_DIMMING_CALLBACK (modest_ui_dimming_rules_on_mark_as_unread_msg_in_view));
3924 modest_hildon2_window_add_to_menu (MODEST_HILDON2_WINDOW (self), _("mcen_me_viewer_save_attachments"), NULL,
3925 APP_MENU_CALLBACK (modest_ui_actions_save_attachments),
3926 MODEST_DIMMING_CALLBACK (modest_ui_dimming_rules_on_save_attachments));
3927 modest_hildon2_window_add_to_menu (MODEST_HILDON2_WINDOW (self), _("mcen_me_inbox_remove_attachments"), NULL,
3928 APP_MENU_CALLBACK (modest_ui_actions_remove_attachments),
3929 MODEST_DIMMING_CALLBACK (modest_ui_dimming_rules_on_remove_attachments));
3931 modest_hildon2_window_add_to_menu (MODEST_HILDON2_WINDOW (self), _("mcen_me_new_message"), "<Control>n",
3932 APP_MENU_CALLBACK (modest_ui_actions_on_new_msg),
3933 MODEST_DIMMING_CALLBACK (modest_ui_dimming_rules_on_new_msg));
3934 modest_hildon2_window_add_to_menu (MODEST_HILDON2_WINDOW (self), _("mcen_me_viewer_addtocontacts"), NULL,
3935 APP_MENU_CALLBACK (modest_ui_actions_add_to_contacts),
3936 MODEST_DIMMING_CALLBACK (modest_ui_dimming_rules_on_add_to_contacts));
3938 modest_hildon2_window_add_to_menu (MODEST_HILDON2_WINDOW (self), _("mcen_ti_message_properties"), NULL,
3939 APP_MENU_CALLBACK (modest_ui_actions_on_details),
3940 MODEST_DIMMING_CALLBACK (modest_ui_dimming_rules_on_details));
3944 modest_msg_view_window_add_to_contacts (ModestMsgViewWindow *self)
3946 ModestMsgViewWindowPrivate *priv;
3947 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
3948 GSList *recipients = NULL;
3951 msg = tny_msg_view_get_msg (TNY_MSG_VIEW (priv->msg_view));
3955 header = modest_msg_view_window_get_header (self);
3958 recipients = modest_tny_msg_header_get_all_recipients_list (header);
3959 g_object_unref (header);
3961 recipients = modest_tny_msg_get_all_recipients_list (msg);
3962 g_object_unref (msg);
3966 /* Offer the user to add recipients to the address book */
3967 modest_address_book_add_address_list_with_selector (recipients, (GtkWindow *) self);
3968 g_slist_foreach (recipients, (GFunc) g_free, NULL); g_slist_free (recipients);
3973 _modest_msg_view_window_map_event (GtkWidget *widget,
3977 ModestMsgViewWindow *self = (ModestMsgViewWindow *) userdata;
3979 update_progress_hint (self);
3985 modest_msg_view_window_fetch_images (ModestMsgViewWindow *self)
3987 ModestMsgViewWindowPrivate *priv;
3988 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
3990 modest_msg_view_request_fetch_images (MODEST_MSG_VIEW (priv->msg_view));
3994 modest_msg_view_window_has_blocked_external_images (ModestMsgViewWindow *self)
3996 ModestMsgViewWindowPrivate *priv;
3997 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
3999 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self), FALSE);
4001 return modest_msg_view_has_blocked_external_images (MODEST_MSG_VIEW (priv->msg_view));
4005 modest_msg_view_window_reload (ModestMsgViewWindow *self)
4007 ModestMsgViewWindowPrivate *priv;
4008 const gchar *msg_uid;
4009 TnyHeader *header = NULL;
4010 TnyFolder *folder = NULL;
4012 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self));
4014 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
4016 header = modest_msg_view_window_get_header (MODEST_MSG_VIEW_WINDOW (self));
4020 folder = tny_header_get_folder (header);
4021 g_object_unref (header);
4026 msg_uid = modest_msg_view_window_get_message_uid (self);
4028 GtkTreeRowReference *row_reference;
4030 if (priv->row_reference && gtk_tree_row_reference_valid (priv->row_reference)) {
4031 row_reference = priv->row_reference;
4033 row_reference = NULL;
4035 if (!message_reader (self, priv, NULL, msg_uid, folder, row_reference))
4036 g_warning ("Shouldn't happen, trying to reload a message failed");
4039 g_object_unref (folder);
4043 update_branding (ModestMsgViewWindow *self)
4045 const gchar *account;
4046 const gchar *mailbox;
4047 ModestAccountMgr *mgr;
4048 ModestProtocol *protocol = NULL;
4049 gchar *service_name = NULL;
4050 const GdkPixbuf *service_icon = NULL;
4051 ModestMsgViewWindowPrivate *priv;
4053 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
4055 account = modest_window_get_active_account (MODEST_WINDOW (self));
4056 mailbox = modest_window_get_active_mailbox (MODEST_WINDOW (self));
4058 mgr = modest_runtime_get_account_mgr ();
4060 if (modest_account_mgr_account_is_multimailbox (mgr, account, &protocol)) {
4061 if (MODEST_IS_ACCOUNT_PROTOCOL (protocol)) {
4062 service_name = modest_account_protocol_get_service_name (MODEST_ACCOUNT_PROTOCOL (protocol),
4064 service_icon = modest_account_protocol_get_service_icon (MODEST_ACCOUNT_PROTOCOL (protocol),
4065 account, mailbox, MODEST_ICON_SIZE_SMALL);
4069 modest_msg_view_set_branding (MODEST_MSG_VIEW (priv->msg_view), service_name, service_icon);
4070 g_free (service_name);
4074 sync_flags (ModestMsgViewWindow *self)
4076 TnyHeader *header = NULL;
4078 header = modest_msg_view_window_get_header (self);
4080 TnyMsg *msg = modest_msg_view_window_get_message (self);
4082 header = tny_msg_get_header (msg);
4083 g_object_unref (msg);
4088 TnyFolder *folder = tny_header_get_folder (header);
4091 ModestMailOperation *mail_op;
4093 /* Sync folder, we need this to save the seen flag */
4094 mail_op = modest_mail_operation_new (NULL);
4095 modest_mail_operation_queue_add (modest_runtime_get_mail_operation_queue (),
4097 modest_mail_operation_sync_folder (mail_op, folder, FALSE, NULL, NULL);
4098 g_object_unref (mail_op);
4099 g_object_unref (folder);
4101 g_object_unref (header);
4106 on_realize (GtkWidget *widget,
4109 GdkDisplay *display;
4111 unsigned long val = 1;
4113 display = gdk_drawable_get_display (widget->window);
4114 atom = gdk_x11_get_xatom_by_name_for_display (display, "_HILDON_ZOOM_KEY_ATOM");
4115 XChangeProperty (GDK_DISPLAY_XDISPLAY (display),
4116 GDK_WINDOW_XID (widget->window), atom,
4117 XA_INTEGER, 32, PropModeReplace,
4118 (unsigned char *) &val, 1);