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>
70 #define MYDOCS_ENV "MYDOCSDIR"
71 #define DOCS_FOLDER ".documents"
73 typedef struct _ModestMsgViewWindowPrivate ModestMsgViewWindowPrivate;
74 struct _ModestMsgViewWindowPrivate {
77 GtkWidget *main_scroll;
78 GtkWidget *find_toolbar;
81 /* Progress observers */
82 GSList *progress_widgets;
85 GtkWidget *prev_toolitem;
86 GtkWidget *next_toolitem;
87 gboolean progress_hint;
90 /* Optimized view enabled */
91 gboolean optimized_view;
93 /* Whether this was created via the *_new_for_search_result() function. */
94 gboolean is_search_result;
96 /* Whether the message is in outbox */
99 /* A reference to the @model of the header view
100 * to allow selecting previous/next messages,
101 * if the message is currently selected in the header view.
103 const gchar *header_folder_id;
104 GtkTreeModel *header_model;
105 GtkTreeRowReference *row_reference;
106 GtkTreeRowReference *next_row_reference;
108 gulong clipboard_change_handler;
109 gulong queue_change_handler;
110 gulong account_removed_handler;
111 gulong row_changed_handler;
112 gulong row_deleted_handler;
113 gulong row_inserted_handler;
114 gulong rows_reordered_handler;
117 GtkWidget *remove_attachment_banner;
124 static void modest_msg_view_window_class_init (ModestMsgViewWindowClass *klass);
125 static void modest_msg_view_window_init (ModestMsgViewWindow *obj);
126 static void modest_header_view_observer_init(
127 ModestHeaderViewObserverIface *iface_class);
128 static void modest_msg_view_window_finalize (GObject *obj);
129 static void modest_msg_view_window_toggle_find_toolbar (GtkToggleAction *obj,
131 static void modest_msg_view_window_find_toolbar_close (GtkWidget *widget,
132 ModestMsgViewWindow *obj);
133 static void modest_msg_view_window_find_toolbar_search (GtkWidget *widget,
134 ModestMsgViewWindow *obj);
136 static void modest_msg_view_window_disconnect_signals (ModestWindow *self);
138 static gdouble modest_msg_view_window_get_zoom (ModestWindow *window);
139 static void modest_msg_view_window_set_zoom (ModestWindow *window,
141 static gboolean modest_msg_view_window_zoom_minus (ModestWindow *window);
142 static gboolean modest_msg_view_window_zoom_plus (ModestWindow *window);
143 static gboolean modest_msg_view_window_key_event (GtkWidget *window,
146 static void modest_msg_view_window_update_priority (ModestMsgViewWindow *window);
148 static void modest_msg_view_window_show_toolbar (ModestWindow *window,
149 gboolean show_toolbar);
151 static void modest_msg_view_window_clipboard_owner_change (GtkClipboard *clipboard,
153 ModestMsgViewWindow *window);
155 static void modest_msg_view_window_on_row_changed (GtkTreeModel *header_model,
158 ModestMsgViewWindow *window);
160 static void modest_msg_view_window_on_row_deleted (GtkTreeModel *header_model,
162 ModestMsgViewWindow *window);
164 static void modest_msg_view_window_on_row_inserted (GtkTreeModel *header_model,
165 GtkTreePath *tree_path,
166 GtkTreeIter *tree_iter,
167 ModestMsgViewWindow *window);
169 static void modest_msg_view_window_on_row_reordered (GtkTreeModel *header_model,
173 ModestMsgViewWindow *window);
175 static void modest_msg_view_window_update_model_replaced (ModestHeaderViewObserver *window,
177 const gchar *tny_folder_id);
179 static void on_queue_changed (ModestMailOperationQueue *queue,
180 ModestMailOperation *mail_op,
181 ModestMailOperationQueueNotification type,
182 ModestMsgViewWindow *self);
184 static void on_account_removed (TnyAccountStore *account_store,
188 static void on_move_focus (GtkWidget *widget,
189 GtkDirectionType direction,
192 static void view_msg_cb (ModestMailOperation *mail_op,
199 static void set_progress_hint (ModestMsgViewWindow *self,
202 static void update_window_title (ModestMsgViewWindow *window);
204 static gboolean set_toolbar_transfer_mode (ModestMsgViewWindow *self);
205 static void init_window (ModestMsgViewWindow *obj);
207 static gboolean msg_is_visible (TnyHeader *header, gboolean check_outbox);
209 static void check_dimming_rules_after_change (ModestMsgViewWindow *window);
211 static gboolean on_fetch_image (ModestMsgView *msgview,
214 ModestMsgViewWindow *window);
216 static gboolean modest_msg_view_window_scroll_child (ModestMsgViewWindow *self,
217 GtkScrollType scroll_type,
220 static gboolean message_reader (ModestMsgViewWindow *window,
221 ModestMsgViewWindowPrivate *priv,
223 GtkTreeRowReference *row_reference);
225 static void setup_menu (ModestMsgViewWindow *self);
226 static gboolean _modest_msg_view_window_map_event (GtkWidget *widget,
231 /* list my signals */
238 static const GtkToggleActionEntry msg_view_toggle_action_entries [] = {
239 { "FindInMessage", MODEST_TOOLBAR_ICON_FIND, N_("qgn_toolb_gene_find"), NULL, NULL, G_CALLBACK (modest_msg_view_window_toggle_find_toolbar), FALSE },
243 #define MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE((o), \
244 MODEST_TYPE_MSG_VIEW_WINDOW, \
245 ModestMsgViewWindowPrivate))
247 static GtkWindowClass *parent_class = NULL;
249 /* uncomment the following if you have defined any signals */
250 static guint signals[LAST_SIGNAL] = {0};
253 modest_msg_view_window_get_type (void)
255 static GType my_type = 0;
257 static const GTypeInfo my_info = {
258 sizeof(ModestMsgViewWindowClass),
259 NULL, /* base init */
260 NULL, /* base finalize */
261 (GClassInitFunc) modest_msg_view_window_class_init,
262 NULL, /* class finalize */
263 NULL, /* class data */
264 sizeof(ModestMsgViewWindow),
266 (GInstanceInitFunc) modest_msg_view_window_init,
269 my_type = g_type_register_static (MODEST_TYPE_HILDON2_WINDOW,
270 "ModestMsgViewWindow",
273 static const GInterfaceInfo modest_header_view_observer_info =
275 (GInterfaceInitFunc) modest_header_view_observer_init,
276 NULL, /* interface_finalize */
277 NULL /* interface_data */
280 g_type_add_interface_static (my_type,
281 MODEST_TYPE_HEADER_VIEW_OBSERVER,
282 &modest_header_view_observer_info);
288 save_state (ModestWindow *self)
290 modest_widget_memory_save (modest_runtime_get_conf (),
292 MODEST_CONF_MSG_VIEW_WINDOW_KEY);
296 gboolean modest_msg_view_window_scroll_child (ModestMsgViewWindow *self,
297 GtkScrollType scroll_type,
301 ModestMsgViewWindowPrivate *priv;
302 gboolean return_value;
304 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(self);
305 g_signal_emit_by_name (priv->main_scroll, "scroll-child", scroll_type, horizontal, &return_value);
310 add_scroll_binding (GtkBindingSet *binding_set,
312 GtkScrollType scroll)
314 guint keypad_keyval = keyval - GDK_Left + GDK_KP_Left;
316 gtk_binding_entry_add_signal (binding_set, keyval, 0,
318 GTK_TYPE_SCROLL_TYPE, scroll,
319 G_TYPE_BOOLEAN, FALSE);
320 gtk_binding_entry_add_signal (binding_set, keypad_keyval, 0,
322 GTK_TYPE_SCROLL_TYPE, scroll,
323 G_TYPE_BOOLEAN, FALSE);
327 modest_msg_view_window_class_init (ModestMsgViewWindowClass *klass)
329 GObjectClass *gobject_class;
330 HildonWindowClass *hildon_window_class;
331 ModestWindowClass *modest_window_class;
332 GtkBindingSet *binding_set;
334 gobject_class = (GObjectClass*) klass;
335 hildon_window_class = (HildonWindowClass *) klass;
336 modest_window_class = (ModestWindowClass *) klass;
338 parent_class = g_type_class_peek_parent (klass);
339 gobject_class->finalize = modest_msg_view_window_finalize;
341 modest_window_class->set_zoom_func = modest_msg_view_window_set_zoom;
342 modest_window_class->get_zoom_func = modest_msg_view_window_get_zoom;
343 modest_window_class->zoom_plus_func = modest_msg_view_window_zoom_plus;
344 modest_window_class->zoom_minus_func = modest_msg_view_window_zoom_minus;
345 modest_window_class->show_toolbar_func = modest_msg_view_window_show_toolbar;
346 modest_window_class->disconnect_signals_func = modest_msg_view_window_disconnect_signals;
348 modest_window_class->save_state_func = save_state;
350 klass->scroll_child = modest_msg_view_window_scroll_child;
352 signals[MSG_CHANGED_SIGNAL] =
353 g_signal_new ("msg-changed",
354 G_TYPE_FROM_CLASS (gobject_class),
356 G_STRUCT_OFFSET (ModestMsgViewWindowClass, msg_changed),
358 modest_marshal_VOID__POINTER_POINTER,
359 G_TYPE_NONE, 2, G_TYPE_POINTER, G_TYPE_POINTER);
361 signals[SCROLL_CHILD_SIGNAL] =
362 g_signal_new ("scroll-child",
363 G_TYPE_FROM_CLASS (gobject_class),
364 G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
365 G_STRUCT_OFFSET (ModestMsgViewWindowClass, scroll_child),
367 modest_marshal_BOOLEAN__ENUM_BOOLEAN,
368 G_TYPE_BOOLEAN, 2, GTK_TYPE_SCROLL_TYPE, G_TYPE_BOOLEAN);
370 binding_set = gtk_binding_set_by_class (klass);
371 add_scroll_binding (binding_set, GDK_Up, GTK_SCROLL_STEP_UP);
372 add_scroll_binding (binding_set, GDK_Down, GTK_SCROLL_STEP_DOWN);
373 add_scroll_binding (binding_set, GDK_Page_Up, GTK_SCROLL_PAGE_UP);
374 add_scroll_binding (binding_set, GDK_Page_Down, GTK_SCROLL_PAGE_DOWN);
375 add_scroll_binding (binding_set, GDK_Home, GTK_SCROLL_START);
376 add_scroll_binding (binding_set, GDK_End, GTK_SCROLL_END);
378 g_type_class_add_private (gobject_class, sizeof(ModestMsgViewWindowPrivate));
382 static void modest_header_view_observer_init(
383 ModestHeaderViewObserverIface *iface_class)
385 iface_class->update_func = modest_msg_view_window_update_model_replaced;
389 modest_msg_view_window_init (ModestMsgViewWindow *obj)
391 ModestMsgViewWindowPrivate *priv;
392 ModestWindowPrivate *parent_priv = NULL;
393 GtkActionGroup *action_group = NULL;
394 GError *error = NULL;
395 GdkPixbuf *window_icon;
397 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(obj);
398 parent_priv = MODEST_WINDOW_GET_PRIVATE(obj);
399 parent_priv->ui_manager = gtk_ui_manager_new();
401 action_group = gtk_action_group_new ("ModestMsgViewWindowActions");
402 gtk_action_group_set_translation_domain (action_group, GETTEXT_PACKAGE);
404 /* Add common actions */
405 gtk_action_group_add_actions (action_group,
406 modest_action_entries,
407 G_N_ELEMENTS (modest_action_entries),
409 gtk_action_group_add_toggle_actions (action_group,
410 msg_view_toggle_action_entries,
411 G_N_ELEMENTS (msg_view_toggle_action_entries),
414 gtk_ui_manager_insert_action_group (parent_priv->ui_manager, action_group, 0);
415 g_object_unref (action_group);
417 /* Load the UI definition */
418 gtk_ui_manager_add_ui_from_file (parent_priv->ui_manager, MODEST_UIDIR "modest-msg-view-window-ui.xml",
421 g_printerr ("modest: could not merge modest-msg-view-window-ui.xml: %s\n", error->message);
422 g_error_free (error);
427 /* Add accelerators */
428 gtk_window_add_accel_group (GTK_WINDOW (obj),
429 gtk_ui_manager_get_accel_group (parent_priv->ui_manager));
431 priv->is_search_result = FALSE;
432 priv->is_outbox = FALSE;
434 priv->msg_view = NULL;
435 priv->header_model = NULL;
436 priv->header_folder_id = NULL;
437 priv->clipboard_change_handler = 0;
438 priv->queue_change_handler = 0;
439 priv->account_removed_handler = 0;
440 priv->row_changed_handler = 0;
441 priv->row_deleted_handler = 0;
442 priv->row_inserted_handler = 0;
443 priv->rows_reordered_handler = 0;
444 priv->progress_hint = FALSE;
445 priv->fetching_images = 0;
447 priv->optimized_view = FALSE;
448 priv->purge_timeout = 0;
449 priv->remove_attachment_banner = NULL;
450 priv->msg_uid = NULL;
452 priv->sighandlers = NULL;
455 init_window (MODEST_MSG_VIEW_WINDOW(obj));
457 /* Set window icon */
458 window_icon = modest_platform_get_icon (MODEST_APP_MSG_VIEW_ICON, MODEST_ICON_SIZE_BIG);
460 gtk_window_set_icon (GTK_WINDOW (obj), window_icon);
461 g_object_unref (window_icon);
464 hildon_program_add_window (hildon_program_get_instance(),
471 set_toolbar_transfer_mode (ModestMsgViewWindow *self)
473 ModestMsgViewWindowPrivate *priv = NULL;
475 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self), FALSE);
477 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(self);
479 set_progress_hint (self, TRUE);
485 update_progress_hint (ModestMsgViewWindow *self)
487 ModestMsgViewWindowPrivate *priv;
488 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(self);
490 if (GTK_WIDGET_VISIBLE (self)) {
491 hildon_gtk_window_set_progress_indicator (GTK_WINDOW (self),
492 (priv->progress_hint || (priv->fetching_images > 0))?1:0);
497 set_progress_hint (ModestMsgViewWindow *self,
500 ModestWindowPrivate *parent_priv;
501 ModestMsgViewWindowPrivate *priv;
503 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self));
505 parent_priv = MODEST_WINDOW_GET_PRIVATE(self);
506 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(self);
508 /* Sets current progress hint */
509 priv->progress_hint = enabled;
511 update_progress_hint (self);
517 init_window (ModestMsgViewWindow *obj)
519 GtkWidget *main_vbox;
520 ModestMsgViewWindowPrivate *priv;
522 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(obj);
524 priv->msg_view = GTK_WIDGET (tny_platform_factory_new_msg_view (modest_tny_platform_factory_get_instance ()));
525 modest_msg_view_set_shadow_type (MODEST_MSG_VIEW (priv->msg_view), GTK_SHADOW_NONE);
526 main_vbox = gtk_vbox_new (FALSE, 6);
527 priv->main_scroll = hildon_pannable_area_new ();
528 gtk_container_add (GTK_CONTAINER (priv->main_scroll), priv->msg_view);
529 gtk_box_pack_start (GTK_BOX(main_vbox), priv->main_scroll, TRUE, TRUE, 0);
530 gtk_container_add (GTK_CONTAINER(obj), main_vbox);
532 priv->find_toolbar = hildon_find_toolbar_new (NULL);
533 hildon_window_add_toolbar (HILDON_WINDOW (obj), GTK_TOOLBAR (priv->find_toolbar));
534 gtk_widget_set_no_show_all (priv->find_toolbar, TRUE);
536 /* NULL-ize fields if the window is destroyed */
537 g_signal_connect (priv->msg_view, "destroy", G_CALLBACK (gtk_widget_destroyed), &(priv->msg_view));
539 gtk_widget_show_all (GTK_WIDGET(main_vbox));
543 modest_msg_view_window_disconnect_signals (ModestWindow *self)
545 ModestMsgViewWindowPrivate *priv;
546 GtkWidget *header_view = NULL;
547 GtkWindow *parent_window = NULL;
549 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
551 if (gtk_clipboard_get (GDK_SELECTION_PRIMARY) &&
552 g_signal_handler_is_connected (gtk_clipboard_get (GDK_SELECTION_PRIMARY),
553 priv->clipboard_change_handler))
554 g_signal_handler_disconnect (gtk_clipboard_get (GDK_SELECTION_PRIMARY),
555 priv->clipboard_change_handler);
557 if (g_signal_handler_is_connected (G_OBJECT (modest_runtime_get_mail_operation_queue ()),
558 priv->queue_change_handler))
559 g_signal_handler_disconnect (G_OBJECT (modest_runtime_get_mail_operation_queue ()),
560 priv->queue_change_handler);
562 if (g_signal_handler_is_connected (G_OBJECT (modest_runtime_get_account_store ()),
563 priv->account_removed_handler))
564 g_signal_handler_disconnect (G_OBJECT (modest_runtime_get_account_store ()),
565 priv->account_removed_handler);
567 if (priv->header_model) {
568 if (g_signal_handler_is_connected(G_OBJECT (priv->header_model),
569 priv->row_changed_handler))
570 g_signal_handler_disconnect(G_OBJECT (priv->header_model),
571 priv->row_changed_handler);
573 if (g_signal_handler_is_connected(G_OBJECT (priv->header_model),
574 priv->row_deleted_handler))
575 g_signal_handler_disconnect(G_OBJECT (priv->header_model),
576 priv->row_deleted_handler);
578 if (g_signal_handler_is_connected(G_OBJECT (priv->header_model),
579 priv->row_inserted_handler))
580 g_signal_handler_disconnect(G_OBJECT (priv->header_model),
581 priv->row_inserted_handler);
583 if (g_signal_handler_is_connected(G_OBJECT (priv->header_model),
584 priv->rows_reordered_handler))
585 g_signal_handler_disconnect(G_OBJECT (priv->header_model),
586 priv->rows_reordered_handler);
589 modest_signal_mgr_disconnect_all_and_destroy (priv->sighandlers);
590 priv->sighandlers = NULL;
592 parent_window = gtk_window_get_transient_for (GTK_WINDOW (self));
593 if (parent_window && MODEST_IS_HEADER_WINDOW (parent_window)) {
594 header_view = GTK_WIDGET (modest_header_window_get_header_view (MODEST_HEADER_WINDOW (parent_window)));
596 modest_header_view_remove_observer(MODEST_HEADER_VIEW (header_view),
597 MODEST_HEADER_VIEW_OBSERVER(self));
603 modest_msg_view_window_finalize (GObject *obj)
605 ModestMsgViewWindowPrivate *priv;
607 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (obj);
609 /* Sanity check: shouldn't be needed, the window mgr should
610 call this function before */
611 modest_msg_view_window_disconnect_signals (MODEST_WINDOW (obj));
613 if (priv->header_model != NULL) {
614 g_object_unref (priv->header_model);
615 priv->header_model = NULL;
618 if (priv->remove_attachment_banner) {
619 gtk_widget_destroy (priv->remove_attachment_banner);
620 g_object_unref (priv->remove_attachment_banner);
621 priv->remove_attachment_banner = NULL;
624 if (priv->purge_timeout > 0) {
625 g_source_remove (priv->purge_timeout);
626 priv->purge_timeout = 0;
629 if (priv->row_reference) {
630 gtk_tree_row_reference_free (priv->row_reference);
631 priv->row_reference = NULL;
634 if (priv->next_row_reference) {
635 gtk_tree_row_reference_free (priv->next_row_reference);
636 priv->next_row_reference = NULL;
640 g_free (priv->msg_uid);
641 priv->msg_uid = NULL;
644 G_OBJECT_CLASS(parent_class)->finalize (obj);
648 select_next_valid_row (GtkTreeModel *model,
649 GtkTreeRowReference **row_reference,
653 GtkTreeIter tmp_iter;
655 GtkTreePath *next = NULL;
656 gboolean retval = FALSE, finished;
658 g_return_val_if_fail (gtk_tree_row_reference_valid (*row_reference), FALSE);
660 path = gtk_tree_row_reference_get_path (*row_reference);
661 gtk_tree_model_get_iter (model, &tmp_iter, path);
662 gtk_tree_row_reference_free (*row_reference);
663 *row_reference = NULL;
667 TnyHeader *header = NULL;
669 if (gtk_tree_model_iter_next (model, &tmp_iter)) {
670 gtk_tree_model_get (model, &tmp_iter,
671 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
675 if (msg_is_visible (header, is_outbox)) {
676 next = gtk_tree_model_get_path (model, &tmp_iter);
677 *row_reference = gtk_tree_row_reference_new (model, next);
678 gtk_tree_path_free (next);
682 g_object_unref (header);
685 } else if (cycle && gtk_tree_model_get_iter_first (model, &tmp_iter)) {
686 next = gtk_tree_model_get_path (model, &tmp_iter);
688 /* Ensure that we are not selecting the same */
689 if (gtk_tree_path_compare (path, next) != 0) {
690 gtk_tree_model_get (model, &tmp_iter,
691 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
694 if (msg_is_visible (header, is_outbox)) {
695 *row_reference = gtk_tree_row_reference_new (model, next);
699 g_object_unref (header);
703 /* If we ended up in the same message
704 then there is no valid next
708 gtk_tree_path_free (next);
710 /* If there are no more messages and we don't
711 want to start again in the first one then
712 there is no valid next message */
718 gtk_tree_path_free (path);
723 /* TODO: This should be in _init(), with the parameters as properties. */
725 modest_msg_view_window_construct (ModestMsgViewWindow *self,
726 const gchar *modest_account_name,
727 const gchar *mailbox,
728 const gchar *msg_uid)
731 ModestMsgViewWindowPrivate *priv = NULL;
732 ModestWindowPrivate *parent_priv = NULL;
733 ModestDimmingRulesGroup *toolbar_rules_group = NULL;
734 ModestDimmingRulesGroup *clipboard_rules_group = NULL;
736 obj = G_OBJECT (self);
737 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(obj);
738 parent_priv = MODEST_WINDOW_GET_PRIVATE(obj);
740 priv->msg_uid = g_strdup (msg_uid);
743 parent_priv->menubar = NULL;
745 toolbar_rules_group = modest_dimming_rules_group_new (MODEST_DIMMING_RULES_TOOLBAR, TRUE);
746 clipboard_rules_group = modest_dimming_rules_group_new (MODEST_DIMMING_RULES_CLIPBOARD, FALSE);
749 /* Add common dimming rules */
750 modest_dimming_rules_group_add_rules (toolbar_rules_group,
751 modest_msg_view_toolbar_dimming_entries,
752 G_N_ELEMENTS (modest_msg_view_toolbar_dimming_entries),
753 MODEST_WINDOW (self));
754 modest_dimming_rules_group_add_rules (clipboard_rules_group,
755 modest_msg_view_clipboard_dimming_entries,
756 G_N_ELEMENTS (modest_msg_view_clipboard_dimming_entries),
757 MODEST_WINDOW (self));
759 /* Insert dimming rules group for this window */
760 modest_ui_dimming_manager_insert_rules_group (parent_priv->ui_dimming_manager, toolbar_rules_group);
761 modest_ui_dimming_manager_insert_rules_group (parent_priv->ui_dimming_manager, clipboard_rules_group);
762 g_object_unref (toolbar_rules_group);
763 g_object_unref (clipboard_rules_group);
765 /* g_signal_connect (G_OBJECT(obj), "delete-event", G_CALLBACK(on_delete_event), obj); */
767 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);
768 g_signal_connect (G_OBJECT(priv->msg_view), "activate_link",
769 G_CALLBACK (modest_ui_actions_on_msg_link_clicked), obj);
770 g_signal_connect (G_OBJECT(priv->msg_view), "link_hover",
771 G_CALLBACK (modest_ui_actions_on_msg_link_hover), obj);
772 g_signal_connect (G_OBJECT(priv->msg_view), "attachment_clicked",
773 G_CALLBACK (modest_ui_actions_on_msg_attachment_clicked), obj);
774 g_signal_connect (G_OBJECT(priv->msg_view), "recpt_activated",
775 G_CALLBACK (modest_ui_actions_on_msg_recpt_activated), obj);
776 g_signal_connect (G_OBJECT(priv->msg_view), "show_details",
777 G_CALLBACK (modest_ui_actions_on_details), obj);
778 g_signal_connect (G_OBJECT(priv->msg_view), "link_contextual",
779 G_CALLBACK (modest_ui_actions_on_msg_link_contextual), obj);
780 g_signal_connect (G_OBJECT (priv->msg_view), "fetch_image",
781 G_CALLBACK (on_fetch_image), obj);
783 g_signal_connect (G_OBJECT (obj), "key-release-event",
784 G_CALLBACK (modest_msg_view_window_key_event),
787 g_signal_connect (G_OBJECT (obj), "key-press-event",
788 G_CALLBACK (modest_msg_view_window_key_event),
791 g_signal_connect (G_OBJECT (obj), "move-focus",
792 G_CALLBACK (on_move_focus), obj);
794 g_signal_connect (G_OBJECT (obj), "map-event",
795 G_CALLBACK (_modest_msg_view_window_map_event),
798 /* Mail Operation Queue */
799 priv->queue_change_handler = g_signal_connect (G_OBJECT (modest_runtime_get_mail_operation_queue ()),
801 G_CALLBACK (on_queue_changed),
804 /* Account manager */
805 priv->account_removed_handler = g_signal_connect (G_OBJECT (modest_runtime_get_account_store ()),
807 G_CALLBACK(on_account_removed),
810 modest_window_set_active_account (MODEST_WINDOW(obj), modest_account_name);
811 modest_window_set_active_mailbox (MODEST_WINDOW(obj), mailbox);
813 g_signal_connect (G_OBJECT (priv->find_toolbar), "close", G_CALLBACK (modest_msg_view_window_find_toolbar_close), obj);
814 g_signal_connect (G_OBJECT (priv->find_toolbar), "search", G_CALLBACK (modest_msg_view_window_find_toolbar_search), obj);
815 priv->last_search = NULL;
817 modest_msg_view_window_show_toolbar (MODEST_WINDOW (obj), TRUE);
819 /* Init the clipboard actions dim status */
820 modest_msg_view_grab_focus(MODEST_MSG_VIEW (priv->msg_view));
822 update_window_title (MODEST_MSG_VIEW_WINDOW (obj));
827 /* FIXME: parameter checks */
829 modest_msg_view_window_new_with_header_model (TnyMsg *msg,
830 const gchar *modest_account_name,
831 const gchar *mailbox,
832 const gchar *msg_uid,
834 GtkTreeRowReference *row_reference)
836 ModestMsgViewWindow *window = NULL;
837 ModestMsgViewWindowPrivate *priv = NULL;
838 TnyFolder *header_folder = NULL;
839 ModestHeaderView *header_view = NULL;
840 ModestWindowMgr *mgr = NULL;
843 modest_tny_mime_part_to_string (TNY_MIME_PART (msg), 0);
846 mgr = modest_runtime_get_window_mgr ();
847 window = MODEST_MSG_VIEW_WINDOW (modest_window_mgr_get_msg_view_window (mgr));
848 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), NULL);
850 modest_msg_view_window_construct (window, modest_account_name, mailbox, msg_uid);
852 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
854 /* Remember the message list's TreeModel so we can detect changes
855 * and change the list selection when necessary: */
856 header_folder = modest_header_view_get_folder (header_view);
858 priv->is_outbox = (modest_tny_folder_guess_folder_type (header_folder) ==
859 TNY_FOLDER_TYPE_OUTBOX);
860 priv->header_folder_id = tny_folder_get_id (header_folder);
861 g_object_unref(header_folder);
864 /* Setup row references and connect signals */
865 priv->header_model = g_object_ref (model);
868 priv->row_reference = gtk_tree_row_reference_copy (row_reference);
869 priv->next_row_reference = gtk_tree_row_reference_copy (row_reference);
870 select_next_valid_row (model, &(priv->next_row_reference), TRUE, priv->is_outbox);
872 priv->row_reference = NULL;
873 priv->next_row_reference = NULL;
876 /* Connect signals */
877 priv->row_changed_handler =
878 g_signal_connect (GTK_TREE_MODEL(model), "row-changed",
879 G_CALLBACK(modest_msg_view_window_on_row_changed),
881 priv->row_deleted_handler =
882 g_signal_connect (GTK_TREE_MODEL(model), "row-deleted",
883 G_CALLBACK(modest_msg_view_window_on_row_deleted),
885 priv->row_inserted_handler =
886 g_signal_connect (GTK_TREE_MODEL(model), "row-inserted",
887 G_CALLBACK(modest_msg_view_window_on_row_inserted),
889 priv->rows_reordered_handler =
890 g_signal_connect(GTK_TREE_MODEL(model), "rows-reordered",
891 G_CALLBACK(modest_msg_view_window_on_row_reordered),
894 if (header_view != NULL){
895 modest_header_view_add_observer(header_view,
896 MODEST_HEADER_VIEW_OBSERVER(window));
899 tny_msg_view_set_msg (TNY_MSG_VIEW (priv->msg_view), msg);
900 update_window_title (MODEST_MSG_VIEW_WINDOW (window));
902 /* gtk_widget_show_all (GTK_WIDGET (window)); */
903 modest_msg_view_window_update_priority (window);
904 /* Check dimming rules */
905 modest_ui_actions_check_toolbar_dimming_rules (MODEST_WINDOW (window));
906 modest_ui_actions_check_menu_dimming_rules (MODEST_WINDOW (window));
907 modest_window_check_dimming_rules_group (MODEST_WINDOW (window), MODEST_DIMMING_RULES_CLIPBOARD);
909 return MODEST_WINDOW(window);
913 modest_msg_view_window_new_from_header_view (ModestHeaderView *header_view,
914 const gchar *modest_account_name,
915 const gchar *mailbox,
916 const gchar *msg_uid,
917 GtkTreeRowReference *row_reference)
919 ModestMsgViewWindow *window = NULL;
920 ModestMsgViewWindowPrivate *priv = NULL;
921 TnyFolder *header_folder = NULL;
922 ModestWindowMgr *mgr = NULL;
926 mgr = modest_runtime_get_window_mgr ();
927 window = MODEST_MSG_VIEW_WINDOW (modest_window_mgr_get_msg_view_window (mgr));
928 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), NULL);
930 modest_msg_view_window_construct (window, modest_account_name, mailbox, msg_uid);
932 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
934 /* Remember the message list's TreeModel so we can detect changes
935 * and change the list selection when necessary: */
937 if (header_view != NULL){
938 header_folder = modest_header_view_get_folder(header_view);
939 /* This could happen if the header folder was
940 unseleted before opening this msg window (for
941 example if the user selects an account in the
942 folder view of the main window */
944 priv->is_outbox = (modest_tny_folder_guess_folder_type (header_folder) ==
945 TNY_FOLDER_TYPE_OUTBOX);
946 priv->header_folder_id = tny_folder_get_id(header_folder);
947 g_object_unref(header_folder);
951 /* Setup row references and connect signals */
952 priv->header_model = gtk_tree_view_get_model (GTK_TREE_VIEW (header_view));
953 g_object_ref (priv->header_model);
956 priv->row_reference = gtk_tree_row_reference_copy (row_reference);
957 priv->next_row_reference = gtk_tree_row_reference_copy (row_reference);
958 select_next_valid_row (priv->header_model, &(priv->next_row_reference), TRUE, priv->is_outbox);
960 priv->row_reference = NULL;
961 priv->next_row_reference = NULL;
964 /* Connect signals */
965 priv->row_changed_handler =
966 g_signal_connect (GTK_TREE_MODEL(priv->header_model), "row-changed",
967 G_CALLBACK(modest_msg_view_window_on_row_changed),
969 priv->row_deleted_handler =
970 g_signal_connect (GTK_TREE_MODEL(priv->header_model), "row-deleted",
971 G_CALLBACK(modest_msg_view_window_on_row_deleted),
973 priv->row_inserted_handler =
974 g_signal_connect (GTK_TREE_MODEL(priv->header_model), "row-inserted",
975 G_CALLBACK(modest_msg_view_window_on_row_inserted),
977 priv->rows_reordered_handler =
978 g_signal_connect(GTK_TREE_MODEL(priv->header_model), "rows-reordered",
979 G_CALLBACK(modest_msg_view_window_on_row_reordered),
982 if (header_view != NULL){
983 modest_header_view_add_observer(header_view,
984 MODEST_HEADER_VIEW_OBSERVER(window));
987 tny_msg_view_set_msg (TNY_MSG_VIEW (priv->msg_view), NULL);
989 path = gtk_tree_row_reference_get_path (row_reference);
990 if (gtk_tree_model_get_iter (priv->header_model, &iter, path)) {
992 gtk_tree_model_get (priv->header_model, &iter,
993 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
995 message_reader (window, priv, header, row_reference);
996 g_object_unref (header);
998 gtk_tree_path_free (path);
1000 /* Check dimming rules */
1001 modest_ui_actions_check_toolbar_dimming_rules (MODEST_WINDOW (window));
1002 modest_ui_actions_check_menu_dimming_rules (MODEST_WINDOW (window));
1003 modest_window_check_dimming_rules_group (MODEST_WINDOW (window), MODEST_DIMMING_RULES_CLIPBOARD);
1005 return MODEST_WINDOW(window);
1009 modest_msg_view_window_new_for_search_result (TnyMsg *msg,
1010 const gchar *modest_account_name,
1011 const gchar *mailbox,
1012 const gchar *msg_uid)
1014 ModestMsgViewWindow *window = NULL;
1015 ModestMsgViewWindowPrivate *priv = NULL;
1016 ModestWindowMgr *mgr = NULL;
1018 mgr = modest_runtime_get_window_mgr ();
1019 window = MODEST_MSG_VIEW_WINDOW (modest_window_mgr_get_msg_view_window (mgr));
1020 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), NULL);
1021 modest_msg_view_window_construct (window, modest_account_name, mailbox, msg_uid);
1023 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1025 /* Remember that this is a search result,
1026 * so we can disable some UI appropriately: */
1027 priv->is_search_result = TRUE;
1029 tny_msg_view_set_msg (TNY_MSG_VIEW (priv->msg_view), msg);
1031 update_window_title (window);
1032 /* gtk_widget_show_all (GTK_WIDGET (window));*/
1033 modest_msg_view_window_update_priority (window);
1035 /* Check dimming rules */
1036 modest_ui_actions_check_toolbar_dimming_rules (MODEST_WINDOW (window));
1037 modest_ui_actions_check_menu_dimming_rules (MODEST_WINDOW (window));
1038 modest_window_check_dimming_rules_group (MODEST_WINDOW (window), MODEST_DIMMING_RULES_CLIPBOARD);
1040 return MODEST_WINDOW(window);
1044 modest_msg_view_window_new_with_other_body (TnyMsg *msg,
1045 TnyMimePart *other_body,
1046 const gchar *modest_account_name,
1047 const gchar *mailbox,
1048 const gchar *msg_uid)
1050 GObject *obj = NULL;
1051 ModestMsgViewWindowPrivate *priv;
1052 ModestWindowMgr *mgr = NULL;
1054 g_return_val_if_fail (msg, NULL);
1055 mgr = modest_runtime_get_window_mgr ();
1056 obj = G_OBJECT (modest_window_mgr_get_msg_view_window (mgr));
1057 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (obj);
1058 modest_msg_view_window_construct (MODEST_MSG_VIEW_WINDOW (obj),
1059 modest_account_name, mailbox, msg_uid);
1062 modest_msg_view_set_msg_with_other_body (MODEST_MSG_VIEW (priv->msg_view), msg, other_body);
1064 tny_msg_view_set_msg (TNY_MSG_VIEW (priv->msg_view), msg);
1066 update_window_title (MODEST_MSG_VIEW_WINDOW (obj));
1068 /* gtk_widget_show_all (GTK_WIDGET (obj)); */
1070 /* Check dimming rules */
1071 modest_ui_actions_check_toolbar_dimming_rules (MODEST_WINDOW (obj));
1072 modest_ui_actions_check_menu_dimming_rules (MODEST_WINDOW (obj));
1073 modest_window_check_dimming_rules_group (MODEST_WINDOW (obj), MODEST_DIMMING_RULES_CLIPBOARD);
1075 return MODEST_WINDOW(obj);
1079 modest_msg_view_window_new_for_attachment (TnyMsg *msg,
1080 const gchar *modest_account_name,
1081 const gchar *mailbox,
1082 const gchar *msg_uid)
1084 return modest_msg_view_window_new_with_other_body (msg, NULL, modest_account_name, mailbox, msg_uid);
1088 modest_msg_view_window_on_row_changed (GtkTreeModel *header_model,
1091 ModestMsgViewWindow *window)
1093 check_dimming_rules_after_change (window);
1097 modest_msg_view_window_on_row_deleted(GtkTreeModel *header_model,
1099 ModestMsgViewWindow *window)
1101 check_dimming_rules_after_change (window);
1103 /* The window could have dissapeared */
1106 check_dimming_rules_after_change (ModestMsgViewWindow *window)
1108 modest_ui_actions_check_menu_dimming_rules (MODEST_WINDOW (window));
1109 modest_ui_actions_check_toolbar_dimming_rules (MODEST_WINDOW (window));
1113 /* On insertions we check if the folder still has the message we are
1114 * showing or do not. If do not, we do nothing. Which means we are still
1115 * not attached to any header folder and thus next/prev buttons are
1116 * still dimmed. Once the message that is shown by msg-view is found, the
1117 * new model of header-view will be attached and the references will be set.
1118 * On each further insertions dimming rules will be checked. However
1119 * this requires extra CPU time at least works.
1120 * (An message might be deleted from TnyFolder and thus will not be
1121 * inserted into the model again for example if it is removed by the
1122 * imap server and the header view is refreshed.)
1125 modest_msg_view_window_on_row_inserted (GtkTreeModel *model,
1126 GtkTreePath *tree_path,
1127 GtkTreeIter *tree_iter,
1128 ModestMsgViewWindow *window)
1130 ModestMsgViewWindowPrivate *priv = NULL;
1131 TnyHeader *header = NULL;
1133 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window));
1134 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1136 g_assert (model == priv->header_model);
1138 /* Check if the newly inserted message is the same we are actually
1139 * showing. IF not, we should remain detached from the header model
1140 * and thus prev and next toolbar buttons should remain dimmed. */
1141 gtk_tree_model_get (model, tree_iter,
1142 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
1145 if (TNY_IS_HEADER (header)) {
1148 uid = modest_tny_folder_get_header_unique_id (header);
1149 if (!g_str_equal(priv->msg_uid, uid)) {
1150 check_dimming_rules_after_change (window);
1152 g_object_unref (G_OBJECT(header));
1156 g_object_unref(G_OBJECT(header));
1159 if (priv->row_reference) {
1160 gtk_tree_row_reference_free (priv->row_reference);
1163 /* Setup row_reference for the actual msg. */
1164 priv->row_reference = gtk_tree_row_reference_new (priv->header_model, tree_path);
1165 if (priv->row_reference == NULL) {
1166 g_warning("No reference for msg header item.");
1170 /* Now set up next_row_reference. */
1171 if (priv->next_row_reference) {
1172 gtk_tree_row_reference_free (priv->next_row_reference);
1175 priv->next_row_reference = gtk_tree_row_reference_copy (priv->row_reference);
1176 select_next_valid_row (priv->header_model,
1177 &(priv->next_row_reference), FALSE, priv->is_outbox);
1179 /* Connect the remaining callbacks to become able to detect
1180 * changes in header-view. */
1181 priv->row_changed_handler =
1182 g_signal_connect (priv->header_model, "row-changed",
1183 G_CALLBACK (modest_msg_view_window_on_row_changed),
1185 priv->row_deleted_handler =
1186 g_signal_connect (priv->header_model, "row-deleted",
1187 G_CALLBACK (modest_msg_view_window_on_row_deleted),
1189 priv->rows_reordered_handler =
1190 g_signal_connect (priv->header_model, "rows-reordered",
1191 G_CALLBACK (modest_msg_view_window_on_row_reordered),
1194 check_dimming_rules_after_change (window);
1198 modest_msg_view_window_on_row_reordered (GtkTreeModel *header_model,
1202 ModestMsgViewWindow *window)
1204 ModestMsgViewWindowPrivate *priv = NULL;
1205 gboolean already_changed = FALSE;
1207 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(window);
1209 /* If the current row was reordered select the proper next
1210 valid row. The same if the next row reference changes */
1211 if (priv->row_reference &&
1212 gtk_tree_row_reference_valid (priv->row_reference)) {
1214 path = gtk_tree_row_reference_get_path (priv->row_reference);
1215 if (gtk_tree_path_compare (path, arg1) == 0) {
1216 if (priv->next_row_reference) {
1217 gtk_tree_row_reference_free (priv->next_row_reference);
1219 priv->next_row_reference = gtk_tree_row_reference_copy (priv->row_reference);
1220 select_next_valid_row (header_model, &(priv->next_row_reference), FALSE, priv->is_outbox);
1221 already_changed = TRUE;
1223 gtk_tree_path_free (path);
1225 if (!already_changed &&
1226 priv->next_row_reference &&
1227 gtk_tree_row_reference_valid (priv->next_row_reference)) {
1229 path = gtk_tree_row_reference_get_path (priv->next_row_reference);
1230 if (gtk_tree_path_compare (path, arg1) == 0) {
1231 if (priv->next_row_reference) {
1232 gtk_tree_row_reference_free (priv->next_row_reference);
1234 priv->next_row_reference = gtk_tree_row_reference_copy (priv->row_reference);
1235 select_next_valid_row (header_model, &(priv->next_row_reference), FALSE, priv->is_outbox);
1237 gtk_tree_path_free (path);
1239 check_dimming_rules_after_change (window);
1242 /* The modest_msg_view_window_update_model_replaced implements update
1243 * function for ModestHeaderViewObserver. Checks whether the TnyFolder
1244 * actually belongs to the header-view is the same as the TnyFolder of
1245 * the message of msg-view or not. If they are different, there is
1246 * nothing to do. If they are the same, then the model has replaced and
1247 * the reference in msg-view shall be replaced from the old model to
1248 * the new model. In this case the view will be detached from it's
1249 * header folder. From this point the next/prev buttons are dimmed.
1252 modest_msg_view_window_update_model_replaced (ModestHeaderViewObserver *observer,
1253 GtkTreeModel *model,
1254 const gchar *tny_folder_id)
1256 ModestMsgViewWindowPrivate *priv = NULL;
1257 ModestMsgViewWindow *window = NULL;
1259 g_assert(MODEST_IS_HEADER_VIEW_OBSERVER(observer));
1260 g_assert(MODEST_IS_MSG_VIEW_WINDOW(observer));
1262 window = MODEST_MSG_VIEW_WINDOW(observer);
1263 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(window);
1265 /* If there is an other folder in the header-view then we do
1266 * not care about it's model (msg list). Else if the
1267 * header-view shows the folder the msg shown by us is in, we
1268 * shall replace our model reference and make some check. */
1269 if(model == NULL || tny_folder_id == NULL ||
1270 (priv->header_folder_id && !g_str_equal(tny_folder_id, priv->header_folder_id)))
1273 /* Model is changed(replaced), so we should forget the old
1274 * one. Because there might be other references and there
1275 * might be some change on the model even if we unreferenced
1276 * it, we need to disconnect our signals here. */
1277 if (priv->header_model) {
1278 if (g_signal_handler_is_connected(G_OBJECT (priv->header_model),
1279 priv->row_changed_handler))
1280 g_signal_handler_disconnect(G_OBJECT (priv->header_model),
1281 priv->row_changed_handler);
1282 if (g_signal_handler_is_connected(G_OBJECT (priv->header_model),
1283 priv->row_deleted_handler))
1284 g_signal_handler_disconnect(G_OBJECT (priv->header_model),
1285 priv->row_deleted_handler);
1286 if (g_signal_handler_is_connected(G_OBJECT (priv->header_model),
1287 priv->row_inserted_handler))
1288 g_signal_handler_disconnect(G_OBJECT (priv->header_model),
1289 priv->row_inserted_handler);
1290 if (g_signal_handler_is_connected(G_OBJECT (priv->header_model),
1291 priv->rows_reordered_handler))
1292 g_signal_handler_disconnect(G_OBJECT (priv->header_model),
1293 priv->rows_reordered_handler);
1296 if (priv->row_reference)
1297 gtk_tree_row_reference_free (priv->row_reference);
1298 if (priv->next_row_reference)
1299 gtk_tree_row_reference_free (priv->next_row_reference);
1300 g_object_unref(priv->header_model);
1303 priv->row_changed_handler = 0;
1304 priv->row_deleted_handler = 0;
1305 priv->row_inserted_handler = 0;
1306 priv->rows_reordered_handler = 0;
1307 priv->next_row_reference = NULL;
1308 priv->row_reference = NULL;
1309 priv->header_model = NULL;
1312 priv->header_model = g_object_ref (model);
1314 /* Also we must connect to the new model for row insertions.
1315 * Only for insertions now. We will need other ones only after
1316 * the msg is show by msg-view is added to the new model. */
1317 priv->row_inserted_handler =
1318 g_signal_connect (priv->header_model, "row-inserted",
1319 G_CALLBACK(modest_msg_view_window_on_row_inserted),
1322 modest_ui_actions_check_menu_dimming_rules(MODEST_WINDOW(window));
1323 modest_ui_actions_check_toolbar_dimming_rules(MODEST_WINDOW(window));
1327 modest_msg_view_window_toolbar_on_transfer_mode (ModestMsgViewWindow *self)
1329 ModestMsgViewWindowPrivate *priv= NULL;
1331 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self), FALSE);
1332 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
1334 return priv->progress_hint;
1338 modest_msg_view_window_get_header (ModestMsgViewWindow *self)
1340 ModestMsgViewWindowPrivate *priv= NULL;
1342 TnyHeader *header = NULL;
1343 GtkTreePath *path = NULL;
1346 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self), NULL);
1347 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
1349 /* If the message was not obtained from a treemodel,
1350 * for instance if it was opened directly by the search UI:
1352 if (priv->header_model == NULL ||
1353 priv->row_reference == NULL ||
1354 !gtk_tree_row_reference_valid (priv->row_reference)) {
1355 msg = modest_msg_view_window_get_message (self);
1357 header = tny_msg_get_header (msg);
1358 g_object_unref (msg);
1363 /* Get iter of the currently selected message in the header view: */
1364 path = gtk_tree_row_reference_get_path (priv->row_reference);
1365 g_return_val_if_fail (path != NULL, NULL);
1366 gtk_tree_model_get_iter (priv->header_model,
1370 /* Get current message header */
1371 gtk_tree_model_get (priv->header_model, &iter,
1372 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
1375 gtk_tree_path_free (path);
1380 modest_msg_view_window_get_message (ModestMsgViewWindow *self)
1382 ModestMsgViewWindowPrivate *priv;
1384 g_return_val_if_fail (self, NULL);
1386 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(self);
1388 return tny_msg_view_get_msg (TNY_MSG_VIEW (priv->msg_view));
1392 modest_msg_view_window_get_message_uid (ModestMsgViewWindow *self)
1394 ModestMsgViewWindowPrivate *priv;
1396 g_return_val_if_fail (self, NULL);
1398 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
1400 return (const gchar*) priv->msg_uid;
1404 modest_msg_view_window_toggle_find_toolbar (GtkToggleAction *toggle,
1407 ModestMsgViewWindow *window = MODEST_MSG_VIEW_WINDOW (data);
1408 ModestMsgViewWindowPrivate *priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1409 ModestWindowPrivate *parent_priv = MODEST_WINDOW_GET_PRIVATE (window);
1413 is_active = gtk_toggle_action_get_active (toggle);
1416 gtk_widget_show (priv->find_toolbar);
1417 hildon_find_toolbar_highlight_entry (HILDON_FIND_TOOLBAR (priv->find_toolbar), TRUE);
1419 gtk_widget_hide (priv->find_toolbar);
1420 modest_msg_view_grab_focus (MODEST_MSG_VIEW (priv->msg_view));
1423 /* update the toggle buttons status */
1424 action = gtk_ui_manager_get_action (parent_priv->ui_manager, "/ToolBar/FindInMessage");
1426 modest_utils_toggle_action_set_active_block_notify (GTK_TOGGLE_ACTION (action), is_active);
1431 modest_msg_view_window_find_toolbar_close (GtkWidget *widget,
1432 ModestMsgViewWindow *obj)
1434 GtkToggleAction *toggle;
1435 ModestWindowPrivate *parent_priv;
1436 ModestMsgViewWindowPrivate *priv;
1438 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (obj);
1439 parent_priv = MODEST_WINDOW_GET_PRIVATE (obj);
1441 toggle = GTK_TOGGLE_ACTION (gtk_ui_manager_get_action (parent_priv->ui_manager, "/ToolBar/FindInMessage"));
1442 gtk_toggle_action_set_active (toggle, FALSE);
1443 modest_msg_view_grab_focus (MODEST_MSG_VIEW (priv->msg_view));
1447 modest_msg_view_window_find_toolbar_search (GtkWidget *widget,
1448 ModestMsgViewWindow *obj)
1450 gchar *current_search;
1451 ModestMsgViewWindowPrivate *priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (obj);
1453 if (modest_mime_part_view_is_empty (MODEST_MIME_PART_VIEW (priv->msg_view))) {
1454 hildon_banner_show_information (NULL, NULL, _("mail_ib_nothing_to_find"));
1458 g_object_get (G_OBJECT (widget), "prefix", ¤t_search, NULL);
1460 if ((current_search == NULL) || (strcmp (current_search, "") == 0)) {
1461 g_free (current_search);
1462 hildon_banner_show_information (NULL, NULL, _CS("ecdg_ib_find_rep_enter_text"));
1466 if ((priv->last_search == NULL) || (strcmp (priv->last_search, current_search) != 0)) {
1468 g_free (priv->last_search);
1469 priv->last_search = g_strdup (current_search);
1470 result = modest_isearch_view_search (MODEST_ISEARCH_VIEW (priv->msg_view),
1473 hildon_banner_show_information (NULL, NULL,
1474 _HL("ckct_ib_find_no_matches"));
1475 g_free (priv->last_search);
1476 priv->last_search = NULL;
1478 hildon_find_toolbar_highlight_entry (HILDON_FIND_TOOLBAR (priv->find_toolbar), TRUE);
1481 if (!modest_isearch_view_search_next (MODEST_ISEARCH_VIEW (priv->msg_view))) {
1482 hildon_banner_show_information (NULL, NULL,
1483 _HL("ckct_ib_find_search_complete"));
1484 g_free (priv->last_search);
1485 priv->last_search = NULL;
1487 hildon_find_toolbar_highlight_entry (HILDON_FIND_TOOLBAR (priv->find_toolbar), TRUE);
1491 g_free (current_search);
1496 modest_msg_view_window_set_zoom (ModestWindow *window,
1499 ModestMsgViewWindowPrivate *priv;
1500 ModestWindowPrivate *parent_priv;
1502 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window));
1504 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1505 parent_priv = MODEST_WINDOW_GET_PRIVATE (window);
1506 modest_zoomable_set_zoom (MODEST_ZOOMABLE (priv->msg_view), zoom);
1511 modest_msg_view_window_get_zoom (ModestWindow *window)
1513 ModestMsgViewWindowPrivate *priv;
1515 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), 1.0);
1517 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1518 return modest_zoomable_get_zoom (MODEST_ZOOMABLE (priv->msg_view));
1522 modest_msg_view_window_zoom_plus (ModestWindow *window)
1525 ModestMsgViewWindowPrivate *priv;
1529 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), 1.0);
1530 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1532 zoom_level = modest_zoomable_get_zoom (MODEST_ZOOMABLE (priv->msg_view));
1534 if (zoom_level >= 2.0) {
1535 hildon_banner_show_information (NULL, NULL,
1536 _CS("ckct_ib_max_zoom_level_reached"));
1538 } else if (zoom_level >= 1.5) {
1540 } else if (zoom_level >= 1.2) {
1542 } else if (zoom_level >= 1.0) {
1544 } else if (zoom_level >= 0.8) {
1546 } else if (zoom_level >= 0.5) {
1552 /* set zoom level */
1553 int_zoom = (gint) rint (zoom_level*100.0+0.1);
1554 banner_text = g_strdup_printf (_HL("wdgt_ib_zoom"), int_zoom);
1555 modest_platform_information_banner (GTK_WIDGET (window), NULL, banner_text);
1556 g_free (banner_text);
1557 modest_zoomable_set_zoom (MODEST_ZOOMABLE (priv->msg_view), zoom_level);
1563 modest_msg_view_window_zoom_minus (ModestWindow *window)
1566 ModestMsgViewWindowPrivate *priv;
1570 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), 1.0);
1571 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1573 zoom_level = modest_zoomable_get_zoom (MODEST_ZOOMABLE (priv->msg_view));
1575 if (zoom_level <= 0.5) {
1576 hildon_banner_show_information (NULL, NULL,
1577 _CS("ckct_ib_min_zoom_level_reached"));
1579 } else if (zoom_level <= 0.8) {
1581 } else if (zoom_level <= 1.0) {
1583 } else if (zoom_level <= 1.2) {
1585 } else if (zoom_level <= 1.5) {
1587 } else if (zoom_level <= 2.0) {
1593 /* set zoom level */
1594 int_zoom = (gint) rint (zoom_level*100.0+0.1);
1595 banner_text = g_strdup_printf (_HL("wdgt_ib_zoom"), int_zoom);
1596 modest_platform_information_banner (GTK_WIDGET (window), NULL, banner_text);
1597 g_free (banner_text);
1598 modest_zoomable_set_zoom (MODEST_ZOOMABLE (priv->msg_view), zoom_level);
1605 modest_msg_view_window_key_event (GtkWidget *window,
1611 focus = gtk_window_get_focus (GTK_WINDOW (window));
1613 /* for the find toolbar case */
1614 if (focus && GTK_IS_ENTRY (focus)) {
1615 if (event->keyval == GDK_BackSpace) {
1617 copy = gdk_event_copy ((GdkEvent *) event);
1618 gtk_widget_event (focus, copy);
1619 gdk_event_free (copy);
1624 if (event->keyval == GDK_Up || event->keyval == GDK_KP_Up ||
1625 event->keyval == GDK_Down || event->keyval == GDK_KP_Down ||
1626 event->keyval == GDK_Page_Up || event->keyval == GDK_KP_Page_Up ||
1627 event->keyval == GDK_Page_Down || event->keyval == GDK_KP_Page_Down ||
1628 event->keyval == GDK_Home || event->keyval == GDK_KP_Home ||
1629 event->keyval == GDK_End || event->keyval == GDK_KP_End) {
1630 /* ModestMsgViewWindowPrivate *priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window); */
1631 /* gboolean return_value; */
1633 if (event->type == GDK_KEY_PRESS) {
1634 GtkScrollType scroll_type;
1636 switch (event->keyval) {
1639 scroll_type = GTK_SCROLL_STEP_UP; break;
1642 scroll_type = GTK_SCROLL_STEP_DOWN; break;
1644 case GDK_KP_Page_Up:
1645 scroll_type = GTK_SCROLL_PAGE_UP; break;
1647 case GDK_KP_Page_Down:
1648 scroll_type = GTK_SCROLL_PAGE_DOWN; break;
1651 scroll_type = GTK_SCROLL_START; break;
1654 scroll_type = GTK_SCROLL_END; break;
1655 default: scroll_type = GTK_SCROLL_NONE;
1658 /* g_signal_emit_by_name (G_OBJECT (priv->main_scroll), "scroll-child", */
1659 /* scroll_type, FALSE, &return_value); */
1670 modest_msg_view_window_last_message_selected (ModestMsgViewWindow *window)
1673 ModestMsgViewWindowPrivate *priv;
1674 GtkTreeIter tmp_iter;
1675 gboolean is_last_selected;
1677 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), TRUE);
1678 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1680 /*if no model (so no rows at all), then virtually we are the last*/
1681 if (!priv->header_model || !priv->row_reference)
1684 if (!gtk_tree_row_reference_valid (priv->row_reference))
1687 path = gtk_tree_row_reference_get_path (priv->row_reference);
1691 is_last_selected = TRUE;
1692 while (is_last_selected) {
1694 gtk_tree_path_next (path);
1695 if (!gtk_tree_model_get_iter (priv->header_model, &tmp_iter, path))
1697 gtk_tree_model_get (priv->header_model, &tmp_iter,
1698 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
1701 if (msg_is_visible (header, priv->is_outbox))
1702 is_last_selected = FALSE;
1703 g_object_unref(G_OBJECT(header));
1706 gtk_tree_path_free (path);
1707 return is_last_selected;
1711 modest_msg_view_window_has_headers_model (ModestMsgViewWindow *window)
1713 ModestMsgViewWindowPrivate *priv;
1715 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), TRUE);
1716 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1718 return priv->header_model != NULL;
1722 modest_msg_view_window_is_search_result (ModestMsgViewWindow *window)
1724 ModestMsgViewWindowPrivate *priv;
1726 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), TRUE);
1727 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1729 return priv->is_search_result;
1733 msg_is_visible (TnyHeader *header, gboolean check_outbox)
1735 if ((tny_header_get_flags(header) & TNY_HEADER_FLAG_DELETED))
1737 if (!check_outbox) {
1740 ModestTnySendQueueStatus status;
1741 status = modest_tny_all_send_queues_get_msg_status (header);
1742 return ((status != MODEST_TNY_SEND_QUEUE_FAILED) &&
1743 (status != MODEST_TNY_SEND_QUEUE_SENDING));
1748 modest_msg_view_window_first_message_selected (ModestMsgViewWindow *window)
1751 ModestMsgViewWindowPrivate *priv;
1752 gboolean is_first_selected;
1753 GtkTreeIter tmp_iter;
1755 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), TRUE);
1756 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1758 /*if no model (so no rows at all), then virtually we are the first*/
1759 if (!priv->header_model || !priv->row_reference)
1762 if (!gtk_tree_row_reference_valid (priv->row_reference))
1765 path = gtk_tree_row_reference_get_path (priv->row_reference);
1769 is_first_selected = TRUE;
1770 while (is_first_selected) {
1772 if(!gtk_tree_path_prev (path))
1774 /* Here the 'if' is needless for logic, but let make sure
1775 * iter is valid for gtk_tree_model_get. */
1776 if (!gtk_tree_model_get_iter (priv->header_model, &tmp_iter, path))
1778 gtk_tree_model_get (priv->header_model, &tmp_iter,
1779 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
1782 if (msg_is_visible (header, priv->is_outbox))
1783 is_first_selected = FALSE;
1784 g_object_unref(G_OBJECT(header));
1787 gtk_tree_path_free (path);
1788 return is_first_selected;
1793 GtkTreeRowReference *row_reference;
1797 message_reader_performer (gboolean canceled,
1799 GtkWindow *parent_window,
1800 TnyAccount *account,
1803 ModestMailOperation *mail_op = NULL;
1804 MsgReaderInfo *info;
1806 info = (MsgReaderInfo *) user_data;
1807 if (canceled || err) {
1808 update_window_title (MODEST_MSG_VIEW_WINDOW (parent_window));
1812 /* Register the header - it'll be unregistered in the callback */
1813 modest_window_mgr_register_header (modest_runtime_get_window_mgr (), info->header, NULL);
1815 /* New mail operation */
1816 mail_op = modest_mail_operation_new_with_error_handling (G_OBJECT(parent_window),
1817 modest_ui_actions_disk_operations_error_handler,
1820 modest_mail_operation_queue_add (modest_runtime_get_mail_operation_queue (), mail_op);
1821 modest_mail_operation_get_msg (mail_op, info->header, TRUE, view_msg_cb, info->row_reference);
1822 g_object_unref (mail_op);
1824 /* Update dimming rules */
1825 modest_ui_actions_check_toolbar_dimming_rules (MODEST_WINDOW (parent_window));
1826 modest_ui_actions_check_menu_dimming_rules (MODEST_WINDOW (parent_window));
1829 /* Frees. The row_reference will be freed by the view_msg_cb callback */
1830 g_object_unref (info->header);
1831 g_slice_free (MsgReaderInfo, info);
1836 * Reads the message whose summary item is @header. It takes care of
1837 * several things, among others:
1839 * If the message was not previously downloaded then ask the user
1840 * before downloading. If there is no connection launch the connection
1841 * dialog. Update toolbar dimming rules.
1843 * Returns: TRUE if the mail operation was started, otherwise if the
1844 * user do not want to download the message, or if the user do not
1845 * want to connect, then the operation is not issued
1848 message_reader (ModestMsgViewWindow *window,
1849 ModestMsgViewWindowPrivate *priv,
1851 GtkTreeRowReference *row_reference)
1853 ModestWindowMgr *mgr;
1854 TnyAccount *account;
1856 MsgReaderInfo *info;
1858 g_return_val_if_fail (row_reference != NULL, FALSE);
1860 /* We set the header from model while we're loading */
1861 tny_header_view_set_header (TNY_HEADER_VIEW (priv->msg_view), header);
1862 gtk_window_set_title (GTK_WINDOW (window), _CS("ckdg_pb_updating"));
1864 mgr = modest_runtime_get_window_mgr ();
1865 /* Msg download completed */
1866 if (!(tny_header_get_flags (header) & TNY_HEADER_FLAG_CACHED)) {
1868 /* Ask the user if he wants to download the message if
1870 if (!tny_device_is_online (modest_runtime_get_device())) {
1871 GtkResponseType response;
1873 response = modest_platform_run_confirmation_dialog (GTK_WINDOW (window),
1874 _("mcen_nc_get_msg"));
1875 if (response == GTK_RESPONSE_CANCEL) {
1876 update_window_title (window);
1880 folder = tny_header_get_folder (header);
1881 info = g_slice_new (MsgReaderInfo);
1882 info->header = g_object_ref (header);
1883 info->row_reference = gtk_tree_row_reference_copy (row_reference);
1885 /* Offer the connection dialog if necessary */
1886 modest_platform_connect_if_remote_and_perform ((GtkWindow *) window,
1888 TNY_FOLDER_STORE (folder),
1889 message_reader_performer,
1891 g_object_unref (folder);
1896 folder = tny_header_get_folder (header);
1897 account = tny_folder_get_account (folder);
1898 info = g_slice_new (MsgReaderInfo);
1899 info->header = g_object_ref (header);
1900 info->row_reference = gtk_tree_row_reference_copy (row_reference);
1902 message_reader_performer (FALSE, NULL, (GtkWindow *) window, account, info);
1903 g_object_unref (account);
1904 g_object_unref (folder);
1910 modest_msg_view_window_select_next_message (ModestMsgViewWindow *window)
1912 ModestMsgViewWindowPrivate *priv;
1913 GtkTreePath *path= NULL;
1914 GtkTreeIter tmp_iter;
1916 gboolean retval = TRUE;
1917 GtkTreeRowReference *row_reference = NULL;
1919 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), FALSE);
1920 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1922 if (!priv->row_reference)
1925 /* Update the next row reference if it's not valid. This could
1926 happen if for example the header which it was pointing to,
1927 was deleted. The best place to do it is in the row-deleted
1928 handler but the tinymail model do not work like the glib
1929 tree models and reports the deletion when the row is still
1931 if (!gtk_tree_row_reference_valid (priv->next_row_reference)) {
1932 if (gtk_tree_row_reference_valid (priv->row_reference)) {
1933 priv->next_row_reference = gtk_tree_row_reference_copy (priv->row_reference);
1934 select_next_valid_row (priv->header_model, &(priv->next_row_reference), FALSE, priv->is_outbox);
1937 if (priv->next_row_reference)
1938 path = gtk_tree_row_reference_get_path (priv->next_row_reference);
1942 row_reference = gtk_tree_row_reference_copy (priv->next_row_reference);
1944 gtk_tree_model_get_iter (priv->header_model,
1947 gtk_tree_path_free (path);
1949 gtk_tree_model_get (priv->header_model, &tmp_iter,
1950 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
1953 /* Read the message & show it */
1954 if (!message_reader (window, priv, header, row_reference)) {
1957 gtk_tree_row_reference_free (row_reference);
1960 g_object_unref (header);
1966 modest_msg_view_window_select_previous_message (ModestMsgViewWindow *window)
1968 ModestMsgViewWindowPrivate *priv = NULL;
1970 gboolean finished = FALSE;
1971 gboolean retval = FALSE;
1973 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), FALSE);
1974 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1976 /* Return inmediatly if there is no header model */
1977 if (!priv->header_model || !priv->row_reference)
1980 path = gtk_tree_row_reference_get_path (priv->row_reference);
1981 while (!finished && gtk_tree_path_prev (path)) {
1985 gtk_tree_model_get_iter (priv->header_model, &iter, path);
1986 gtk_tree_model_get (priv->header_model, &iter,
1987 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
1991 if (msg_is_visible (header, priv->is_outbox)) {
1992 GtkTreeRowReference *row_reference;
1993 row_reference = gtk_tree_row_reference_new (priv->header_model, path);
1994 /* Read the message & show it */
1995 retval = message_reader (window, priv, header, row_reference);
1996 gtk_tree_row_reference_free (row_reference);
2000 g_object_unref (header);
2004 gtk_tree_path_free (path);
2009 view_msg_cb (ModestMailOperation *mail_op,
2016 ModestMsgViewWindow *self = NULL;
2017 ModestMsgViewWindowPrivate *priv = NULL;
2018 GtkTreeRowReference *row_reference = NULL;
2020 /* Unregister the header (it was registered before creating the mail operation) */
2021 modest_window_mgr_unregister_header (modest_runtime_get_window_mgr (), header);
2023 row_reference = (GtkTreeRowReference *) user_data;
2025 gtk_tree_row_reference_free (row_reference);
2026 self = (ModestMsgViewWindow *) modest_mail_operation_get_source (mail_op);
2028 /* Restore window title */
2029 update_window_title (self);
2030 g_object_unref (self);
2035 /* If there was any error */
2036 if (!modest_ui_actions_msg_retrieval_check (mail_op, header, msg)) {
2037 gtk_tree_row_reference_free (row_reference);
2038 self = (ModestMsgViewWindow *) modest_mail_operation_get_source (mail_op);
2040 /* Restore window title */
2041 update_window_title (self);
2042 g_object_unref (self);
2047 /* Get the window */
2048 self = (ModestMsgViewWindow *) modest_mail_operation_get_source (mail_op);
2049 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self));
2050 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
2052 /* Update the row reference */
2053 if (priv->row_reference != NULL) {
2054 gtk_tree_row_reference_free (priv->row_reference);
2055 priv->row_reference = gtk_tree_row_reference_copy (row_reference);
2056 if (priv->next_row_reference != NULL) {
2057 gtk_tree_row_reference_free (priv->next_row_reference);
2059 priv->next_row_reference = gtk_tree_row_reference_copy (priv->row_reference);
2060 select_next_valid_row (priv->header_model, &(priv->next_row_reference), TRUE, priv->is_outbox);
2063 /* Mark header as read */
2064 if (!(tny_header_get_flags (header) & TNY_HEADER_FLAG_SEEN))
2065 tny_header_set_flag (header, TNY_HEADER_FLAG_SEEN);
2067 /* Set new message */
2068 if (priv->msg_view != NULL && TNY_IS_MSG_VIEW (priv->msg_view)) {
2069 tny_msg_view_set_msg (TNY_MSG_VIEW (priv->msg_view), msg);
2070 modest_msg_view_window_update_priority (self);
2071 update_window_title (MODEST_MSG_VIEW_WINDOW (self));
2072 modest_msg_view_grab_focus (MODEST_MSG_VIEW (priv->msg_view));
2075 /* Set the new message uid of the window */
2076 if (priv->msg_uid) {
2077 g_free (priv->msg_uid);
2078 priv->msg_uid = modest_tny_folder_get_header_unique_id (header);
2081 /* Notify the observers */
2082 g_signal_emit (G_OBJECT (self), signals[MSG_CHANGED_SIGNAL],
2083 0, priv->header_model, priv->row_reference);
2086 g_object_unref (self);
2087 gtk_tree_row_reference_free (row_reference);
2091 modest_msg_view_window_get_folder_type (ModestMsgViewWindow *window)
2093 ModestMsgViewWindowPrivate *priv;
2095 TnyFolderType folder_type;
2097 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
2099 folder_type = TNY_FOLDER_TYPE_UNKNOWN;
2101 msg = tny_msg_view_get_msg (TNY_MSG_VIEW (priv->msg_view));
2105 folder = tny_msg_get_folder (msg);
2107 folder_type = modest_tny_folder_guess_folder_type (folder);
2108 g_object_unref (folder);
2110 g_object_unref (msg);
2118 modest_msg_view_window_update_priority (ModestMsgViewWindow *window)
2120 ModestMsgViewWindowPrivate *priv;
2121 TnyHeader *header = NULL;
2122 TnyHeaderFlags flags = 0;
2124 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
2126 if (priv->header_model && priv->row_reference) {
2128 GtkTreePath *path = NULL;
2130 path = gtk_tree_row_reference_get_path (priv->row_reference);
2131 g_return_if_fail (path != NULL);
2132 gtk_tree_model_get_iter (priv->header_model,
2134 gtk_tree_row_reference_get_path (priv->row_reference));
2136 gtk_tree_model_get (priv->header_model, &iter, TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
2138 gtk_tree_path_free (path);
2141 msg = tny_msg_view_get_msg (TNY_MSG_VIEW (priv->msg_view));
2143 header = tny_msg_get_header (msg);
2144 g_object_unref (msg);
2149 flags = tny_header_get_flags (header);
2150 g_object_unref(G_OBJECT(header));
2153 modest_msg_view_set_priority (MODEST_MSG_VIEW(priv->msg_view), flags);
2158 toolbar_resize (ModestMsgViewWindow *self)
2160 ModestMsgViewWindowPrivate *priv = NULL;
2161 ModestWindowPrivate *parent_priv = NULL;
2163 gint static_button_size;
2164 ModestWindowMgr *mgr;
2166 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self));
2167 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
2168 parent_priv = MODEST_WINDOW_GET_PRIVATE(self);
2170 mgr = modest_runtime_get_window_mgr ();
2171 static_button_size = modest_window_mgr_get_fullscreen_mode (mgr)?120:120;
2173 if (parent_priv->toolbar) {
2174 /* left size buttons */
2175 widget = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/ToolbarMessageReply");
2176 gtk_tool_item_set_expand (GTK_TOOL_ITEM (widget), FALSE);
2177 gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (widget), FALSE);
2178 gtk_widget_set_size_request (GTK_WIDGET (widget), static_button_size, -1);
2179 widget = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/ToolbarMessageMoveTo");
2180 gtk_tool_item_set_expand (GTK_TOOL_ITEM (widget), FALSE);
2181 gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (widget), FALSE);
2182 gtk_widget_set_size_request (GTK_WIDGET (widget), static_button_size, -1);
2183 widget = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/ToolbarDeleteMessage");
2184 gtk_tool_item_set_expand (GTK_TOOL_ITEM (widget), FALSE);
2185 gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (widget), FALSE);
2186 gtk_widget_set_size_request (GTK_WIDGET (widget), static_button_size, -1);
2187 widget = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/FindInMessage");
2188 gtk_tool_item_set_expand (GTK_TOOL_ITEM (widget), FALSE);
2189 gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (widget), FALSE);
2190 gtk_widget_set_size_request (GTK_WIDGET (widget), static_button_size, -1);
2192 gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (priv->next_toolitem), TRUE);
2193 gtk_tool_item_set_expand (GTK_TOOL_ITEM (priv->next_toolitem), TRUE);
2194 gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (priv->prev_toolitem), TRUE);
2195 gtk_tool_item_set_expand (GTK_TOOL_ITEM (priv->prev_toolitem), TRUE);
2200 modest_msg_view_window_show_toolbar (ModestWindow *self,
2201 gboolean show_toolbar)
2203 ModestMsgViewWindowPrivate *priv = NULL;
2204 ModestWindowPrivate *parent_priv;
2206 parent_priv = MODEST_WINDOW_GET_PRIVATE(self);
2207 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(self);
2209 /* Set optimized view status */
2210 priv->optimized_view = !show_toolbar;
2212 if (!parent_priv->toolbar) {
2213 parent_priv->toolbar = gtk_ui_manager_get_widget (parent_priv->ui_manager,
2215 gtk_toolbar_set_icon_size (GTK_TOOLBAR (parent_priv->toolbar), HILDON_ICON_SIZE_FINGER);
2216 gtk_widget_set_no_show_all (parent_priv->toolbar, TRUE);
2218 priv->next_toolitem = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/ToolbarMessageNext");
2219 priv->prev_toolitem = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/ToolbarMessageBack");
2220 toolbar_resize (MODEST_MSG_VIEW_WINDOW (self));
2223 hildon_window_add_toolbar (HILDON_WINDOW (self),
2224 GTK_TOOLBAR (parent_priv->toolbar));
2229 /* Quick hack: this prevents toolbar icons "dance" when progress bar show status is changed */
2230 /* TODO: resize mode migth be GTK_RESIZE_QUEUE, in order to avoid unneccesary shows */
2231 gtk_container_set_resize_mode (GTK_CONTAINER(parent_priv->toolbar), GTK_RESIZE_IMMEDIATE);
2233 gtk_widget_show (GTK_WIDGET (parent_priv->toolbar));
2234 if (modest_msg_view_window_transfer_mode_enabled (MODEST_MSG_VIEW_WINDOW (self)))
2235 set_progress_hint (MODEST_MSG_VIEW_WINDOW (self), TRUE);
2237 set_progress_hint (MODEST_MSG_VIEW_WINDOW (self), FALSE);
2240 gtk_widget_set_no_show_all (parent_priv->toolbar, TRUE);
2241 gtk_widget_hide (GTK_WIDGET (parent_priv->toolbar));
2246 modest_msg_view_window_clipboard_owner_change (GtkClipboard *clipboard,
2248 ModestMsgViewWindow *window)
2250 if (!GTK_WIDGET_VISIBLE (window))
2253 modest_window_check_dimming_rules_group (MODEST_WINDOW (window), MODEST_DIMMING_RULES_CLIPBOARD);
2257 modest_msg_view_window_transfer_mode_enabled (ModestMsgViewWindow *self)
2259 ModestMsgViewWindowPrivate *priv;
2261 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self), FALSE);
2262 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(self);
2264 return priv->progress_hint;
2268 observers_empty (ModestMsgViewWindow *self)
2271 ModestMsgViewWindowPrivate *priv;
2272 gboolean is_empty = TRUE;
2273 guint pending_ops = 0;
2275 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(self);
2276 tmp = priv->progress_widgets;
2278 /* Check all observers */
2279 while (tmp && is_empty) {
2280 pending_ops = modest_progress_object_num_pending_operations (MODEST_PROGRESS_OBJECT(tmp->data));
2281 is_empty = pending_ops == 0;
2283 tmp = g_slist_next(tmp);
2290 on_account_removed (TnyAccountStore *account_store,
2291 TnyAccount *account,
2294 /* Do nothing if it's a transport account, because we only
2295 show the messages of a store account */
2296 if (tny_account_get_account_type(account) == TNY_ACCOUNT_TYPE_STORE) {
2297 const gchar *parent_acc = NULL;
2298 const gchar *our_acc = NULL;
2300 our_acc = modest_window_get_active_account (MODEST_WINDOW (user_data));
2301 parent_acc = modest_tny_account_get_parent_modest_account_name_for_server_account (account);
2303 /* Close this window if I'm showing a message of the removed account */
2304 if (our_acc && parent_acc && strcmp (parent_acc, our_acc) == 0)
2305 modest_ui_actions_on_close_window (NULL, MODEST_WINDOW (user_data));
2310 on_mail_operation_started (ModestMailOperation *mail_op,
2313 ModestMsgViewWindow *self;
2314 ModestMailOperationTypeOperation op_type;
2316 ModestMsgViewWindowPrivate *priv;
2317 GObject *source = NULL;
2319 self = MODEST_MSG_VIEW_WINDOW (user_data);
2320 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
2321 op_type = modest_mail_operation_get_type_operation (mail_op);
2322 tmp = priv->progress_widgets;
2323 source = modest_mail_operation_get_source(mail_op);
2324 if (G_OBJECT (self) == source) {
2325 if (op_type == MODEST_MAIL_OPERATION_TYPE_RECEIVE) {
2326 set_toolbar_transfer_mode(self);
2328 modest_progress_object_add_operation (
2329 MODEST_PROGRESS_OBJECT (tmp->data),
2331 tmp = g_slist_next (tmp);
2335 g_object_unref (source);
2339 on_mail_operation_finished (ModestMailOperation *mail_op,
2342 ModestMsgViewWindow *self;
2343 ModestMailOperationTypeOperation op_type;
2345 ModestMsgViewWindowPrivate *priv;
2347 self = MODEST_MSG_VIEW_WINDOW (user_data);
2348 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
2349 op_type = modest_mail_operation_get_type_operation (mail_op);
2350 tmp = priv->progress_widgets;
2352 if (op_type == MODEST_MAIL_OPERATION_TYPE_RECEIVE) {
2354 modest_progress_object_remove_operation (MODEST_PROGRESS_OBJECT (tmp->data),
2356 tmp = g_slist_next (tmp);
2359 /* If no more operations are being observed, NORMAL mode is enabled again */
2360 if (observers_empty (self)) {
2361 set_progress_hint (self, FALSE);
2365 /* Update dimming rules. We have to do this right here
2366 and not in view_msg_cb because at that point the
2367 transfer mode is still enabled so the dimming rule
2368 won't let the user delete the message that has been
2369 readed for example */
2370 check_dimming_rules_after_change (self);
2375 on_queue_changed (ModestMailOperationQueue *queue,
2376 ModestMailOperation *mail_op,
2377 ModestMailOperationQueueNotification type,
2378 ModestMsgViewWindow *self)
2380 ModestMsgViewWindowPrivate *priv;
2382 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
2384 /* If this operations was created by another window, do nothing */
2385 if (!modest_mail_operation_is_mine (mail_op, G_OBJECT(self)))
2388 if (type == MODEST_MAIL_OPERATION_QUEUE_OPERATION_ADDED) {
2389 priv->sighandlers = modest_signal_mgr_connect (priv->sighandlers,
2391 "operation-started",
2392 G_CALLBACK (on_mail_operation_started),
2394 priv->sighandlers = modest_signal_mgr_connect (priv->sighandlers,
2396 "operation-finished",
2397 G_CALLBACK (on_mail_operation_finished),
2399 } else if (type == MODEST_MAIL_OPERATION_QUEUE_OPERATION_REMOVED) {
2400 priv->sighandlers = modest_signal_mgr_disconnect (priv->sighandlers,
2402 "operation-started");
2403 priv->sighandlers = modest_signal_mgr_disconnect (priv->sighandlers,
2405 "operation-finished");
2410 modest_msg_view_window_get_attachments (ModestMsgViewWindow *win)
2412 ModestMsgViewWindowPrivate *priv;
2413 TnyList *selected_attachments = NULL;
2415 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (win), NULL);
2416 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (win);
2418 /* In Hildon 2.2 as there's no selection we assume we have all attachments selected */
2419 selected_attachments = modest_msg_view_get_attachments (MODEST_MSG_VIEW (priv->msg_view));
2421 return selected_attachments;
2425 ModestMsgViewWindow *self;
2427 } DecodeAsyncHelper;
2430 on_decode_to_stream_async_handler (TnyMimePart *mime_part,
2436 DecodeAsyncHelper *helper = (DecodeAsyncHelper *) user_data;
2438 /* It could happen that the window was closed */
2439 if (GTK_WIDGET_VISIBLE (helper->self))
2440 set_progress_hint (helper->self, FALSE);
2442 if (cancelled || err) {
2444 gchar *msg = g_strdup_printf (_KR("cerm_device_memory_full"), "");
2445 modest_platform_information_banner (NULL, NULL, msg);
2451 /* make the file read-only */
2452 g_chmod(helper->file_path, 0444);
2454 /* Activate the file */
2455 modest_platform_activate_file (helper->file_path, tny_mime_part_get_content_type (mime_part));
2459 g_object_unref (helper->self);
2460 g_free (helper->file_path);
2461 g_slice_free (DecodeAsyncHelper, helper);
2465 modest_msg_view_window_view_attachment (ModestMsgViewWindow *window,
2466 TnyMimePart *mime_part)
2468 ModestMsgViewWindowPrivate *priv;
2469 const gchar *msg_uid;
2470 gchar *attachment_uid = NULL;
2471 gint attachment_index = 0;
2472 TnyList *attachments;
2474 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window));
2475 g_return_if_fail (TNY_IS_MIME_PART (mime_part) || (mime_part == NULL));
2476 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
2478 msg_uid = modest_msg_view_window_get_message_uid (MODEST_MSG_VIEW_WINDOW (window));
2479 attachments = modest_msg_view_get_attachments (MODEST_MSG_VIEW (priv->msg_view));
2480 attachment_index = modest_list_index (attachments, (GObject *) mime_part);
2481 g_object_unref (attachments);
2483 if (msg_uid && attachment_index >= 0) {
2484 attachment_uid = g_strdup_printf ("%s/%d", msg_uid, attachment_index);
2487 if (mime_part == NULL) {
2488 gboolean error = FALSE;
2489 TnyList *selected_attachments = modest_msg_view_get_selected_attachments (MODEST_MSG_VIEW (priv->msg_view));
2490 if (selected_attachments == NULL || tny_list_get_length (selected_attachments) == 0) {
2492 } else if (tny_list_get_length (selected_attachments) > 1) {
2493 hildon_banner_show_information (NULL, NULL, _("mcen_ib_unable_to_display_more"));
2497 iter = tny_list_create_iterator (selected_attachments);
2498 mime_part = (TnyMimePart *) tny_iterator_get_current (iter);
2499 g_object_unref (iter);
2501 if (selected_attachments)
2502 g_object_unref (selected_attachments);
2507 g_object_ref (mime_part);
2510 if (tny_mime_part_is_purged (mime_part))
2513 if (!modest_tny_mime_part_is_msg (mime_part)) {
2514 gchar *filepath = NULL;
2515 const gchar *att_filename = tny_mime_part_get_filename (mime_part);
2516 gboolean show_error_banner = FALSE;
2517 TnyFsStream *temp_stream = NULL;
2518 temp_stream = modest_utils_create_temp_stream (att_filename, attachment_uid,
2521 if (temp_stream != NULL) {
2522 DecodeAsyncHelper *helper;
2524 /* Activate progress hint */
2525 set_progress_hint (window, TRUE);
2527 helper = g_slice_new0 (DecodeAsyncHelper);
2528 helper->self = g_object_ref (window);
2529 helper->file_path = g_strdup (filepath);
2531 tny_mime_part_decode_to_stream_async (mime_part, TNY_STREAM (temp_stream),
2532 on_decode_to_stream_async_handler,
2535 g_object_unref (temp_stream);
2536 /* NOTE: files in the temporary area will be automatically
2537 * cleaned after some time if they are no longer in use */
2540 const gchar *content_type;
2541 /* the file may already exist but it isn't writable,
2542 * let's try to open it anyway */
2543 content_type = tny_mime_part_get_content_type (mime_part);
2544 modest_platform_activate_file (filepath, content_type);
2546 g_warning ("%s: modest_utils_create_temp_stream failed", __FUNCTION__);
2547 show_error_banner = TRUE;
2552 if (show_error_banner)
2553 modest_platform_information_banner (NULL, NULL, _("mail_ib_file_operation_failed"));
2555 /* message attachment */
2556 TnyHeader *header = NULL;
2557 ModestWindowMgr *mgr;
2558 ModestWindow *msg_win = NULL;
2561 header = tny_msg_get_header (TNY_MSG (mime_part));
2562 mgr = modest_runtime_get_window_mgr ();
2563 found = modest_window_mgr_find_registered_header (mgr, header, &msg_win);
2566 /* if it's found, but there is no msg_win, it's probably in the process of being created;
2567 * thus, we don't do anything */
2568 g_warning ("window for is already being created");
2570 /* it's not found, so create a new window for it */
2571 modest_window_mgr_register_header (mgr, header, attachment_uid); /* register the uid before building the window */
2572 gchar *account = g_strdup (modest_window_get_active_account (MODEST_WINDOW (window)));
2573 const gchar *mailbox = modest_window_get_active_mailbox (MODEST_WINDOW (window));
2575 account = modest_account_mgr_get_default_account (modest_runtime_get_account_mgr ());
2576 msg_win = modest_msg_view_window_new_for_attachment (TNY_MSG (mime_part), account,
2577 mailbox, attachment_uid);
2578 modest_window_set_zoom (MODEST_WINDOW (msg_win),
2579 modest_window_get_zoom (MODEST_WINDOW (window)));
2580 if (modest_window_mgr_register_window (mgr, msg_win, MODEST_WINDOW (window)))
2581 gtk_widget_show_all (GTK_WIDGET (msg_win));
2583 gtk_widget_destroy (GTK_WIDGET (msg_win));
2589 g_free (attachment_uid);
2591 g_object_unref (mime_part);
2603 GnomeVFSResult result;
2606 static void save_mime_part_info_free (SaveMimePartInfo *info, gboolean with_struct);
2607 static gboolean idle_save_mime_part_show_result (SaveMimePartInfo *info);
2608 static gpointer save_mime_part_to_file (SaveMimePartInfo *info);
2609 static void save_mime_parts_to_file_with_checks (SaveMimePartInfo *info);
2612 save_mime_part_info_free (SaveMimePartInfo *info, gboolean with_struct)
2616 for (node = info->pairs; node != NULL; node = g_list_next (node)) {
2617 SaveMimePartPair *pair = (SaveMimePartPair *) node->data;
2618 g_free (pair->filename);
2619 g_object_unref (pair->part);
2620 g_slice_free (SaveMimePartPair, pair);
2622 g_list_free (info->pairs);
2625 g_slice_free (SaveMimePartInfo, info);
2630 idle_save_mime_part_show_result (SaveMimePartInfo *info)
2632 if (info->pairs != NULL) {
2633 save_mime_part_to_file (info);
2635 /* This is a GDK lock because we are an idle callback and
2636 * hildon_banner_show_information is or does Gtk+ code */
2638 gdk_threads_enter (); /* CHECKED */
2639 save_mime_part_info_free (info, TRUE);
2640 if (info->result == GNOME_VFS_OK) {
2641 hildon_banner_show_information (NULL, NULL, _CS("sfil_ib_saved"));
2642 } else if (info->result == GNOME_VFS_ERROR_NO_SPACE) {
2643 gchar *msg = g_strdup_printf (_KR("cerm_device_memory_full"), "");
2644 modest_platform_information_banner (NULL, NULL, msg);
2647 hildon_banner_show_information (NULL, NULL, _("mail_ib_file_operation_failed"));
2649 gdk_threads_leave (); /* CHECKED */
2656 save_mime_part_to_file (SaveMimePartInfo *info)
2658 GnomeVFSHandle *handle;
2660 SaveMimePartPair *pair = (SaveMimePartPair *) info->pairs->data;
2662 info->result = gnome_vfs_create (&handle, pair->filename, GNOME_VFS_OPEN_WRITE, FALSE, 0644);
2663 if (info->result == GNOME_VFS_OK) {
2664 GError *error = NULL;
2665 stream = tny_vfs_stream_new (handle);
2666 if (tny_mime_part_decode_to_stream (pair->part, stream, &error) < 0) {
2667 g_warning ("modest: could not save attachment %s: %d (%s)\n", pair->filename, error?error->code:-1, error?error->message:"Unknown error");
2669 if ((error->domain == TNY_ERROR_DOMAIN) &&
2670 (error->code == TNY_IO_ERROR_WRITE) &&
2671 (errno == ENOSPC)) {
2672 info->result = GNOME_VFS_ERROR_NO_SPACE;
2674 info->result = GNOME_VFS_ERROR_IO;
2677 g_object_unref (G_OBJECT (stream));
2678 g_object_unref (pair->part);
2679 g_slice_free (SaveMimePartPair, pair);
2680 info->pairs = g_list_delete_link (info->pairs, info->pairs);
2682 g_warning ("modest: could not create save attachment %s: %s\n", pair->filename, gnome_vfs_result_to_string (info->result));
2683 save_mime_part_info_free (info, FALSE);
2686 g_idle_add ((GSourceFunc) idle_save_mime_part_show_result, info);
2691 save_mime_parts_to_file_with_checks (SaveMimePartInfo *info)
2693 gboolean is_ok = TRUE;
2694 gint replaced_files = 0;
2695 const GList *files = info->pairs;
2698 for (iter = files; (iter != NULL) && (replaced_files < 2); iter = g_list_next(iter)) {
2699 SaveMimePartPair *pair = iter->data;
2700 if (modest_utils_file_exists (pair->filename)) {
2704 if (replaced_files) {
2705 GtkWidget *confirm_overwrite_dialog;
2707 if (replaced_files == 1) {
2708 SaveMimePartPair *pair = files->data;
2709 const gchar *basename = strrchr (pair->filename, G_DIR_SEPARATOR) + 1;
2711 gchar *message = g_strdup_printf ("%s\n%s",
2712 _FM("docm_nc_replace_file"),
2713 (basename) ? basename : "");
2714 confirm_overwrite_dialog = hildon_note_new_confirmation (NULL, message);
2717 confirm_overwrite_dialog = hildon_note_new_confirmation (NULL,
2718 _FM("docm_nc_replace_multiple"));
2720 if (gtk_dialog_run (GTK_DIALOG (confirm_overwrite_dialog)) != GTK_RESPONSE_OK)
2723 gtk_widget_destroy (confirm_overwrite_dialog);
2727 save_mime_part_info_free (info, TRUE);
2729 g_thread_create ((GThreadFunc)save_mime_part_to_file, info, FALSE, NULL);
2735 save_attachments_response (GtkDialog *dialog,
2739 TnyList *mime_parts;
2741 GList *files_to_save = NULL;
2742 gchar *current_folder;
2744 mime_parts = TNY_LIST (user_data);
2746 if (arg1 != GTK_RESPONSE_OK)
2749 chooser_uri = gtk_file_chooser_get_uri (GTK_FILE_CHOOSER (dialog));
2750 current_folder = gtk_file_chooser_get_current_folder_uri (GTK_FILE_CHOOSER (dialog));
2751 if (current_folder && current_folder != '\0') {
2753 modest_conf_set_string (modest_runtime_get_conf (), MODEST_CONF_LATEST_SAVE_ATTACHMENT_PATH,
2754 current_folder,&err);
2756 g_debug ("Error storing latest used folder: %s", err->message);
2760 g_free (current_folder);
2762 if (!modest_utils_folder_writable (chooser_uri)) {
2763 hildon_banner_show_information
2764 (NULL, NULL, _FM("sfil_ib_readonly_location"));
2768 iter = tny_list_create_iterator (mime_parts);
2769 while (!tny_iterator_is_done (iter)) {
2770 TnyMimePart *mime_part = (TnyMimePart *) tny_iterator_get_current (iter);
2772 if ((modest_tny_mime_part_is_attachment_for_modest (mime_part)) &&
2773 !tny_mime_part_is_purged (mime_part) &&
2774 (tny_mime_part_get_filename (mime_part) != NULL)) {
2775 SaveMimePartPair *pair;
2777 pair = g_slice_new0 (SaveMimePartPair);
2779 if (tny_list_get_length (mime_parts) > 1) {
2781 gnome_vfs_escape_slashes (tny_mime_part_get_filename (mime_part));
2782 pair->filename = g_build_filename (chooser_uri, escaped, NULL);
2785 pair->filename = g_strdup (chooser_uri);
2787 pair->part = mime_part;
2788 files_to_save = g_list_prepend (files_to_save, pair);
2790 tny_iterator_next (iter);
2792 g_object_unref (iter);
2794 g_free (chooser_uri);
2796 if (files_to_save != NULL) {
2797 SaveMimePartInfo *info = g_slice_new0 (SaveMimePartInfo);
2798 info->pairs = files_to_save;
2799 info->result = TRUE;
2800 save_mime_parts_to_file_with_checks (info);
2804 /* Free and close the dialog */
2805 g_object_unref (mime_parts);
2806 gtk_widget_destroy (GTK_WIDGET (dialog));
2810 modest_msg_view_window_save_attachments (ModestMsgViewWindow *window,
2811 TnyList *mime_parts)
2813 ModestMsgViewWindowPrivate *priv;
2814 GtkWidget *save_dialog = NULL;
2815 gchar *conf_folder = NULL;
2816 gchar *filename = NULL;
2817 gchar *save_multiple_str = NULL;
2819 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window));
2820 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
2822 if (mime_parts == NULL) {
2823 /* In Hildon 2.2 save and delete operate over all the attachments as there's no
2824 * selection available */
2825 mime_parts = modest_msg_view_get_attachments (MODEST_MSG_VIEW (priv->msg_view));
2826 if (mime_parts && !modest_maemo_utils_select_attachments (GTK_WINDOW (window), mime_parts, FALSE)) {
2827 g_object_unref (mime_parts);
2830 if (mime_parts == NULL || tny_list_get_length (mime_parts) == 0) {
2832 g_object_unref (mime_parts);
2838 g_object_ref (mime_parts);
2841 /* prepare dialog */
2842 if (tny_list_get_length (mime_parts) == 1) {
2844 /* only one attachment selected */
2845 iter = tny_list_create_iterator (mime_parts);
2846 TnyMimePart *mime_part = (TnyMimePart *) tny_iterator_get_current (iter);
2847 g_object_unref (iter);
2848 if (!modest_tny_mime_part_is_msg (mime_part) &&
2849 modest_tny_mime_part_is_attachment_for_modest (mime_part) &&
2850 !tny_mime_part_is_purged (mime_part)) {
2851 filename = g_strdup (tny_mime_part_get_filename (mime_part));
2853 /* TODO: show any error? */
2854 g_warning ("Tried to save a non-file attachment");
2855 g_object_unref (mime_parts);
2858 g_object_unref (mime_part);
2860 save_multiple_str = g_strdup_printf (_FM("sfil_va_number_of_objects_attachments"),
2861 tny_list_get_length (mime_parts));
2864 save_dialog = hildon_file_chooser_dialog_new (GTK_WINDOW (window),
2865 GTK_FILE_CHOOSER_ACTION_SAVE);
2868 conf_folder = modest_conf_get_string (modest_runtime_get_conf (), MODEST_CONF_LATEST_SAVE_ATTACHMENT_PATH, NULL);
2869 if (conf_folder && conf_folder[0] != '\0') {
2870 gtk_file_chooser_set_current_folder_uri (GTK_FILE_CHOOSER (save_dialog), conf_folder);
2873 /* Set the default folder to images folder */
2874 docs_folder = g_build_filename (g_getenv (MYDOCS_ENV), DOCS_FOLDER, NULL);
2875 gtk_file_chooser_set_current_folder (GTK_FILE_CHOOSER (save_dialog), docs_folder);
2876 g_free (docs_folder);
2878 g_free (conf_folder);
2882 gtk_file_chooser_set_current_name (GTK_FILE_CHOOSER (save_dialog),
2887 /* if multiple, set multiple string */
2888 if (save_multiple_str) {
2889 g_object_set (G_OBJECT (save_dialog), "save-multiple", save_multiple_str, NULL);
2890 gtk_window_set_title (GTK_WINDOW (save_dialog), _FM("sfil_ti_save_objects_files"));
2893 /* We must run this asynchronously, because the hildon dialog
2894 performs a gtk_dialog_run by itself which leads to gdk
2896 g_signal_connect (save_dialog, "response",
2897 G_CALLBACK (save_attachments_response), mime_parts);
2899 gtk_widget_show_all (save_dialog);
2903 show_remove_attachment_information (gpointer userdata)
2905 ModestMsgViewWindow *window = (ModestMsgViewWindow *) userdata;
2906 ModestMsgViewWindowPrivate *priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
2908 /* We're outside the main lock */
2909 gdk_threads_enter ();
2911 if (priv->remove_attachment_banner != NULL) {
2912 gtk_widget_destroy (priv->remove_attachment_banner);
2913 g_object_unref (priv->remove_attachment_banner);
2916 priv->remove_attachment_banner = g_object_ref (
2917 hildon_banner_show_animation (NULL, NULL, _("mcen_me_inbox_remove_attachments")));
2919 gdk_threads_leave ();
2925 modest_msg_view_window_remove_attachments (ModestMsgViewWindow *window, gboolean get_all)
2927 ModestMsgViewWindowPrivate *priv;
2928 TnyList *mime_parts = NULL, *tmp;
2929 gchar *confirmation_message;
2935 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window));
2936 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
2938 /* In hildon 2.2 we ignore the get_all flag as we always get all attachments. This is
2939 * because we don't have selection
2941 mime_parts = modest_msg_view_get_attachments (MODEST_MSG_VIEW (priv->msg_view));
2943 /* Remove already purged messages from mime parts list. We use
2944 a copy of the list to remove items in the original one */
2945 tmp = tny_list_copy (mime_parts);
2946 iter = tny_list_create_iterator (tmp);
2947 while (!tny_iterator_is_done (iter)) {
2948 TnyMimePart *part = TNY_MIME_PART (tny_iterator_get_current (iter));
2949 if (tny_mime_part_is_purged (part))
2950 tny_list_remove (mime_parts, (GObject *) part);
2952 g_object_unref (part);
2953 tny_iterator_next (iter);
2955 g_object_unref (tmp);
2956 g_object_unref (iter);
2958 if (!modest_maemo_utils_select_attachments (GTK_WINDOW (window), mime_parts, TRUE) ||
2959 tny_list_get_length (mime_parts) == 0) {
2960 g_object_unref (mime_parts);
2964 n_attachments = tny_list_get_length (mime_parts);
2965 if (n_attachments == 1) {
2969 iter = tny_list_create_iterator (mime_parts);
2970 part = (TnyMimePart *) tny_iterator_get_current (iter);
2971 g_object_unref (iter);
2972 if (modest_tny_mime_part_is_msg (part)) {
2974 header = tny_msg_get_header (TNY_MSG (part));
2975 filename = tny_header_dup_subject (header);
2976 g_object_unref (header);
2977 if (filename == NULL)
2978 filename = g_strdup (_("mail_va_no_subject"));
2980 filename = g_strdup (tny_mime_part_get_filename (TNY_MIME_PART (part)));
2982 confirmation_message = g_strdup_printf (_("mcen_nc_purge_file_text"), filename);
2984 g_object_unref (part);
2986 confirmation_message = g_strdup_printf (ngettext("mcen_nc_purge_file_text",
2987 "mcen_nc_purge_files_text",
2988 n_attachments), n_attachments);
2990 response = modest_platform_run_confirmation_dialog (GTK_WINDOW (window),
2991 confirmation_message);
2992 g_free (confirmation_message);
2994 if (response != GTK_RESPONSE_OK) {
2995 g_object_unref (mime_parts);
2999 priv->purge_timeout = g_timeout_add (2000, show_remove_attachment_information, window);
3001 iter = tny_list_create_iterator (mime_parts);
3002 while (!tny_iterator_is_done (iter)) {
3005 part = (TnyMimePart *) tny_iterator_get_current (iter);
3006 tny_mime_part_set_purged (TNY_MIME_PART (part));
3007 g_object_unref (part);
3008 tny_iterator_next (iter);
3010 g_object_unref (iter);
3012 msg = tny_msg_view_get_msg (TNY_MSG_VIEW (priv->msg_view));
3013 tny_msg_view_clear (TNY_MSG_VIEW (priv->msg_view));
3014 tny_msg_rewrite_cache (msg);
3015 tny_msg_view_set_msg (TNY_MSG_VIEW (priv->msg_view), msg);
3016 g_object_unref (msg);
3018 g_object_unref (mime_parts);
3020 if (priv->purge_timeout > 0) {
3021 g_source_remove (priv->purge_timeout);
3022 priv->purge_timeout = 0;
3025 if (priv->remove_attachment_banner) {
3026 gtk_widget_destroy (priv->remove_attachment_banner);
3027 g_object_unref (priv->remove_attachment_banner);
3028 priv->remove_attachment_banner = NULL;
3034 update_window_title (ModestMsgViewWindow *window)
3036 ModestMsgViewWindowPrivate *priv;
3038 TnyHeader *header = NULL;
3039 gchar *subject = NULL;
3041 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
3043 /* Note that if the window is closed while we're retrieving
3044 the message, this widget could de deleted */
3045 if (!priv->msg_view)
3048 msg = tny_msg_view_get_msg (TNY_MSG_VIEW (priv->msg_view));
3051 header = tny_msg_get_header (msg);
3052 subject = tny_header_dup_subject (header);
3053 g_object_unref (header);
3054 g_object_unref (msg);
3057 if ((subject == NULL)||(subject[0] == '\0')) {
3059 subject = g_strdup (_("mail_va_no_subject"));
3062 gtk_window_set_title (GTK_WINDOW (window), subject);
3067 on_move_focus (GtkWidget *widget,
3068 GtkDirectionType direction,
3071 g_signal_stop_emission_by_name (G_OBJECT (widget), "move-focus");
3075 fetch_image_open_stream (TnyStreamCache *self, gint64 *expected_size, gchar *uri)
3077 GnomeVFSResult result;
3078 GnomeVFSHandle *handle = NULL;
3079 GnomeVFSFileInfo *info = NULL;
3082 result = gnome_vfs_open (&handle, uri, GNOME_VFS_OPEN_READ);
3083 if (result != GNOME_VFS_OK) {
3088 info = gnome_vfs_file_info_new ();
3089 result = gnome_vfs_get_file_info_from_handle (handle, info, GNOME_VFS_FILE_INFO_DEFAULT);
3090 if (result != GNOME_VFS_OK || ! (info->valid_fields & GNOME_VFS_FILE_INFO_FIELDS_SIZE)) {
3091 /* We put a "safe" default size for going to cache */
3092 *expected_size = (300*1024);
3094 *expected_size = info->size;
3096 gnome_vfs_file_info_unref (info);
3098 stream = tny_vfs_stream_new (handle);
3107 TnyStream *output_stream;
3108 GtkWidget *msg_view;
3113 on_fetch_image_idle_refresh_view (gpointer userdata)
3116 FetchImageData *fidata = (FetchImageData *) userdata;
3118 gdk_threads_enter ();
3119 if (GTK_WIDGET_DRAWABLE (fidata->msg_view)) {
3120 ModestMsgViewWindowPrivate *priv;
3122 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (fidata->window);
3123 priv->fetching_images--;
3124 gtk_widget_queue_draw (fidata->msg_view);
3125 update_progress_hint (MODEST_MSG_VIEW_WINDOW (fidata->window));
3127 gdk_threads_leave ();
3129 g_object_unref (fidata->msg_view);
3130 g_object_unref (fidata->window);
3131 g_slice_free (FetchImageData, fidata);
3136 on_fetch_image_thread (gpointer userdata)
3138 FetchImageData *fidata = (FetchImageData *) userdata;
3139 TnyStreamCache *cache;
3140 TnyStream *cache_stream;
3142 cache = modest_runtime_get_images_cache ();
3144 tny_stream_cache_get_stream (cache,
3146 (TnyStreamCacheOpenStreamFetcher) fetch_image_open_stream,
3147 (gpointer) fidata->uri);
3148 g_free (fidata->cache_id);
3149 g_free (fidata->uri);
3151 if (cache_stream != NULL) {
3154 while (G_LIKELY (!tny_stream_is_eos (cache_stream))) {
3157 nb_read = tny_stream_read (cache_stream, buffer, sizeof (buffer));
3158 if (G_UNLIKELY (nb_read < 0)) {
3160 } else if (G_LIKELY (nb_read > 0)) {
3161 gssize nb_written = 0;
3163 while (G_UNLIKELY (nb_written < nb_read)) {
3166 len = tny_stream_write (fidata->output_stream, buffer + nb_written,
3167 nb_read - nb_written);
3168 if (G_UNLIKELY (len < 0))
3174 tny_stream_close (cache_stream);
3175 g_object_unref (cache_stream);
3178 tny_stream_close (fidata->output_stream);
3179 g_object_unref (fidata->output_stream);
3181 g_idle_add (on_fetch_image_idle_refresh_view, fidata);
3187 on_fetch_image (ModestMsgView *msgview,
3190 ModestMsgViewWindow *window)
3192 const gchar *current_account;
3193 ModestMsgViewWindowPrivate *priv;
3194 FetchImageData *fidata;
3196 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
3198 current_account = modest_window_get_active_account (MODEST_WINDOW (window));
3200 fidata = g_slice_new0 (FetchImageData);
3201 fidata->msg_view = g_object_ref (msgview);
3202 fidata->window = g_object_ref (window);
3203 fidata->uri = g_strdup (uri);
3204 fidata->cache_id = modest_images_cache_get_id (current_account, uri);
3205 fidata->output_stream = g_object_ref (stream);
3207 priv->fetching_images++;
3208 if (g_thread_create (on_fetch_image_thread, fidata, FALSE, NULL) == NULL) {
3209 g_object_unref (fidata->output_stream);
3210 g_free (fidata->cache_id);
3211 g_free (fidata->uri);
3212 g_object_unref (fidata->msg_view);
3213 g_slice_free (FetchImageData, fidata);
3214 tny_stream_close (stream);
3215 priv->fetching_images--;
3216 update_progress_hint (window);
3219 update_progress_hint (window);
3225 setup_menu (ModestMsgViewWindow *self)
3227 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW(self));
3229 /* Settings menu buttons */
3230 modest_hildon2_window_add_to_menu (MODEST_HILDON2_WINDOW (self), _("mcen_me_inbox_replytoall"), NULL,
3231 APP_MENU_CALLBACK (modest_ui_actions_on_reply_all),
3232 MODEST_DIMMING_CALLBACK (modest_ui_dimming_rules_on_reply_msg));
3233 modest_hildon2_window_add_to_menu (MODEST_HILDON2_WINDOW (self), _("mcen_me_inbox_forward"), "<Control>d",
3234 APP_MENU_CALLBACK (modest_ui_actions_on_forward),
3235 MODEST_DIMMING_CALLBACK (modest_ui_dimming_rules_on_reply_msg));
3237 modest_hildon2_window_add_to_menu (MODEST_HILDON2_WINDOW (self), _("mcen_me_inbox_mark_as_read"), NULL,
3238 APP_MENU_CALLBACK (modest_ui_actions_on_mark_as_read),
3239 MODEST_DIMMING_CALLBACK (modest_ui_dimming_rules_on_mark_as_read_msg_in_view));
3240 modest_hildon2_window_add_to_menu (MODEST_HILDON2_WINDOW (self), _("mcen_me_inbox_mark_as_unread"), NULL,
3241 APP_MENU_CALLBACK (modest_ui_actions_on_mark_as_unread),
3242 MODEST_DIMMING_CALLBACK (modest_ui_dimming_rules_on_mark_as_unread_msg_in_view));
3244 modest_hildon2_window_add_to_menu (MODEST_HILDON2_WINDOW (self), _("mcen_me_viewer_save_attachments"), NULL,
3245 APP_MENU_CALLBACK (modest_ui_actions_save_attachments),
3246 MODEST_DIMMING_CALLBACK (modest_ui_dimming_rules_on_save_attachments));
3247 modest_hildon2_window_add_to_menu (MODEST_HILDON2_WINDOW (self), _("mcen_me_inbox_remove_attachments"), NULL,
3248 APP_MENU_CALLBACK (modest_ui_actions_remove_attachments),
3249 MODEST_DIMMING_CALLBACK (modest_ui_dimming_rules_on_remove_attachments));
3251 modest_hildon2_window_add_to_menu (MODEST_HILDON2_WINDOW (self), _("mcen_me_new_message"), "<Control>n",
3252 APP_MENU_CALLBACK (modest_ui_actions_on_new_msg),
3253 MODEST_DIMMING_CALLBACK (modest_ui_dimming_rules_on_new_msg));
3254 modest_hildon2_window_add_to_menu (MODEST_HILDON2_WINDOW (self), _("mcen_me_viewer_addtocontacts"), NULL,
3255 APP_MENU_CALLBACK (modest_ui_actions_add_to_contacts),
3256 MODEST_DIMMING_CALLBACK (modest_ui_dimming_rules_on_add_to_contacts));
3258 modest_hildon2_window_add_to_menu (MODEST_HILDON2_WINDOW (self), _("mail_bd_external_images"), NULL,
3259 APP_MENU_CALLBACK (modest_ui_actions_on_fetch_images),
3260 MODEST_DIMMING_CALLBACK (modest_ui_dimming_rules_on_fetch_images));
3261 modest_hildon2_window_add_to_menu (MODEST_HILDON2_WINDOW (self), _("mcen_ti_message_properties"), NULL,
3262 APP_MENU_CALLBACK (modest_ui_actions_on_details),
3263 MODEST_DIMMING_CALLBACK (modest_ui_dimming_rules_on_details));
3267 modest_msg_view_window_add_to_contacts (ModestMsgViewWindow *self)
3269 ModestMsgViewWindowPrivate *priv;
3270 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
3271 GSList *recipients = NULL;
3273 gboolean contacts_to_add = FALSE;
3275 msg = tny_msg_view_get_msg (TNY_MSG_VIEW (priv->msg_view));
3279 header = modest_msg_view_window_get_header (self);
3282 recipients = modest_tny_msg_header_get_all_recipients_list (header);
3283 g_object_unref (header);
3285 recipients = modest_tny_msg_get_all_recipients_list (msg);
3286 g_object_unref (msg);
3289 if (recipients != NULL) {
3290 GtkWidget *picker_dialog;
3291 GtkWidget *selector;
3293 gchar *selected = NULL;
3295 selector = hildon_touch_selector_new_text ();
3296 g_object_ref (selector);
3298 for (node = recipients; node != NULL; node = g_slist_next (node)) {
3299 if (!modest_address_book_has_address ((const gchar *) node->data)) {
3300 hildon_touch_selector_append_text (HILDON_TOUCH_SELECTOR (selector),
3301 (const gchar *) node->data);
3302 contacts_to_add = TRUE;
3306 if (contacts_to_add) {
3309 picker_dialog = hildon_picker_dialog_new (GTK_WINDOW (self));
3310 gtk_window_set_title (GTK_WINDOW (picker_dialog), _("mcen_me_viewer_addtocontacts"));
3312 hildon_picker_dialog_set_selector (HILDON_PICKER_DIALOG (picker_dialog),
3313 HILDON_TOUCH_SELECTOR (selector));
3315 picker_result = gtk_dialog_run (GTK_DIALOG (picker_dialog));
3317 if (picker_result == GTK_RESPONSE_OK) {
3318 selected = hildon_touch_selector_get_current_text (HILDON_TOUCH_SELECTOR (selector));
3320 gtk_widget_destroy (picker_dialog);
3323 modest_address_book_add_address (selected);
3328 g_object_unref (selector);
3333 if (recipients) {g_slist_foreach (recipients, (GFunc) g_free, NULL); g_slist_free (recipients);}
3337 _modest_msg_view_window_map_event (GtkWidget *widget,
3341 ModestMsgViewWindow *self = (ModestMsgViewWindow *) userdata;
3343 update_progress_hint (self);
3349 modest_msg_view_window_fetch_images (ModestMsgViewWindow *self)
3351 ModestMsgViewWindowPrivate *priv;
3352 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
3354 modest_msg_view_request_fetch_images (MODEST_MSG_VIEW (priv->msg_view));
3358 modest_msg_view_window_has_blocked_external_images (ModestMsgViewWindow *self)
3360 ModestMsgViewWindowPrivate *priv;
3361 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
3363 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self), FALSE);
3365 return modest_msg_view_has_blocked_external_images (MODEST_MSG_VIEW (priv->msg_view));
3369 modest_msg_view_window_reload (ModestMsgViewWindow *self)
3371 ModestMsgViewWindowPrivate *priv;
3374 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self));
3376 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
3377 header = modest_msg_view_window_get_header (MODEST_MSG_VIEW_WINDOW (self));
3379 if (!message_reader (self, priv, header, priv->row_reference)) {
3380 g_warning ("Shouldn't happen, trying to reload a message failed");
3383 g_object_unref (header);