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-msg-view-window-ui-dimming.h"
44 #include <modest-widget-memory.h>
45 #include <modest-progress-object.h>
46 #include <modest-runtime.h>
47 #include <modest-window-priv.h>
48 #include <modest-tny-folder.h>
49 #include <modest-text-utils.h>
50 #include <modest-account-mgr-helpers.h>
51 #include <modest-toolkit-factory.h>
52 #include <modest-scrollable.h>
53 #include <modest-isearch-toolbar.h>
54 #include "modest-defs.h"
55 #include "modest-ui-dimming-manager.h"
56 #include <gdk/gdkkeysyms.h>
57 #include <modest-tny-account.h>
58 #include <modest-mime-part-view.h>
59 #include <modest-isearch-view.h>
60 #include <modest-tny-mime-part.h>
61 #include <modest-address-book.h>
64 #include <glib/gstdio.h>
65 #include <modest-debug.h>
66 #include <modest-header-window.h>
67 #include <modest-account-protocol.h>
68 #include <modest-icon-names.h>
69 #include <modest-ui-actions.h>
70 #include <modest-hildon2-window-mgr.h>
71 #include <tny-camel-msg.h>
73 #define MYDOCS_ENV "MYDOCSDIR"
74 #define DOCS_FOLDER ".documents"
76 typedef struct _ModestMsgViewWindowPrivate ModestMsgViewWindowPrivate;
77 struct _ModestMsgViewWindowPrivate {
80 GtkWidget *main_scroll;
81 GtkWidget *isearch_toolbar;
84 /* Progress observers */
85 GSList *progress_widgets;
88 GtkWidget *prev_toolitem;
89 GtkWidget *next_toolitem;
90 gboolean progress_hint;
93 /* Optimized view enabled */
94 gboolean optimized_view;
96 /* Whether this was created via the *_new_for_search_result() function. */
97 gboolean is_search_result;
99 /* Whether the message is in outbox */
102 /* A reference to the @model of the header view
103 * to allow selecting previous/next messages,
104 * if the message is currently selected in the header view.
106 const gchar *header_folder_id;
107 GtkTreeModel *header_model;
108 GtkTreeRowReference *row_reference;
109 GtkTreeRowReference *next_row_reference;
111 gulong clipboard_change_handler;
112 gulong queue_change_handler;
113 gulong account_removed_handler;
114 gulong row_changed_handler;
115 gulong row_deleted_handler;
116 gulong row_inserted_handler;
117 gulong rows_reordered_handler;
120 GtkWidget *remove_attachment_banner;
123 TnyMimePart *other_body;
128 static void modest_msg_view_window_class_init (ModestMsgViewWindowClass *klass);
129 static void modest_msg_view_window_init (ModestMsgViewWindow *obj);
130 static void modest_header_view_observer_init (ModestHeaderViewObserverIface *iface_class);
131 static void modest_msg_view_window_finalize (GObject *obj);
132 static void modest_msg_view_window_show_isearch_toolbar (GtkWidget *obj, gpointer data);
133 static void modest_msg_view_window_isearch_toolbar_close (GtkWidget *widget,
134 ModestMsgViewWindow *obj);
135 static void modest_msg_view_window_isearch_toolbar_search (GtkWidget *widget,
136 ModestMsgViewWindow *obj);
137 static void modest_msg_view_window_toggle_isearch_toolbar (GtkWidget *obj,
139 static void modest_msg_view_window_disconnect_signals (ModestWindow *self);
141 static gdouble modest_msg_view_window_get_zoom (ModestWindow *window);
142 static void modest_msg_view_window_set_zoom (ModestWindow *window,
144 static gboolean modest_msg_view_window_zoom_minus (ModestWindow *window);
145 static gboolean modest_msg_view_window_zoom_plus (ModestWindow *window);
146 static gboolean modest_msg_view_window_key_event (GtkWidget *window,
149 static void modest_msg_view_window_update_priority (ModestMsgViewWindow *window);
151 static void modest_msg_view_window_show_toolbar (ModestWindow *window,
152 gboolean show_toolbar);
154 static void modest_msg_view_window_clipboard_owner_change (GtkClipboard *clipboard,
156 ModestMsgViewWindow *window);
158 static void modest_msg_view_window_on_row_changed (GtkTreeModel *header_model,
161 ModestMsgViewWindow *window);
163 static void modest_msg_view_window_on_row_deleted (GtkTreeModel *header_model,
165 ModestMsgViewWindow *window);
167 static void modest_msg_view_window_on_row_inserted (GtkTreeModel *header_model,
168 GtkTreePath *tree_path,
169 GtkTreeIter *tree_iter,
170 ModestMsgViewWindow *window);
172 static void modest_msg_view_window_on_row_reordered (GtkTreeModel *header_model,
176 ModestMsgViewWindow *window);
178 static void modest_msg_view_window_update_model_replaced (ModestHeaderViewObserver *window,
180 const gchar *tny_folder_id);
182 static void on_queue_changed (ModestMailOperationQueue *queue,
183 ModestMailOperation *mail_op,
184 ModestMailOperationQueueNotification type,
185 ModestMsgViewWindow *self);
187 static void on_account_removed (TnyAccountStore *account_store,
191 static void on_move_focus (GtkWidget *widget,
192 GtkDirectionType direction,
195 static void view_msg_cb (ModestMailOperation *mail_op,
202 static void set_progress_hint (ModestMsgViewWindow *self,
205 static void update_window_title (ModestMsgViewWindow *window);
207 static void init_window (ModestMsgViewWindow *obj);
209 static gboolean msg_is_visible (TnyHeader *header, gboolean check_outbox);
211 static void check_dimming_rules_after_change (ModestMsgViewWindow *window);
213 static gboolean on_fetch_image (ModestMsgView *msgview,
216 ModestMsgViewWindow *window);
218 static gboolean modest_msg_view_window_scroll_child (ModestMsgViewWindow *self,
219 GtkScrollType scroll_type,
222 static gboolean message_reader (ModestMsgViewWindow *window,
223 ModestMsgViewWindowPrivate *priv,
225 const gchar *msg_uid,
227 GtkTreeRowReference *row_reference);
229 static void setup_menu (ModestMsgViewWindow *self);
230 static gboolean _modest_msg_view_window_map_event (GtkWidget *widget,
233 static void update_branding (ModestMsgViewWindow *self);
234 static void sync_flags (ModestMsgViewWindow *self);
236 /* list my signals */
243 static const GtkActionEntry msg_view_toolbar_action_entries [] = {
246 { "ToolbarMessageReply", MODEST_STOCK_REPLY, N_("mcen_me_inbox_reply"), "<CTRL>R", NULL, G_CALLBACK (modest_ui_actions_on_reply) },
247 { "ToolbarMessageReplyAll", MODEST_STOCK_REPLY_ALL, N_("mcen_me_inbox_replytoall"), NULL, NULL, G_CALLBACK (modest_ui_actions_on_reply_all) },
248 { "ToolbarMessageForward", MODEST_STOCK_FORWARD, N_("mcen_me_inbox_forward"), NULL, NULL, G_CALLBACK (modest_ui_actions_on_forward) },
249 { "ToolbarDeleteMessage", MODEST_STOCK_DELETE, N_("qgn_toolb_gene_deletebutton"), NULL, NULL, G_CALLBACK (modest_ui_actions_on_delete_message_or_folder) },
250 { "ToolbarMessageBack", MODEST_TOOLBAR_ICON_PREV, N_("qgn_toolb_gene_back"), NULL, NULL, G_CALLBACK (modest_ui_actions_on_prev) },
251 { "ToolbarMessageNext", MODEST_TOOLBAR_ICON_NEXT, N_("qgn_toolb_gene_forward"), NULL, NULL, G_CALLBACK (modest_ui_actions_on_next) },
252 { "ToolbarDownloadExternalImages", MODEST_TOOLBAR_ICON_DOWNLOAD_IMAGES, N_("mail_bd_external_images"), NULL, NULL, G_CALLBACK (modest_ui_actions_on_fetch_images) },
255 static const GtkToggleActionEntry msg_view_toggle_action_entries [] = {
256 { "FindInMessage", MODEST_TOOLBAR_ICON_FIND, N_("qgn_toolb_gene_find"), "<CTRL>F", NULL, G_CALLBACK (modest_msg_view_window_toggle_isearch_toolbar), FALSE },
259 #define MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE((o), \
260 MODEST_TYPE_MSG_VIEW_WINDOW, \
261 ModestMsgViewWindowPrivate))
263 static GtkWindowClass *parent_class = NULL;
265 /* uncomment the following if you have defined any signals */
266 static guint signals[LAST_SIGNAL] = {0};
269 modest_msg_view_window_get_type (void)
271 static GType my_type = 0;
273 static const GTypeInfo my_info = {
274 sizeof(ModestMsgViewWindowClass),
275 NULL, /* base init */
276 NULL, /* base finalize */
277 (GClassInitFunc) modest_msg_view_window_class_init,
278 NULL, /* class finalize */
279 NULL, /* class data */
280 sizeof(ModestMsgViewWindow),
282 (GInstanceInitFunc) modest_msg_view_window_init,
285 #ifdef MODEST_TOOLKIT_GTK
286 my_type = g_type_register_static (MODEST_TYPE_WINDOW,
287 "ModestMsgViewWindow",
290 my_type = g_type_register_static (MODEST_TYPE_HILDON2_WINDOW,
291 "ModestMsgViewWindow",
295 static const GInterfaceInfo modest_header_view_observer_info =
297 (GInterfaceInitFunc) modest_header_view_observer_init,
298 NULL, /* interface_finalize */
299 NULL /* interface_data */
302 g_type_add_interface_static (my_type,
303 MODEST_TYPE_HEADER_VIEW_OBSERVER,
304 &modest_header_view_observer_info);
310 save_state (ModestWindow *self)
312 modest_widget_memory_save (modest_runtime_get_conf (),
314 MODEST_CONF_MSG_VIEW_WINDOW_KEY);
318 modest_msg_view_window_scroll_child (ModestMsgViewWindow *self,
319 GtkScrollType scroll_type,
323 ModestMsgViewWindowPrivate *priv;
326 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(self);
328 switch (scroll_type) {
329 case GTK_SCROLL_STEP_UP:
332 case GTK_SCROLL_STEP_DOWN:
335 case GTK_SCROLL_PAGE_UP:
338 case GTK_SCROLL_PAGE_DOWN:
341 case GTK_SCROLL_START:
352 modest_scrollable_scroll ((ModestScrollable *) priv->main_scroll, 0, step);
354 return (gboolean) step;
358 add_scroll_binding (GtkBindingSet *binding_set,
360 GtkScrollType scroll)
362 guint keypad_keyval = keyval - GDK_Left + GDK_KP_Left;
364 gtk_binding_entry_add_signal (binding_set, keyval, 0,
366 GTK_TYPE_SCROLL_TYPE, scroll,
367 G_TYPE_BOOLEAN, FALSE);
368 gtk_binding_entry_add_signal (binding_set, keypad_keyval, 0,
370 GTK_TYPE_SCROLL_TYPE, scroll,
371 G_TYPE_BOOLEAN, FALSE);
375 modest_msg_view_window_class_init (ModestMsgViewWindowClass *klass)
377 GObjectClass *gobject_class;
378 ModestWindowClass *modest_window_class;
379 GtkBindingSet *binding_set;
381 gobject_class = (GObjectClass*) klass;
382 modest_window_class = (ModestWindowClass *) klass;
384 parent_class = g_type_class_peek_parent (klass);
385 gobject_class->finalize = modest_msg_view_window_finalize;
387 modest_window_class->set_zoom_func = modest_msg_view_window_set_zoom;
388 modest_window_class->get_zoom_func = modest_msg_view_window_get_zoom;
389 modest_window_class->zoom_plus_func = modest_msg_view_window_zoom_plus;
390 modest_window_class->zoom_minus_func = modest_msg_view_window_zoom_minus;
391 modest_window_class->show_toolbar_func = modest_msg_view_window_show_toolbar;
392 modest_window_class->disconnect_signals_func = modest_msg_view_window_disconnect_signals;
394 modest_window_class->save_state_func = save_state;
396 klass->scroll_child = modest_msg_view_window_scroll_child;
398 signals[MSG_CHANGED_SIGNAL] =
399 g_signal_new ("msg-changed",
400 G_TYPE_FROM_CLASS (gobject_class),
402 G_STRUCT_OFFSET (ModestMsgViewWindowClass, msg_changed),
404 modest_marshal_VOID__POINTER_POINTER,
405 G_TYPE_NONE, 2, G_TYPE_POINTER, G_TYPE_POINTER);
407 signals[SCROLL_CHILD_SIGNAL] =
408 g_signal_new ("scroll-child",
409 G_TYPE_FROM_CLASS (gobject_class),
410 G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
411 G_STRUCT_OFFSET (ModestMsgViewWindowClass, scroll_child),
413 modest_marshal_BOOLEAN__ENUM_BOOLEAN,
414 G_TYPE_BOOLEAN, 2, GTK_TYPE_SCROLL_TYPE, G_TYPE_BOOLEAN);
416 binding_set = gtk_binding_set_by_class (klass);
417 add_scroll_binding (binding_set, GDK_Up, GTK_SCROLL_STEP_UP);
418 add_scroll_binding (binding_set, GDK_Down, GTK_SCROLL_STEP_DOWN);
419 add_scroll_binding (binding_set, GDK_Page_Up, GTK_SCROLL_PAGE_UP);
420 add_scroll_binding (binding_set, GDK_Page_Down, GTK_SCROLL_PAGE_DOWN);
421 add_scroll_binding (binding_set, GDK_Home, GTK_SCROLL_START);
422 add_scroll_binding (binding_set, GDK_End, GTK_SCROLL_END);
424 g_type_class_add_private (gobject_class, sizeof(ModestMsgViewWindowPrivate));
428 static void modest_header_view_observer_init(
429 ModestHeaderViewObserverIface *iface_class)
431 iface_class->update_func = modest_msg_view_window_update_model_replaced;
435 modest_msg_view_window_init (ModestMsgViewWindow *obj)
437 ModestMsgViewWindowPrivate *priv;
438 ModestWindowPrivate *parent_priv = NULL;
439 GtkActionGroup *action_group = NULL;
440 GError *error = NULL;
442 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(obj);
443 parent_priv = MODEST_WINDOW_GET_PRIVATE(obj);
444 parent_priv->ui_manager = gtk_ui_manager_new();
446 action_group = gtk_action_group_new ("ModestMsgViewWindowActions");
447 gtk_action_group_set_translation_domain (action_group, GETTEXT_PACKAGE);
449 /* Add common actions */
450 gtk_action_group_add_actions (action_group,
451 msg_view_toolbar_action_entries,
452 G_N_ELEMENTS (msg_view_toolbar_action_entries),
454 gtk_action_group_add_toggle_actions (action_group,
455 msg_view_toggle_action_entries,
456 G_N_ELEMENTS (msg_view_toggle_action_entries),
459 gtk_ui_manager_insert_action_group (parent_priv->ui_manager, action_group, 0);
460 g_object_unref (action_group);
462 /* Load the UI definition */
463 gtk_ui_manager_add_ui_from_file (parent_priv->ui_manager, MODEST_UIDIR "modest-msg-view-window-ui.xml",
466 g_printerr ("modest: could not merge modest-msg-view-window-ui.xml: %s\n", error->message);
467 g_error_free (error);
472 /* Add accelerators */
473 gtk_window_add_accel_group (GTK_WINDOW (obj),
474 gtk_ui_manager_get_accel_group (parent_priv->ui_manager));
476 priv->is_search_result = FALSE;
477 priv->is_outbox = FALSE;
479 priv->msg_view = NULL;
480 priv->header_model = NULL;
481 priv->header_folder_id = NULL;
482 priv->clipboard_change_handler = 0;
483 priv->queue_change_handler = 0;
484 priv->account_removed_handler = 0;
485 priv->row_changed_handler = 0;
486 priv->row_deleted_handler = 0;
487 priv->row_inserted_handler = 0;
488 priv->rows_reordered_handler = 0;
489 priv->progress_hint = FALSE;
490 priv->fetching_images = 0;
492 priv->optimized_view = FALSE;
493 priv->purge_timeout = 0;
494 priv->remove_attachment_banner = NULL;
495 priv->msg_uid = NULL;
496 priv->other_body = NULL;
498 priv->sighandlers = NULL;
501 init_window (MODEST_MSG_VIEW_WINDOW(obj));
506 update_progress_hint (ModestMsgViewWindow *self)
508 ModestMsgViewWindowPrivate *priv;
509 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(self);
511 if (GTK_WIDGET_VISIBLE (self)) {
512 modest_window_show_progress (MODEST_WINDOW (self),
513 (priv->progress_hint || (priv->fetching_images > 0))?1:0);
518 set_progress_hint (ModestMsgViewWindow *self,
521 ModestWindowPrivate *parent_priv;
522 ModestMsgViewWindowPrivate *priv;
524 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self));
526 parent_priv = MODEST_WINDOW_GET_PRIVATE(self);
527 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(self);
529 /* Sets current progress hint */
530 priv->progress_hint = enabled;
532 update_progress_hint (self);
538 init_window (ModestMsgViewWindow *obj)
540 GtkWidget *main_vbox;
541 ModestMsgViewWindowPrivate *priv;
543 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(obj);
545 priv->msg_view = GTK_WIDGET (tny_platform_factory_new_msg_view (modest_tny_platform_factory_get_instance ()));
546 modest_msg_view_set_shadow_type (MODEST_MSG_VIEW (priv->msg_view), GTK_SHADOW_NONE);
547 main_vbox = gtk_vbox_new (FALSE, 6);
549 priv->main_scroll = modest_toolkit_factory_create_scrollable (modest_runtime_get_toolkit_factory ());
550 modest_scrollable_set_horizontal_policy (MODEST_SCROLLABLE (priv->main_scroll), GTK_POLICY_AUTOMATIC);
551 g_object_set (G_OBJECT (priv->main_scroll),
552 "movement-mode", MODEST_MOVEMENT_MODE_BOTH,
553 "horizontal-max-overshoot", 0,
555 gtk_container_add (GTK_CONTAINER (priv->main_scroll), priv->msg_view);
556 gtk_box_pack_start (GTK_BOX(main_vbox), priv->main_scroll, TRUE, TRUE, 0);
557 gtk_container_add (GTK_CONTAINER(obj), main_vbox);
559 /* NULL-ize fields if the window is destroyed */
560 g_signal_connect (priv->msg_view, "destroy", G_CALLBACK (gtk_widget_destroyed), &(priv->msg_view));
562 gtk_widget_show_all (GTK_WIDGET(main_vbox));
566 modest_msg_view_window_disconnect_signals (ModestWindow *self)
568 ModestMsgViewWindowPrivate *priv;
569 GtkWidget *header_view = NULL;
570 GtkWindow *parent_window = NULL;
572 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
574 if (gtk_clipboard_get (GDK_SELECTION_PRIMARY) &&
575 g_signal_handler_is_connected (gtk_clipboard_get (GDK_SELECTION_PRIMARY),
576 priv->clipboard_change_handler))
577 g_signal_handler_disconnect (gtk_clipboard_get (GDK_SELECTION_PRIMARY),
578 priv->clipboard_change_handler);
580 if (g_signal_handler_is_connected (G_OBJECT (modest_runtime_get_mail_operation_queue ()),
581 priv->queue_change_handler))
582 g_signal_handler_disconnect (G_OBJECT (modest_runtime_get_mail_operation_queue ()),
583 priv->queue_change_handler);
585 if (g_signal_handler_is_connected (G_OBJECT (modest_runtime_get_account_store ()),
586 priv->account_removed_handler))
587 g_signal_handler_disconnect (G_OBJECT (modest_runtime_get_account_store ()),
588 priv->account_removed_handler);
590 if (priv->header_model) {
591 if (g_signal_handler_is_connected(G_OBJECT (priv->header_model),
592 priv->row_changed_handler))
593 g_signal_handler_disconnect(G_OBJECT (priv->header_model),
594 priv->row_changed_handler);
596 if (g_signal_handler_is_connected(G_OBJECT (priv->header_model),
597 priv->row_deleted_handler))
598 g_signal_handler_disconnect(G_OBJECT (priv->header_model),
599 priv->row_deleted_handler);
601 if (g_signal_handler_is_connected(G_OBJECT (priv->header_model),
602 priv->row_inserted_handler))
603 g_signal_handler_disconnect(G_OBJECT (priv->header_model),
604 priv->row_inserted_handler);
606 if (g_signal_handler_is_connected(G_OBJECT (priv->header_model),
607 priv->rows_reordered_handler))
608 g_signal_handler_disconnect(G_OBJECT (priv->header_model),
609 priv->rows_reordered_handler);
612 modest_signal_mgr_disconnect_all_and_destroy (priv->sighandlers);
613 priv->sighandlers = NULL;
615 parent_window = gtk_window_get_transient_for (GTK_WINDOW (self));
616 if (parent_window && MODEST_IS_HEADER_WINDOW (parent_window)) {
617 header_view = GTK_WIDGET (modest_header_window_get_header_view (MODEST_HEADER_WINDOW (parent_window)));
619 modest_header_view_remove_observer(MODEST_HEADER_VIEW (header_view),
620 MODEST_HEADER_VIEW_OBSERVER(self));
626 modest_msg_view_window_finalize (GObject *obj)
628 ModestMsgViewWindowPrivate *priv;
630 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (obj);
632 /* Sanity check: shouldn't be needed, the window mgr should
633 call this function before */
634 modest_msg_view_window_disconnect_signals (MODEST_WINDOW (obj));
636 if (priv->other_body != NULL) {
637 g_object_unref (priv->other_body);
638 priv->other_body = NULL;
641 if (priv->header_model != NULL) {
642 g_object_unref (priv->header_model);
643 priv->header_model = NULL;
646 if (priv->remove_attachment_banner) {
647 gtk_widget_destroy (priv->remove_attachment_banner);
648 g_object_unref (priv->remove_attachment_banner);
649 priv->remove_attachment_banner = NULL;
652 if (priv->purge_timeout > 0) {
653 g_source_remove (priv->purge_timeout);
654 priv->purge_timeout = 0;
657 if (priv->row_reference) {
658 gtk_tree_row_reference_free (priv->row_reference);
659 priv->row_reference = NULL;
662 if (priv->next_row_reference) {
663 gtk_tree_row_reference_free (priv->next_row_reference);
664 priv->next_row_reference = NULL;
668 g_free (priv->msg_uid);
669 priv->msg_uid = NULL;
672 G_OBJECT_CLASS(parent_class)->finalize (obj);
676 select_next_valid_row (GtkTreeModel *model,
677 GtkTreeRowReference **row_reference,
681 GtkTreeIter tmp_iter;
683 GtkTreePath *next = NULL;
684 gboolean retval = FALSE, finished;
686 g_return_val_if_fail (gtk_tree_row_reference_valid (*row_reference), FALSE);
688 path = gtk_tree_row_reference_get_path (*row_reference);
689 gtk_tree_model_get_iter (model, &tmp_iter, path);
690 gtk_tree_row_reference_free (*row_reference);
691 *row_reference = NULL;
695 TnyHeader *header = NULL;
697 if (gtk_tree_model_iter_next (model, &tmp_iter)) {
698 gtk_tree_model_get (model, &tmp_iter,
699 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
703 if (msg_is_visible (header, is_outbox)) {
704 next = gtk_tree_model_get_path (model, &tmp_iter);
705 *row_reference = gtk_tree_row_reference_new (model, next);
706 gtk_tree_path_free (next);
710 g_object_unref (header);
713 } else if (cycle && gtk_tree_model_get_iter_first (model, &tmp_iter)) {
714 next = gtk_tree_model_get_path (model, &tmp_iter);
716 /* Ensure that we are not selecting the same */
717 if (gtk_tree_path_compare (path, next) != 0) {
718 gtk_tree_model_get (model, &tmp_iter,
719 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
722 if (msg_is_visible (header, is_outbox)) {
723 *row_reference = gtk_tree_row_reference_new (model, next);
727 g_object_unref (header);
731 /* If we ended up in the same message
732 then there is no valid next
736 gtk_tree_path_free (next);
738 /* If there are no more messages and we don't
739 want to start again in the first one then
740 there is no valid next message */
746 gtk_tree_path_free (path);
751 /* TODO: This should be in _init(), with the parameters as properties. */
753 modest_msg_view_window_construct (ModestMsgViewWindow *self,
754 const gchar *modest_account_name,
755 const gchar *mailbox,
756 const gchar *msg_uid)
759 ModestMsgViewWindowPrivate *priv = NULL;
760 ModestWindowPrivate *parent_priv = NULL;
761 ModestDimmingRulesGroup *toolbar_rules_group = NULL;
762 ModestDimmingRulesGroup *clipboard_rules_group = NULL;
764 obj = G_OBJECT (self);
765 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(obj);
766 parent_priv = MODEST_WINDOW_GET_PRIVATE(obj);
768 priv->msg_uid = g_strdup (msg_uid);
771 parent_priv->menubar = NULL;
773 toolbar_rules_group = modest_dimming_rules_group_new (MODEST_DIMMING_RULES_TOOLBAR, TRUE);
774 clipboard_rules_group = modest_dimming_rules_group_new (MODEST_DIMMING_RULES_CLIPBOARD, FALSE);
777 /* Add common dimming rules */
778 modest_dimming_rules_group_add_rules (toolbar_rules_group,
779 modest_msg_view_toolbar_dimming_entries,
780 G_N_ELEMENTS (modest_msg_view_toolbar_dimming_entries),
781 MODEST_WINDOW (self));
782 modest_dimming_rules_group_add_rules (clipboard_rules_group,
783 modest_msg_view_clipboard_dimming_entries,
784 G_N_ELEMENTS (modest_msg_view_clipboard_dimming_entries),
785 MODEST_WINDOW (self));
787 /* Insert dimming rules group for this window */
788 modest_ui_dimming_manager_insert_rules_group (parent_priv->ui_dimming_manager, toolbar_rules_group);
789 modest_ui_dimming_manager_insert_rules_group (parent_priv->ui_dimming_manager, clipboard_rules_group);
790 g_object_unref (toolbar_rules_group);
791 g_object_unref (clipboard_rules_group);
793 /* g_signal_connect (G_OBJECT(obj), "delete-event", G_CALLBACK(on_delete_event), obj); */
795 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);
796 g_signal_connect (G_OBJECT(priv->msg_view), "activate_link",
797 G_CALLBACK (modest_ui_actions_on_msg_link_clicked), obj);
798 g_signal_connect (G_OBJECT(priv->msg_view), "link_hover",
799 G_CALLBACK (modest_ui_actions_on_msg_link_hover), obj);
800 g_signal_connect (G_OBJECT(priv->msg_view), "attachment_clicked",
801 G_CALLBACK (modest_ui_actions_on_msg_attachment_clicked), obj);
802 g_signal_connect (G_OBJECT(priv->msg_view), "recpt_activated",
803 G_CALLBACK (modest_ui_actions_on_msg_recpt_activated), obj);
804 g_signal_connect (G_OBJECT(priv->msg_view), "show_details",
805 G_CALLBACK (modest_ui_actions_on_details), obj);
806 g_signal_connect (G_OBJECT(priv->msg_view), "link_contextual",
807 G_CALLBACK (modest_ui_actions_on_msg_link_contextual), obj);
808 g_signal_connect (G_OBJECT(priv->msg_view), "limit_error",
809 G_CALLBACK (modest_ui_actions_on_limit_error), obj);
810 g_signal_connect (G_OBJECT (priv->msg_view), "fetch_image",
811 G_CALLBACK (on_fetch_image), obj);
813 g_signal_connect (G_OBJECT (obj), "key-release-event",
814 G_CALLBACK (modest_msg_view_window_key_event),
817 g_signal_connect (G_OBJECT (obj), "key-press-event",
818 G_CALLBACK (modest_msg_view_window_key_event),
821 g_signal_connect (G_OBJECT (obj), "move-focus",
822 G_CALLBACK (on_move_focus), obj);
824 g_signal_connect (G_OBJECT (obj), "map-event",
825 G_CALLBACK (_modest_msg_view_window_map_event),
828 /* Mail Operation Queue */
829 priv->queue_change_handler = g_signal_connect (G_OBJECT (modest_runtime_get_mail_operation_queue ()),
831 G_CALLBACK (on_queue_changed),
834 /* Account manager */
835 priv->account_removed_handler = g_signal_connect (G_OBJECT (modest_runtime_get_account_store ()),
837 G_CALLBACK(on_account_removed),
840 modest_window_set_active_account (MODEST_WINDOW(obj), modest_account_name);
841 modest_window_set_active_mailbox (MODEST_WINDOW(obj), mailbox);
843 /* First add out toolbar ... */
844 modest_msg_view_window_show_toolbar (MODEST_WINDOW (obj), TRUE);
846 priv->isearch_toolbar = modest_toolkit_factory_create_isearch_toolbar (modest_runtime_get_toolkit_factory (),
848 modest_window_add_toolbar (MODEST_WINDOW (obj), GTK_TOOLBAR (priv->isearch_toolbar));
849 gtk_widget_set_no_show_all (priv->isearch_toolbar, TRUE);
850 g_signal_connect (G_OBJECT (priv->isearch_toolbar), "isearch-close",
851 G_CALLBACK (modest_msg_view_window_isearch_toolbar_close), obj);
852 g_signal_connect (G_OBJECT (priv->isearch_toolbar), "isearch-search",
853 G_CALLBACK (modest_msg_view_window_isearch_toolbar_search), obj);
854 priv->last_search = NULL;
856 /* Init the clipboard actions dim status */
857 modest_msg_view_grab_focus(MODEST_MSG_VIEW (priv->msg_view));
859 update_window_title (MODEST_MSG_VIEW_WINDOW (obj));
864 /* FIXME: parameter checks */
866 modest_msg_view_window_new_with_header_model (TnyMsg *msg,
867 const gchar *modest_account_name,
868 const gchar *mailbox,
869 const gchar *msg_uid,
871 GtkTreeRowReference *row_reference)
873 ModestMsgViewWindow *window = NULL;
874 ModestMsgViewWindowPrivate *priv = NULL;
875 TnyFolder *header_folder = NULL;
876 ModestHeaderView *header_view = NULL;
877 ModestWindowMgr *mgr = NULL;
880 modest_tny_mime_part_to_string (TNY_MIME_PART (msg), 0);
883 mgr = modest_runtime_get_window_mgr ();
884 window = MODEST_MSG_VIEW_WINDOW (modest_window_mgr_get_msg_view_window (mgr));
885 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), NULL);
887 modest_msg_view_window_construct (window, modest_account_name, mailbox, msg_uid);
889 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
891 /* Remember the message list's TreeModel so we can detect changes
892 * and change the list selection when necessary: */
893 header_folder = modest_header_view_get_folder (header_view);
895 priv->is_outbox = (modest_tny_folder_guess_folder_type (header_folder) ==
896 TNY_FOLDER_TYPE_OUTBOX);
897 priv->header_folder_id = tny_folder_get_id (header_folder);
898 g_object_unref(header_folder);
901 /* Setup row references and connect signals */
902 priv->header_model = g_object_ref (model);
904 if (row_reference && gtk_tree_row_reference_valid (row_reference)) {
905 priv->row_reference = gtk_tree_row_reference_copy (row_reference);
906 priv->next_row_reference = gtk_tree_row_reference_copy (row_reference);
907 select_next_valid_row (model, &(priv->next_row_reference), TRUE, priv->is_outbox);
909 priv->row_reference = NULL;
910 priv->next_row_reference = NULL;
913 /* Connect signals */
914 priv->row_changed_handler =
915 g_signal_connect (GTK_TREE_MODEL(model), "row-changed",
916 G_CALLBACK(modest_msg_view_window_on_row_changed),
918 priv->row_deleted_handler =
919 g_signal_connect (GTK_TREE_MODEL(model), "row-deleted",
920 G_CALLBACK(modest_msg_view_window_on_row_deleted),
922 priv->row_inserted_handler =
923 g_signal_connect (GTK_TREE_MODEL(model), "row-inserted",
924 G_CALLBACK(modest_msg_view_window_on_row_inserted),
926 priv->rows_reordered_handler =
927 g_signal_connect(GTK_TREE_MODEL(model), "rows-reordered",
928 G_CALLBACK(modest_msg_view_window_on_row_reordered),
931 if (header_view != NULL){
932 modest_header_view_add_observer(header_view,
933 MODEST_HEADER_VIEW_OBSERVER(window));
936 tny_msg_view_set_msg (TNY_MSG_VIEW (priv->msg_view), msg);
937 update_window_title (MODEST_MSG_VIEW_WINDOW (window));
938 update_branding (MODEST_MSG_VIEW_WINDOW (window));
940 /* gtk_widget_show_all (GTK_WIDGET (window)); */
941 modest_msg_view_window_update_priority (window);
942 /* Check dimming rules */
943 modest_ui_actions_check_toolbar_dimming_rules (MODEST_WINDOW (window));
944 modest_ui_actions_check_menu_dimming_rules (MODEST_WINDOW (window));
945 modest_window_check_dimming_rules_group (MODEST_WINDOW (window), MODEST_DIMMING_RULES_CLIPBOARD);
947 return MODEST_WINDOW(window);
951 modest_msg_view_window_new_from_uid (const gchar *modest_account_name,
952 const gchar *mailbox,
953 const gchar *msg_uid)
955 ModestMsgViewWindow *window = NULL;
956 ModestMsgViewWindowPrivate *priv = NULL;
957 ModestWindowMgr *mgr = NULL;
959 TnyAccount *account = NULL;
961 mgr = modest_runtime_get_window_mgr ();
962 window = MODEST_MSG_VIEW_WINDOW (modest_window_mgr_get_msg_view_window (mgr));
963 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), NULL);
965 modest_msg_view_window_construct (window, modest_account_name, mailbox, msg_uid);
967 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
969 is_merge = g_str_has_prefix (msg_uid, "merge:");
971 /* Get the account */
973 account = tny_account_store_find_account (TNY_ACCOUNT_STORE (modest_runtime_get_account_store ()),
976 if (is_merge || account) {
977 TnyFolder *folder = NULL;
979 /* Try to get the message, if it's already downloaded
980 we don't need to connect */
982 folder = modest_tny_folder_store_find_folder_from_uri (TNY_FOLDER_STORE (account), msg_uid);
984 ModestTnyAccountStore *account_store;
985 ModestTnyLocalFoldersAccount *local_folders_account;
987 account_store = modest_runtime_get_account_store ();
988 local_folders_account = MODEST_TNY_LOCAL_FOLDERS_ACCOUNT (
989 modest_tny_account_store_get_local_folders_account (account_store));
990 folder = modest_tny_local_folders_account_get_merged_outbox (local_folders_account);
991 g_object_unref (local_folders_account);
995 gboolean device_online;
997 device = modest_runtime_get_device();
998 device_online = tny_device_is_online (device);
1000 message_reader (window, priv, NULL, msg_uid, folder, NULL);
1002 TnyMsg *msg = tny_folder_find_msg (folder, msg_uid, NULL);
1004 tny_msg_view_set_msg (TNY_MSG_VIEW (priv->msg_view), msg);
1005 update_window_title (MODEST_MSG_VIEW_WINDOW (window));
1006 update_branding (MODEST_MSG_VIEW_WINDOW (window));
1007 g_object_unref (msg);
1008 /* Sync flags to server */
1009 sync_flags (MODEST_MSG_VIEW_WINDOW (window));
1011 message_reader (window, priv, NULL, msg_uid, folder, NULL);
1014 g_object_unref (folder);
1019 /* Check dimming rules */
1020 modest_ui_actions_check_toolbar_dimming_rules (MODEST_WINDOW (window));
1021 modest_ui_actions_check_menu_dimming_rules (MODEST_WINDOW (window));
1022 modest_window_check_dimming_rules_group (MODEST_WINDOW (window), MODEST_DIMMING_RULES_CLIPBOARD);
1024 return MODEST_WINDOW(window);
1028 modest_msg_view_window_new_from_header_view (ModestHeaderView *header_view,
1029 const gchar *modest_account_name,
1030 const gchar *mailbox,
1031 const gchar *msg_uid,
1032 GtkTreeRowReference *row_reference)
1034 ModestMsgViewWindow *window = NULL;
1035 ModestMsgViewWindowPrivate *priv = NULL;
1036 TnyFolder *header_folder = NULL;
1037 ModestWindowMgr *mgr = NULL;
1041 mgr = modest_runtime_get_window_mgr ();
1042 window = MODEST_MSG_VIEW_WINDOW (modest_window_mgr_get_msg_view_window (mgr));
1043 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), NULL);
1045 modest_msg_view_window_construct (window, modest_account_name, mailbox, msg_uid);
1047 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1049 /* Remember the message list's TreeModel so we can detect changes
1050 * and change the list selection when necessary: */
1052 if (header_view != NULL){
1053 header_folder = modest_header_view_get_folder(header_view);
1054 /* This could happen if the header folder was
1055 unseleted before opening this msg window (for
1056 example if the user selects an account in the
1057 folder view of the main window */
1058 if (header_folder) {
1059 priv->is_outbox = (modest_tny_folder_guess_folder_type (header_folder) ==
1060 TNY_FOLDER_TYPE_OUTBOX);
1061 priv->header_folder_id = tny_folder_get_id(header_folder);
1062 g_object_unref(header_folder);
1066 /* Setup row references and connect signals */
1067 priv->header_model = gtk_tree_view_get_model (GTK_TREE_VIEW (header_view));
1068 g_object_ref (priv->header_model);
1070 if (row_reference && gtk_tree_row_reference_valid (row_reference)) {
1071 priv->row_reference = gtk_tree_row_reference_copy (row_reference);
1072 priv->next_row_reference = gtk_tree_row_reference_copy (row_reference);
1073 select_next_valid_row (priv->header_model, &(priv->next_row_reference), TRUE, priv->is_outbox);
1075 priv->row_reference = NULL;
1076 priv->next_row_reference = NULL;
1079 /* Connect signals */
1080 priv->row_changed_handler =
1081 g_signal_connect (GTK_TREE_MODEL(priv->header_model), "row-changed",
1082 G_CALLBACK(modest_msg_view_window_on_row_changed),
1084 priv->row_deleted_handler =
1085 g_signal_connect (GTK_TREE_MODEL(priv->header_model), "row-deleted",
1086 G_CALLBACK(modest_msg_view_window_on_row_deleted),
1088 priv->row_inserted_handler =
1089 g_signal_connect (GTK_TREE_MODEL(priv->header_model), "row-inserted",
1090 G_CALLBACK(modest_msg_view_window_on_row_inserted),
1092 priv->rows_reordered_handler =
1093 g_signal_connect(GTK_TREE_MODEL(priv->header_model), "rows-reordered",
1094 G_CALLBACK(modest_msg_view_window_on_row_reordered),
1097 if (header_view != NULL){
1098 modest_header_view_add_observer(header_view,
1099 MODEST_HEADER_VIEW_OBSERVER(window));
1102 tny_msg_view_set_msg (TNY_MSG_VIEW (priv->msg_view), NULL);
1103 update_branding (MODEST_MSG_VIEW_WINDOW (window));
1105 if (priv->row_reference) {
1106 path = gtk_tree_row_reference_get_path (priv->row_reference);
1107 if (gtk_tree_model_get_iter (priv->header_model, &iter, path)) {
1109 gtk_tree_model_get (priv->header_model, &iter,
1110 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
1112 message_reader (window, priv, header, NULL, NULL, priv->row_reference);
1113 g_object_unref (header);
1115 gtk_tree_path_free (path);
1117 /* Check dimming rules */
1118 modest_ui_actions_check_toolbar_dimming_rules (MODEST_WINDOW (window));
1119 modest_ui_actions_check_menu_dimming_rules (MODEST_WINDOW (window));
1120 modest_window_check_dimming_rules_group (MODEST_WINDOW (window), MODEST_DIMMING_RULES_CLIPBOARD);
1122 return MODEST_WINDOW(window);
1126 modest_msg_view_window_new_for_search_result (TnyMsg *msg,
1127 const gchar *modest_account_name,
1128 const gchar *mailbox,
1129 const gchar *msg_uid)
1131 ModestMsgViewWindow *window = NULL;
1132 ModestMsgViewWindowPrivate *priv = NULL;
1133 ModestWindowMgr *mgr = NULL;
1135 mgr = modest_runtime_get_window_mgr ();
1136 window = MODEST_MSG_VIEW_WINDOW (modest_window_mgr_get_msg_view_window (mgr));
1137 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), NULL);
1138 modest_msg_view_window_construct (window, modest_account_name, mailbox, msg_uid);
1140 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1142 /* Remember that this is a search result,
1143 * so we can disable some UI appropriately: */
1144 priv->is_search_result = TRUE;
1146 tny_msg_view_set_msg (TNY_MSG_VIEW (priv->msg_view), msg);
1147 update_branding (MODEST_MSG_VIEW_WINDOW (window));
1149 update_window_title (window);
1150 /* gtk_widget_show_all (GTK_WIDGET (window));*/
1151 modest_msg_view_window_update_priority (window);
1153 /* Check dimming rules */
1154 modest_ui_actions_check_toolbar_dimming_rules (MODEST_WINDOW (window));
1155 modest_ui_actions_check_menu_dimming_rules (MODEST_WINDOW (window));
1156 modest_window_check_dimming_rules_group (MODEST_WINDOW (window), MODEST_DIMMING_RULES_CLIPBOARD);
1158 return MODEST_WINDOW(window);
1162 modest_msg_view_window_is_other_body (ModestMsgViewWindow *self)
1164 ModestMsgViewWindowPrivate *priv = NULL;
1166 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self), FALSE);
1167 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
1169 return (priv->other_body != NULL);
1173 modest_msg_view_window_new_with_other_body (TnyMsg *msg,
1174 TnyMimePart *other_body,
1175 const gchar *modest_account_name,
1176 const gchar *mailbox,
1177 const gchar *msg_uid)
1179 GObject *obj = NULL;
1180 ModestMsgViewWindowPrivate *priv;
1181 ModestWindowMgr *mgr = NULL;
1183 g_return_val_if_fail (msg, NULL);
1184 mgr = modest_runtime_get_window_mgr ();
1185 obj = G_OBJECT (modest_window_mgr_get_msg_view_window (mgr));
1186 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (obj);
1187 modest_msg_view_window_construct (MODEST_MSG_VIEW_WINDOW (obj),
1188 modest_account_name, mailbox, msg_uid);
1191 priv->other_body = g_object_ref (other_body);
1192 modest_msg_view_set_msg_with_other_body (MODEST_MSG_VIEW (priv->msg_view), msg, other_body);
1194 tny_msg_view_set_msg (TNY_MSG_VIEW (priv->msg_view), msg);
1196 update_window_title (MODEST_MSG_VIEW_WINDOW (obj));
1197 update_branding (MODEST_MSG_VIEW_WINDOW (obj));
1199 /* gtk_widget_show_all (GTK_WIDGET (obj)); */
1201 /* Check dimming rules */
1202 modest_ui_actions_check_toolbar_dimming_rules (MODEST_WINDOW (obj));
1203 modest_ui_actions_check_menu_dimming_rules (MODEST_WINDOW (obj));
1204 modest_window_check_dimming_rules_group (MODEST_WINDOW (obj), MODEST_DIMMING_RULES_CLIPBOARD);
1206 return MODEST_WINDOW(obj);
1210 modest_msg_view_window_new_for_attachment (TnyMsg *msg,
1211 const gchar *modest_account_name,
1212 const gchar *mailbox,
1213 const gchar *msg_uid)
1215 return modest_msg_view_window_new_with_other_body (msg, NULL, modest_account_name, mailbox, msg_uid);
1219 modest_msg_view_window_on_row_changed (GtkTreeModel *header_model,
1222 ModestMsgViewWindow *window)
1224 check_dimming_rules_after_change (window);
1228 modest_msg_view_window_on_row_deleted(GtkTreeModel *header_model,
1230 ModestMsgViewWindow *window)
1232 check_dimming_rules_after_change (window);
1234 /* The window could have dissapeared */
1237 check_dimming_rules_after_change (ModestMsgViewWindow *window)
1239 modest_ui_actions_check_menu_dimming_rules (MODEST_WINDOW (window));
1240 modest_ui_actions_check_toolbar_dimming_rules (MODEST_WINDOW (window));
1244 /* On insertions we check if the folder still has the message we are
1245 * showing or do not. If do not, we do nothing. Which means we are still
1246 * not attached to any header folder and thus next/prev buttons are
1247 * still dimmed. Once the message that is shown by msg-view is found, the
1248 * new model of header-view will be attached and the references will be set.
1249 * On each further insertions dimming rules will be checked. However
1250 * this requires extra CPU time at least works.
1251 * (An message might be deleted from TnyFolder and thus will not be
1252 * inserted into the model again for example if it is removed by the
1253 * imap server and the header view is refreshed.)
1256 modest_msg_view_window_on_row_inserted (GtkTreeModel *model,
1257 GtkTreePath *tree_path,
1258 GtkTreeIter *tree_iter,
1259 ModestMsgViewWindow *window)
1261 ModestMsgViewWindowPrivate *priv = NULL;
1262 TnyHeader *header = NULL;
1264 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window));
1265 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1267 g_assert (model == priv->header_model);
1269 /* Check if the newly inserted message is the same we are actually
1270 * showing. IF not, we should remain detached from the header model
1271 * and thus prev and next toolbar buttons should remain dimmed. */
1272 gtk_tree_model_get (model, tree_iter,
1273 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
1276 if (TNY_IS_HEADER (header)) {
1279 uid = modest_tny_folder_get_header_unique_id (header);
1280 if (!g_str_equal(priv->msg_uid, uid)) {
1281 check_dimming_rules_after_change (window);
1283 g_object_unref (G_OBJECT(header));
1287 g_object_unref(G_OBJECT(header));
1290 if (priv->row_reference) {
1291 gtk_tree_row_reference_free (priv->row_reference);
1294 /* Setup row_reference for the actual msg. */
1295 priv->row_reference = gtk_tree_row_reference_new (priv->header_model, tree_path);
1296 if (priv->row_reference == NULL) {
1297 g_warning("%s: No reference for msg header item.", __FUNCTION__);
1301 /* Now set up next_row_reference. */
1302 if (priv->next_row_reference) {
1303 gtk_tree_row_reference_free (priv->next_row_reference);
1306 priv->next_row_reference = gtk_tree_row_reference_copy (priv->row_reference);
1307 select_next_valid_row (priv->header_model,
1308 &(priv->next_row_reference), FALSE, priv->is_outbox);
1310 /* Connect the remaining callbacks to become able to detect
1311 * changes in header-view. */
1312 priv->row_changed_handler =
1313 g_signal_connect (priv->header_model, "row-changed",
1314 G_CALLBACK (modest_msg_view_window_on_row_changed),
1316 priv->row_deleted_handler =
1317 g_signal_connect (priv->header_model, "row-deleted",
1318 G_CALLBACK (modest_msg_view_window_on_row_deleted),
1320 priv->rows_reordered_handler =
1321 g_signal_connect (priv->header_model, "rows-reordered",
1322 G_CALLBACK (modest_msg_view_window_on_row_reordered),
1325 check_dimming_rules_after_change (window);
1329 modest_msg_view_window_on_row_reordered (GtkTreeModel *header_model,
1333 ModestMsgViewWindow *window)
1335 ModestMsgViewWindowPrivate *priv = NULL;
1336 gboolean already_changed = FALSE;
1338 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(window);
1340 /* If the current row was reordered select the proper next
1341 valid row. The same if the next row reference changes */
1342 if (!priv->row_reference ||
1343 !gtk_tree_row_reference_valid (priv->row_reference))
1346 if (priv->next_row_reference &&
1347 gtk_tree_row_reference_valid (priv->next_row_reference)) {
1348 GtkTreePath *cur, *next;
1349 /* Check that the order is still the correct one */
1350 cur = gtk_tree_row_reference_get_path (priv->row_reference);
1351 next = gtk_tree_row_reference_get_path (priv->next_row_reference);
1352 gtk_tree_path_next (cur);
1353 if (gtk_tree_path_compare (cur, next) != 0) {
1354 gtk_tree_row_reference_free (priv->next_row_reference);
1355 priv->next_row_reference = gtk_tree_row_reference_copy (priv->row_reference);
1356 select_next_valid_row (header_model, &(priv->next_row_reference), FALSE, priv->is_outbox);
1357 already_changed = TRUE;
1359 gtk_tree_path_free (cur);
1360 gtk_tree_path_free (next);
1362 if (priv->next_row_reference)
1363 gtk_tree_row_reference_free (priv->next_row_reference);
1364 /* Update next row reference */
1365 priv->next_row_reference = gtk_tree_row_reference_copy (priv->row_reference);
1366 select_next_valid_row (header_model, &(priv->next_row_reference), FALSE, priv->is_outbox);
1367 already_changed = TRUE;
1370 check_dimming_rules_after_change (window);
1373 /* The modest_msg_view_window_update_model_replaced implements update
1374 * function for ModestHeaderViewObserver. Checks whether the TnyFolder
1375 * actually belongs to the header-view is the same as the TnyFolder of
1376 * the message of msg-view or not. If they are different, there is
1377 * nothing to do. If they are the same, then the model has replaced and
1378 * the reference in msg-view shall be replaced from the old model to
1379 * the new model. In this case the view will be detached from it's
1380 * header folder. From this point the next/prev buttons are dimmed.
1383 modest_msg_view_window_update_model_replaced (ModestHeaderViewObserver *observer,
1384 GtkTreeModel *model,
1385 const gchar *tny_folder_id)
1387 ModestMsgViewWindowPrivate *priv = NULL;
1388 ModestMsgViewWindow *window = NULL;
1390 g_assert(MODEST_IS_HEADER_VIEW_OBSERVER(observer));
1391 g_assert(MODEST_IS_MSG_VIEW_WINDOW(observer));
1393 window = MODEST_MSG_VIEW_WINDOW(observer);
1394 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(window);
1396 /* If there is an other folder in the header-view then we do
1397 * not care about it's model (msg list). Else if the
1398 * header-view shows the folder the msg shown by us is in, we
1399 * shall replace our model reference and make some check. */
1400 if(model == NULL || tny_folder_id == NULL ||
1401 (priv->header_folder_id && !g_str_equal(tny_folder_id, priv->header_folder_id)))
1404 /* Model is changed(replaced), so we should forget the old
1405 * one. Because there might be other references and there
1406 * might be some change on the model even if we unreferenced
1407 * it, we need to disconnect our signals here. */
1408 if (priv->header_model) {
1409 if (g_signal_handler_is_connected(G_OBJECT (priv->header_model),
1410 priv->row_changed_handler))
1411 g_signal_handler_disconnect(G_OBJECT (priv->header_model),
1412 priv->row_changed_handler);
1413 if (g_signal_handler_is_connected(G_OBJECT (priv->header_model),
1414 priv->row_deleted_handler))
1415 g_signal_handler_disconnect(G_OBJECT (priv->header_model),
1416 priv->row_deleted_handler);
1417 if (g_signal_handler_is_connected(G_OBJECT (priv->header_model),
1418 priv->row_inserted_handler))
1419 g_signal_handler_disconnect(G_OBJECT (priv->header_model),
1420 priv->row_inserted_handler);
1421 if (g_signal_handler_is_connected(G_OBJECT (priv->header_model),
1422 priv->rows_reordered_handler))
1423 g_signal_handler_disconnect(G_OBJECT (priv->header_model),
1424 priv->rows_reordered_handler);
1427 if (priv->row_reference)
1428 gtk_tree_row_reference_free (priv->row_reference);
1429 if (priv->next_row_reference)
1430 gtk_tree_row_reference_free (priv->next_row_reference);
1431 g_object_unref(priv->header_model);
1434 priv->row_changed_handler = 0;
1435 priv->row_deleted_handler = 0;
1436 priv->row_inserted_handler = 0;
1437 priv->rows_reordered_handler = 0;
1438 priv->next_row_reference = NULL;
1439 priv->row_reference = NULL;
1440 priv->header_model = NULL;
1443 priv->header_model = g_object_ref (model);
1445 /* Also we must connect to the new model for row insertions.
1446 * Only for insertions now. We will need other ones only after
1447 * the msg is show by msg-view is added to the new model. */
1448 priv->row_inserted_handler =
1449 g_signal_connect (priv->header_model, "row-inserted",
1450 G_CALLBACK(modest_msg_view_window_on_row_inserted),
1453 modest_ui_actions_check_menu_dimming_rules(MODEST_WINDOW(window));
1454 modest_ui_actions_check_toolbar_dimming_rules(MODEST_WINDOW(window));
1458 modest_msg_view_window_toolbar_on_transfer_mode (ModestMsgViewWindow *self)
1460 ModestMsgViewWindowPrivate *priv= NULL;
1462 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self), FALSE);
1463 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
1465 return priv->progress_hint;
1469 modest_msg_view_window_get_header (ModestMsgViewWindow *self)
1471 ModestMsgViewWindowPrivate *priv= NULL;
1473 TnyHeader *header = NULL;
1474 GtkTreePath *path = NULL;
1477 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self), NULL);
1478 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
1480 /* If the message was not obtained from a treemodel,
1481 * for instance if it was opened directly by the search UI:
1483 if (priv->header_model == NULL ||
1484 priv->row_reference == NULL ||
1485 !gtk_tree_row_reference_valid (priv->row_reference)) {
1486 msg = modest_msg_view_window_get_message (self);
1488 header = tny_msg_get_header (msg);
1489 g_object_unref (msg);
1494 /* Get iter of the currently selected message in the header view: */
1495 path = gtk_tree_row_reference_get_path (priv->row_reference);
1496 g_return_val_if_fail (path != NULL, NULL);
1497 gtk_tree_model_get_iter (priv->header_model,
1501 /* Get current message header */
1502 gtk_tree_model_get (priv->header_model, &iter,
1503 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
1506 gtk_tree_path_free (path);
1511 modest_msg_view_window_get_message (ModestMsgViewWindow *self)
1513 ModestMsgViewWindowPrivate *priv;
1515 g_return_val_if_fail (self, NULL);
1517 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(self);
1519 return tny_msg_view_get_msg (TNY_MSG_VIEW (priv->msg_view));
1523 modest_msg_view_window_get_message_uid (ModestMsgViewWindow *self)
1525 ModestMsgViewWindowPrivate *priv;
1527 g_return_val_if_fail (self, NULL);
1529 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
1531 return (const gchar*) priv->msg_uid;
1534 /* Used for the Ctrl+F accelerator */
1536 modest_msg_view_window_toggle_isearch_toolbar (GtkWidget *obj,
1539 ModestMsgViewWindow *window = MODEST_MSG_VIEW_WINDOW (data);
1540 ModestMsgViewWindowPrivate *priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1542 if (GTK_WIDGET_VISIBLE (priv->isearch_toolbar)) {
1543 modest_msg_view_window_isearch_toolbar_close (obj, data);
1545 modest_msg_view_window_show_isearch_toolbar (obj, data);
1549 /* Handler for menu option */
1551 modest_msg_view_window_show_isearch_toolbar (GtkWidget *obj,
1554 ModestMsgViewWindow *window = MODEST_MSG_VIEW_WINDOW (data);
1555 ModestMsgViewWindowPrivate *priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1557 gtk_widget_show (priv->isearch_toolbar);
1558 modest_isearch_toolbar_highlight_entry (MODEST_ISEARCH_TOOLBAR (priv->isearch_toolbar), TRUE);
1561 /* Handler for click on the "X" close button in isearch toolbar */
1563 modest_msg_view_window_isearch_toolbar_close (GtkWidget *widget,
1564 ModestMsgViewWindow *obj)
1566 ModestMsgViewWindowPrivate *priv;
1568 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (obj);
1571 gtk_widget_hide (priv->isearch_toolbar);
1572 modest_msg_view_grab_focus (MODEST_MSG_VIEW (priv->msg_view));
1576 modest_msg_view_window_isearch_toolbar_search (GtkWidget *widget,
1577 ModestMsgViewWindow *obj)
1579 const gchar *current_search;
1580 ModestMsgViewWindowPrivate *priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (obj);
1582 if (modest_mime_part_view_is_empty (MODEST_MIME_PART_VIEW (priv->msg_view))) {
1583 modest_platform_system_banner (NULL, NULL, _("mail_ib_nothing_to_find"));
1587 current_search = modest_isearch_toolbar_get_search (MODEST_ISEARCH_TOOLBAR (widget));
1589 if ((current_search == NULL) || (strcmp (current_search, "") == 0)) {
1590 modest_platform_system_banner (NULL, NULL, _CS("ecdg_ib_find_rep_enter_text"));
1594 if ((priv->last_search == NULL) || (strcmp (priv->last_search, current_search) != 0)) {
1596 g_free (priv->last_search);
1597 priv->last_search = g_strdup (current_search);
1598 result = modest_isearch_view_search (MODEST_ISEARCH_VIEW (priv->msg_view),
1601 modest_platform_system_banner (NULL, NULL,
1602 _HL("ckct_ib_find_no_matches"));
1603 g_free (priv->last_search);
1604 priv->last_search = NULL;
1606 modest_isearch_toolbar_highlight_entry (MODEST_ISEARCH_TOOLBAR (priv->isearch_toolbar), TRUE);
1609 if (!modest_isearch_view_search_next (MODEST_ISEARCH_VIEW (priv->msg_view))) {
1610 modest_platform_system_banner (NULL, NULL,
1611 _HL("ckct_ib_find_search_complete"));
1612 g_free (priv->last_search);
1613 priv->last_search = NULL;
1615 modest_isearch_toolbar_highlight_entry (MODEST_ISEARCH_TOOLBAR (priv->isearch_toolbar), TRUE);
1622 modest_msg_view_window_set_zoom (ModestWindow *window,
1625 ModestMsgViewWindowPrivate *priv;
1626 ModestWindowPrivate *parent_priv;
1628 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window));
1630 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1631 parent_priv = MODEST_WINDOW_GET_PRIVATE (window);
1632 modest_zoomable_set_zoom (MODEST_ZOOMABLE (priv->msg_view), zoom);
1637 modest_msg_view_window_get_zoom (ModestWindow *window)
1639 ModestMsgViewWindowPrivate *priv;
1641 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), 1.0);
1643 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1644 return modest_zoomable_get_zoom (MODEST_ZOOMABLE (priv->msg_view));
1648 modest_msg_view_window_zoom_plus (ModestWindow *window)
1651 ModestMsgViewWindowPrivate *priv;
1655 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), 1.0);
1656 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1658 zoom_level = modest_zoomable_get_zoom (MODEST_ZOOMABLE (priv->msg_view));
1660 if (zoom_level >= 2.0) {
1661 modest_platform_system_banner (NULL, NULL,
1662 _CS("ckct_ib_max_zoom_level_reached"));
1664 } else if (zoom_level >= 1.5) {
1666 } else if (zoom_level >= 1.2) {
1668 } else if (zoom_level >= 1.0) {
1670 } else if (zoom_level >= 0.8) {
1672 } else if (zoom_level >= 0.5) {
1678 /* set zoom level */
1679 int_zoom = (gint) rint (zoom_level*100.0+0.1);
1680 banner_text = g_strdup_printf (_HL("wdgt_ib_zoom"), int_zoom);
1681 modest_platform_information_banner (GTK_WIDGET (window), NULL, banner_text);
1682 g_free (banner_text);
1683 modest_zoomable_set_zoom (MODEST_ZOOMABLE (priv->msg_view), zoom_level);
1689 modest_msg_view_window_zoom_minus (ModestWindow *window)
1692 ModestMsgViewWindowPrivate *priv;
1696 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), 1.0);
1697 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1699 zoom_level = modest_zoomable_get_zoom (MODEST_ZOOMABLE (priv->msg_view));
1701 if (zoom_level <= 0.5) {
1702 modest_platform_system_banner (NULL, NULL,
1703 _CS("ckct_ib_min_zoom_level_reached"));
1705 } else if (zoom_level <= 0.8) {
1707 } else if (zoom_level <= 1.0) {
1709 } else if (zoom_level <= 1.2) {
1711 } else if (zoom_level <= 1.5) {
1713 } else if (zoom_level <= 2.0) {
1719 /* set zoom level */
1720 int_zoom = (gint) rint (zoom_level*100.0+0.1);
1721 banner_text = g_strdup_printf (_HL("wdgt_ib_zoom"), int_zoom);
1722 modest_platform_information_banner (GTK_WIDGET (window), NULL, banner_text);
1723 g_free (banner_text);
1724 modest_zoomable_set_zoom (MODEST_ZOOMABLE (priv->msg_view), zoom_level);
1730 modest_msg_view_window_key_event (GtkWidget *window,
1736 focus = gtk_window_get_focus (GTK_WINDOW (window));
1738 /* for the isearch toolbar case */
1739 if (focus && GTK_IS_ENTRY (focus)) {
1740 if (event->keyval == GDK_BackSpace) {
1742 copy = gdk_event_copy ((GdkEvent *) event);
1743 gtk_widget_event (focus, copy);
1744 gdk_event_free (copy);
1754 modest_msg_view_window_last_message_selected (ModestMsgViewWindow *window)
1757 ModestMsgViewWindowPrivate *priv;
1758 GtkTreeIter tmp_iter;
1759 gboolean is_last_selected;
1761 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), TRUE);
1762 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1764 /*if no model (so no rows at all), then virtually we are the last*/
1765 if (!priv->header_model || !priv->row_reference)
1768 if (!gtk_tree_row_reference_valid (priv->row_reference))
1771 path = gtk_tree_row_reference_get_path (priv->row_reference);
1775 is_last_selected = TRUE;
1776 while (is_last_selected) {
1778 gtk_tree_path_next (path);
1779 if (!gtk_tree_model_get_iter (priv->header_model, &tmp_iter, path))
1781 gtk_tree_model_get (priv->header_model, &tmp_iter,
1782 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
1785 if (msg_is_visible (header, priv->is_outbox))
1786 is_last_selected = FALSE;
1787 g_object_unref(G_OBJECT(header));
1790 gtk_tree_path_free (path);
1791 return is_last_selected;
1795 modest_msg_view_window_has_headers_model (ModestMsgViewWindow *window)
1797 ModestMsgViewWindowPrivate *priv;
1799 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), TRUE);
1800 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1802 return priv->header_model != NULL;
1806 modest_msg_view_window_is_search_result (ModestMsgViewWindow *window)
1808 ModestMsgViewWindowPrivate *priv;
1810 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), TRUE);
1811 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1813 return priv->is_search_result;
1817 msg_is_visible (TnyHeader *header, gboolean check_outbox)
1819 if ((tny_header_get_flags(header) & TNY_HEADER_FLAG_DELETED))
1821 if (!check_outbox) {
1824 ModestTnySendQueueStatus status;
1825 status = modest_tny_all_send_queues_get_msg_status (header);
1826 return ((status != MODEST_TNY_SEND_QUEUE_FAILED) &&
1827 (status != MODEST_TNY_SEND_QUEUE_SENDING));
1832 modest_msg_view_window_first_message_selected (ModestMsgViewWindow *window)
1835 ModestMsgViewWindowPrivate *priv;
1836 gboolean is_first_selected;
1837 GtkTreeIter tmp_iter;
1839 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), TRUE);
1840 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1842 /*if no model (so no rows at all), then virtually we are the first*/
1843 if (!priv->header_model || !priv->row_reference)
1846 if (!gtk_tree_row_reference_valid (priv->row_reference))
1849 path = gtk_tree_row_reference_get_path (priv->row_reference);
1853 is_first_selected = TRUE;
1854 while (is_first_selected) {
1856 if(!gtk_tree_path_prev (path))
1858 /* Here the 'if' is needless for logic, but let make sure
1859 * iter is valid for gtk_tree_model_get. */
1860 if (!gtk_tree_model_get_iter (priv->header_model, &tmp_iter, path))
1862 gtk_tree_model_get (priv->header_model, &tmp_iter,
1863 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
1866 if (msg_is_visible (header, priv->is_outbox))
1867 is_first_selected = FALSE;
1868 g_object_unref(G_OBJECT(header));
1871 gtk_tree_path_free (path);
1872 return is_first_selected;
1879 GtkTreeRowReference *row_reference;
1883 message_reader_performer (gboolean canceled,
1885 GtkWindow *parent_window,
1886 TnyAccount *account,
1889 ModestMailOperation *mail_op = NULL;
1890 MsgReaderInfo *info;
1892 info = (MsgReaderInfo *) user_data;
1893 if (canceled || err) {
1894 update_window_title (MODEST_MSG_VIEW_WINDOW (parent_window));
1898 /* Register the header - it'll be unregistered in the callback */
1900 modest_window_mgr_register_header (modest_runtime_get_window_mgr (), info->header, NULL);
1902 /* New mail operation */
1903 mail_op = modest_mail_operation_new_with_error_handling (G_OBJECT(parent_window),
1904 modest_ui_actions_disk_operations_error_handler,
1907 modest_mail_operation_queue_add (modest_runtime_get_mail_operation_queue (), mail_op);
1909 modest_mail_operation_get_msg (mail_op, info->header, TRUE, view_msg_cb, info->row_reference);
1911 modest_mail_operation_find_msg (mail_op, info->folder, info->msg_uid, TRUE, view_msg_cb, NULL);
1912 g_object_unref (mail_op);
1914 /* Update dimming rules */
1915 modest_ui_actions_check_toolbar_dimming_rules (MODEST_WINDOW (parent_window));
1916 modest_ui_actions_check_menu_dimming_rules (MODEST_WINDOW (parent_window));
1919 /* Frees. The row_reference will be freed by the view_msg_cb callback */
1920 g_free (info->msg_uid);
1922 g_object_unref (info->folder);
1924 g_object_unref (info->header);
1925 g_slice_free (MsgReaderInfo, info);
1930 * Reads the message whose summary item is @header. It takes care of
1931 * several things, among others:
1933 * If the message was not previously downloaded then ask the user
1934 * before downloading. If there is no connection launch the connection
1935 * dialog. Update toolbar dimming rules.
1937 * Returns: TRUE if the mail operation was started, otherwise if the
1938 * user do not want to download the message, or if the user do not
1939 * want to connect, then the operation is not issued
1942 message_reader (ModestMsgViewWindow *window,
1943 ModestMsgViewWindowPrivate *priv,
1945 const gchar *msg_uid,
1947 GtkTreeRowReference *row_reference)
1949 ModestWindowMgr *mgr;
1950 TnyAccount *account = NULL;
1951 MsgReaderInfo *info;
1953 /* We set the header from model while we're loading */
1954 tny_header_view_set_header (TNY_HEADER_VIEW (priv->msg_view), header);
1955 modest_window_set_title (MODEST_WINDOW (window), _CS("ckdg_pb_updating"));
1961 g_object_ref (folder);
1963 mgr = modest_runtime_get_window_mgr ();
1964 /* Msg download completed */
1965 if (!header || !(tny_header_get_flags (header) & TNY_HEADER_FLAG_CACHED)) {
1967 /* Ask the user if he wants to download the message if
1969 if (!tny_device_is_online (modest_runtime_get_device())) {
1970 GtkResponseType response;
1972 response = modest_platform_run_confirmation_dialog (GTK_WINDOW (window),
1973 _("mcen_nc_get_msg"));
1974 if (response == GTK_RESPONSE_CANCEL) {
1975 update_window_title (window);
1980 folder = tny_header_get_folder (header);
1982 info = g_slice_new (MsgReaderInfo);
1983 info->msg_uid = g_strdup (msg_uid);
1985 info->header = g_object_ref (header);
1987 info->header = NULL;
1989 info->folder = g_object_ref (folder);
1991 info->folder = NULL;
1992 if (row_reference) {
1993 info->row_reference = gtk_tree_row_reference_copy (row_reference);
1995 info->row_reference = NULL;
1998 /* Offer the connection dialog if necessary */
1999 modest_platform_connect_if_remote_and_perform ((GtkWindow *) window,
2001 TNY_FOLDER_STORE (folder),
2002 message_reader_performer,
2005 g_object_unref (folder);
2011 folder = tny_header_get_folder (header);
2014 account = tny_folder_get_account (folder);
2016 info = g_slice_new (MsgReaderInfo);
2017 info->msg_uid = g_strdup (msg_uid);
2019 info->folder = g_object_ref (folder);
2021 info->folder = NULL;
2023 info->header = g_object_ref (header);
2025 info->header = NULL;
2027 info->row_reference = gtk_tree_row_reference_copy (row_reference);
2029 info->row_reference = NULL;
2031 message_reader_performer (FALSE, NULL, (GtkWindow *) window, account, info);
2033 g_object_unref (account);
2035 g_object_unref (folder);
2041 modest_msg_view_window_select_next_message (ModestMsgViewWindow *window)
2043 ModestMsgViewWindowPrivate *priv;
2044 GtkTreePath *path= NULL;
2045 GtkTreeIter tmp_iter;
2047 gboolean retval = TRUE;
2048 GtkTreeRowReference *row_reference = NULL;
2050 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), FALSE);
2051 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
2053 if (!priv->row_reference)
2056 /* Update the next row reference if it's not valid. This could
2057 happen if for example the header which it was pointing to,
2058 was deleted. The best place to do it is in the row-deleted
2059 handler but the tinymail model do not work like the glib
2060 tree models and reports the deletion when the row is still
2062 if (!gtk_tree_row_reference_valid (priv->next_row_reference)) {
2063 if (priv->next_row_reference) {
2064 gtk_tree_row_reference_free (priv->next_row_reference);
2066 if (gtk_tree_row_reference_valid (priv->row_reference)) {
2067 priv->next_row_reference = gtk_tree_row_reference_copy (priv->row_reference);
2068 select_next_valid_row (priv->header_model, &(priv->next_row_reference), FALSE, priv->is_outbox);
2070 priv->next_row_reference = NULL;
2073 if (priv->next_row_reference)
2074 path = gtk_tree_row_reference_get_path (priv->next_row_reference);
2078 row_reference = gtk_tree_row_reference_copy (priv->next_row_reference);
2080 gtk_tree_model_get_iter (priv->header_model,
2083 gtk_tree_path_free (path);
2085 gtk_tree_model_get (priv->header_model, &tmp_iter,
2086 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
2089 /* Read the message & show it */
2090 if (!message_reader (window, priv, header, NULL, NULL, row_reference)) {
2093 gtk_tree_row_reference_free (row_reference);
2096 g_object_unref (header);
2102 modest_msg_view_window_select_previous_message (ModestMsgViewWindow *window)
2104 ModestMsgViewWindowPrivate *priv = NULL;
2106 gboolean finished = FALSE;
2107 gboolean retval = FALSE;
2109 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), FALSE);
2110 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
2112 if (priv->row_reference && !gtk_tree_row_reference_valid (priv->row_reference)) {
2113 gtk_tree_row_reference_free (priv->row_reference);
2114 priv->row_reference = NULL;
2117 /* Return inmediatly if there is no header model */
2118 if (!priv->header_model || !priv->row_reference)
2121 path = gtk_tree_row_reference_get_path (priv->row_reference);
2122 while (!finished && gtk_tree_path_prev (path)) {
2126 gtk_tree_model_get_iter (priv->header_model, &iter, path);
2127 gtk_tree_model_get (priv->header_model, &iter,
2128 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
2132 if (msg_is_visible (header, priv->is_outbox)) {
2133 GtkTreeRowReference *row_reference;
2134 row_reference = gtk_tree_row_reference_new (priv->header_model, path);
2135 /* Read the message & show it */
2136 retval = message_reader (window, priv, header, NULL, NULL, row_reference);
2137 gtk_tree_row_reference_free (row_reference);
2141 g_object_unref (header);
2145 gtk_tree_path_free (path);
2150 view_msg_cb (ModestMailOperation *mail_op,
2157 ModestMsgViewWindow *self = NULL;
2158 ModestMsgViewWindowPrivate *priv = NULL;
2159 GtkTreeRowReference *row_reference = NULL;
2161 /* Unregister the header (it was registered before creating the mail operation) */
2162 modest_window_mgr_unregister_header (modest_runtime_get_window_mgr (), header);
2164 row_reference = (GtkTreeRowReference *) user_data;
2167 gtk_tree_row_reference_free (row_reference);
2168 self = (ModestMsgViewWindow *) modest_mail_operation_get_source (mail_op);
2170 /* Restore window title */
2171 update_window_title (self);
2172 modest_ui_actions_on_close_window (NULL, MODEST_WINDOW (self));
2173 g_object_unref (self);
2178 /* If there was any error */
2179 if (!modest_ui_actions_msg_retrieval_check (mail_op, header, msg)) {
2181 gtk_tree_row_reference_free (row_reference);
2182 self = (ModestMsgViewWindow *) modest_mail_operation_get_source (mail_op);
2184 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
2185 /* First we check if the parent is a folder window */
2186 if (priv->msg_uid && !modest_hildon2_window_mgr_get_folder_window (MODEST_HILDON2_WINDOW_MGR (modest_runtime_get_window_mgr ()))) {
2188 TnyAccount *account = NULL;
2189 GtkWidget *header_window = NULL;
2191 is_merge = g_str_has_prefix (priv->msg_uid, "merge:");
2193 /* Get the account */
2195 account = tny_account_store_find_account (TNY_ACCOUNT_STORE (modest_runtime_get_account_store ()),
2198 if (is_merge || account) {
2199 TnyFolder *folder = NULL;
2201 /* Try to get the message, if it's already downloaded
2202 we don't need to connect */
2204 folder = modest_tny_folder_store_find_folder_from_uri (TNY_FOLDER_STORE (account),
2207 ModestTnyAccountStore *account_store;
2208 ModestTnyLocalFoldersAccount *local_folders_account;
2210 account_store = modest_runtime_get_account_store ();
2211 local_folders_account = MODEST_TNY_LOCAL_FOLDERS_ACCOUNT (
2212 modest_tny_account_store_get_local_folders_account (account_store));
2213 folder = modest_tny_local_folders_account_get_merged_outbox (local_folders_account);
2214 g_object_unref (local_folders_account);
2216 if (account) g_object_unref (account);
2219 header_window = (GtkWidget *)
2220 modest_header_window_new (
2222 modest_window_get_active_account (MODEST_WINDOW (self)),
2223 modest_window_get_active_mailbox (MODEST_WINDOW (self)));
2224 if (!modest_window_mgr_register_window (modest_runtime_get_window_mgr (),
2225 MODEST_WINDOW (header_window),
2227 gtk_widget_destroy (GTK_WIDGET (header_window));
2229 gtk_widget_show_all (GTK_WIDGET (header_window));
2231 g_object_unref (folder);
2237 /* Restore window title */
2238 update_window_title (self);
2239 modest_ui_actions_on_close_window (NULL, MODEST_WINDOW (self));
2240 g_object_unref (self);
2245 /* Get the window */
2246 self = (ModestMsgViewWindow *) modest_mail_operation_get_source (mail_op);
2247 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self));
2248 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
2250 /* Update the row reference */
2251 if (priv->row_reference != NULL) {
2252 gtk_tree_row_reference_free (priv->row_reference);
2253 priv->row_reference = (row_reference && gtk_tree_row_reference_valid (row_reference))?gtk_tree_row_reference_copy (row_reference):NULL;
2254 if (priv->next_row_reference != NULL) {
2255 gtk_tree_row_reference_free (priv->next_row_reference);
2257 if (priv->row_reference) {
2258 priv->next_row_reference = gtk_tree_row_reference_copy (priv->row_reference);
2259 select_next_valid_row (priv->header_model, &(priv->next_row_reference), TRUE, priv->is_outbox);
2261 priv->next_row_reference = NULL;
2265 /* Mark header as read */
2266 if (!(tny_header_get_flags (header) & TNY_HEADER_FLAG_SEEN))
2267 tny_header_set_flag (header, TNY_HEADER_FLAG_SEEN);
2269 /* Set new message */
2270 if (priv->msg_view != NULL && TNY_IS_MSG_VIEW (priv->msg_view)) {
2271 tny_msg_view_set_msg (TNY_MSG_VIEW (priv->msg_view), msg);
2272 modest_msg_view_window_update_priority (self);
2273 update_window_title (MODEST_MSG_VIEW_WINDOW (self));
2274 update_branding (MODEST_MSG_VIEW_WINDOW (self));
2275 modest_msg_view_grab_focus (MODEST_MSG_VIEW (priv->msg_view));
2278 /* Set the new message uid of the window */
2279 if (priv->msg_uid) {
2280 g_free (priv->msg_uid);
2281 priv->msg_uid = modest_tny_folder_get_header_unique_id (header);
2284 /* Notify the observers */
2285 g_signal_emit (G_OBJECT (self), signals[MSG_CHANGED_SIGNAL],
2286 0, priv->header_model, priv->row_reference);
2288 /* Sync the flags if the message is not opened from a header
2289 model, i.e, if it's opened from a notification */
2290 if (!priv->header_model)
2294 g_object_unref (self);
2296 gtk_tree_row_reference_free (row_reference);
2300 modest_msg_view_window_get_folder_type (ModestMsgViewWindow *window)
2302 ModestMsgViewWindowPrivate *priv;
2304 TnyFolderType folder_type;
2306 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
2308 folder_type = TNY_FOLDER_TYPE_UNKNOWN;
2310 msg = tny_msg_view_get_msg (TNY_MSG_VIEW (priv->msg_view));
2314 folder = tny_msg_get_folder (msg);
2316 folder_type = modest_tny_folder_guess_folder_type (folder);
2317 g_object_unref (folder);
2319 g_object_unref (msg);
2327 modest_msg_view_window_update_priority (ModestMsgViewWindow *window)
2329 ModestMsgViewWindowPrivate *priv;
2330 TnyHeader *header = NULL;
2331 TnyHeaderFlags flags = 0;
2333 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
2335 if (priv->header_model && priv->row_reference && gtk_tree_row_reference_valid (priv->row_reference)) {
2337 GtkTreePath *path = NULL;
2339 path = gtk_tree_row_reference_get_path (priv->row_reference);
2340 g_return_if_fail (path != NULL);
2341 gtk_tree_model_get_iter (priv->header_model,
2343 gtk_tree_row_reference_get_path (priv->row_reference));
2345 gtk_tree_model_get (priv->header_model, &iter, TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
2347 gtk_tree_path_free (path);
2350 msg = tny_msg_view_get_msg (TNY_MSG_VIEW (priv->msg_view));
2352 header = tny_msg_get_header (msg);
2353 g_object_unref (msg);
2358 flags = tny_header_get_flags (header);
2359 g_object_unref(G_OBJECT(header));
2362 modest_msg_view_set_priority (MODEST_MSG_VIEW(priv->msg_view), flags);
2367 toolbar_resize (ModestMsgViewWindow *self)
2369 ModestMsgViewWindowPrivate *priv = NULL;
2370 ModestWindowPrivate *parent_priv = NULL;
2372 gint static_button_size;
2373 ModestWindowMgr *mgr;
2375 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self));
2376 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
2377 parent_priv = MODEST_WINDOW_GET_PRIVATE(self);
2379 mgr = modest_runtime_get_window_mgr ();
2380 static_button_size = modest_window_mgr_get_fullscreen_mode (mgr)?120:120;
2382 if (parent_priv->toolbar) {
2383 /* Set expandable and homogeneous tool buttons */
2384 widget = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/ToolbarMessageReply");
2385 gtk_tool_item_set_expand (GTK_TOOL_ITEM (widget), TRUE);
2386 gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (widget), TRUE);
2387 widget = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/ToolbarMessageReplyAll");
2388 gtk_tool_item_set_expand (GTK_TOOL_ITEM (widget), TRUE);
2389 gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (widget), TRUE);
2390 widget = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/ToolbarMessageForward");
2391 gtk_tool_item_set_expand (GTK_TOOL_ITEM (widget), TRUE);
2392 gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (widget), TRUE);
2393 widget = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/ToolbarDeleteMessage");
2394 gtk_tool_item_set_expand (GTK_TOOL_ITEM (widget), TRUE);
2395 gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (widget), TRUE);
2396 widget = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/ToolbarDownloadExternalImages");
2397 gtk_tool_item_set_expand (GTK_TOOL_ITEM (widget), TRUE);
2398 gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (widget), TRUE);
2399 gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (priv->next_toolitem), TRUE);
2400 gtk_tool_item_set_expand (GTK_TOOL_ITEM (priv->next_toolitem), TRUE);
2401 gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (priv->prev_toolitem), TRUE);
2402 gtk_tool_item_set_expand (GTK_TOOL_ITEM (priv->prev_toolitem), TRUE);
2407 modest_msg_view_window_show_toolbar (ModestWindow *self,
2408 gboolean show_toolbar)
2410 ModestMsgViewWindowPrivate *priv = NULL;
2411 ModestWindowPrivate *parent_priv;
2413 parent_priv = MODEST_WINDOW_GET_PRIVATE(self);
2414 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(self);
2416 /* Set optimized view status */
2417 priv->optimized_view = !show_toolbar;
2419 if (!parent_priv->toolbar) {
2420 parent_priv->toolbar = gtk_ui_manager_get_widget (parent_priv->ui_manager,
2423 /* We don't use HILDON_ICON_SIZE_FINGER in order to avoid the ifdef here */
2424 gtk_toolbar_set_icon_size (GTK_TOOLBAR (parent_priv->toolbar), gtk_icon_size_from_name ("hildon-finger"));
2425 gtk_widget_set_no_show_all (parent_priv->toolbar, TRUE);
2427 priv->next_toolitem = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/ToolbarMessageNext");
2428 priv->prev_toolitem = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/ToolbarMessageBack");
2429 toolbar_resize (MODEST_MSG_VIEW_WINDOW (self));
2431 modest_window_add_toolbar (MODEST_WINDOW (self),
2432 GTK_TOOLBAR (parent_priv->toolbar));
2437 /* Quick hack: this prevents toolbar icons "dance" when progress bar show status is changed */
2438 /* TODO: resize mode migth be GTK_RESIZE_QUEUE, in order to avoid unneccesary shows */
2439 gtk_container_set_resize_mode (GTK_CONTAINER(parent_priv->toolbar), GTK_RESIZE_IMMEDIATE);
2441 gtk_widget_show (GTK_WIDGET (parent_priv->toolbar));
2442 if (modest_msg_view_window_transfer_mode_enabled (MODEST_MSG_VIEW_WINDOW (self)))
2443 set_progress_hint (MODEST_MSG_VIEW_WINDOW (self), TRUE);
2445 set_progress_hint (MODEST_MSG_VIEW_WINDOW (self), FALSE);
2448 gtk_widget_set_no_show_all (parent_priv->toolbar, TRUE);
2449 gtk_widget_hide (GTK_WIDGET (parent_priv->toolbar));
2454 modest_msg_view_window_clipboard_owner_change (GtkClipboard *clipboard,
2456 ModestMsgViewWindow *window)
2458 if (!GTK_WIDGET_VISIBLE (window))
2461 modest_window_check_dimming_rules_group (MODEST_WINDOW (window), MODEST_DIMMING_RULES_CLIPBOARD);
2465 modest_msg_view_window_transfer_mode_enabled (ModestMsgViewWindow *self)
2467 ModestMsgViewWindowPrivate *priv;
2469 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self), FALSE);
2470 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(self);
2472 return priv->progress_hint;
2476 observers_empty (ModestMsgViewWindow *self)
2479 ModestMsgViewWindowPrivate *priv;
2480 gboolean is_empty = TRUE;
2481 guint pending_ops = 0;
2483 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(self);
2484 tmp = priv->progress_widgets;
2486 /* Check all observers */
2487 while (tmp && is_empty) {
2488 pending_ops = modest_progress_object_num_pending_operations (MODEST_PROGRESS_OBJECT(tmp->data));
2489 is_empty = pending_ops == 0;
2491 tmp = g_slist_next(tmp);
2498 on_account_removed (TnyAccountStore *account_store,
2499 TnyAccount *account,
2502 /* Do nothing if it's a transport account, because we only
2503 show the messages of a store account */
2504 if (tny_account_get_account_type(account) == TNY_ACCOUNT_TYPE_STORE) {
2505 const gchar *parent_acc = NULL;
2506 const gchar *our_acc = NULL;
2508 our_acc = modest_window_get_active_account (MODEST_WINDOW (user_data));
2509 parent_acc = modest_tny_account_get_parent_modest_account_name_for_server_account (account);
2511 /* Close this window if I'm showing a message of the removed account */
2512 if (our_acc && parent_acc && strcmp (parent_acc, our_acc) == 0)
2513 modest_ui_actions_on_close_window (NULL, MODEST_WINDOW (user_data));
2518 on_mail_operation_started (ModestMailOperation *mail_op,
2521 ModestMsgViewWindow *self;
2522 ModestMailOperationTypeOperation op_type;
2524 ModestMsgViewWindowPrivate *priv;
2525 GObject *source = NULL;
2527 self = MODEST_MSG_VIEW_WINDOW (user_data);
2528 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
2529 op_type = modest_mail_operation_get_type_operation (mail_op);
2530 tmp = priv->progress_widgets;
2531 source = modest_mail_operation_get_source(mail_op);
2532 if (G_OBJECT (self) == source) {
2533 if (op_type == MODEST_MAIL_OPERATION_TYPE_RECEIVE ||
2534 op_type == MODEST_MAIL_OPERATION_TYPE_OPEN ||
2535 op_type == MODEST_MAIL_OPERATION_TYPE_DELETE) {
2536 set_progress_hint (self, TRUE);
2538 modest_progress_object_add_operation (
2539 MODEST_PROGRESS_OBJECT (tmp->data),
2541 tmp = g_slist_next (tmp);
2545 g_object_unref (source);
2547 /* Update dimming rules */
2548 check_dimming_rules_after_change (self);
2552 on_mail_operation_finished (ModestMailOperation *mail_op,
2555 ModestMsgViewWindow *self;
2556 ModestMailOperationTypeOperation op_type;
2558 ModestMsgViewWindowPrivate *priv;
2560 self = MODEST_MSG_VIEW_WINDOW (user_data);
2561 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
2562 op_type = modest_mail_operation_get_type_operation (mail_op);
2563 tmp = priv->progress_widgets;
2565 if (op_type == MODEST_MAIL_OPERATION_TYPE_RECEIVE ||
2566 op_type == MODEST_MAIL_OPERATION_TYPE_OPEN ||
2567 op_type == MODEST_MAIL_OPERATION_TYPE_DELETE) {
2569 modest_progress_object_remove_operation (MODEST_PROGRESS_OBJECT (tmp->data),
2571 tmp = g_slist_next (tmp);
2574 /* If no more operations are being observed, NORMAL mode is enabled again */
2575 if (observers_empty (self)) {
2576 set_progress_hint (self, FALSE);
2580 /* Update dimming rules. We have to do this right here
2581 and not in view_msg_cb because at that point the
2582 transfer mode is still enabled so the dimming rule
2583 won't let the user delete the message that has been
2584 readed for example */
2585 check_dimming_rules_after_change (self);
2589 on_queue_changed (ModestMailOperationQueue *queue,
2590 ModestMailOperation *mail_op,
2591 ModestMailOperationQueueNotification type,
2592 ModestMsgViewWindow *self)
2594 ModestMsgViewWindowPrivate *priv;
2596 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
2598 /* If this operations was created by another window, do nothing */
2599 if (!modest_mail_operation_is_mine (mail_op, G_OBJECT(self)))
2602 if (type == MODEST_MAIL_OPERATION_QUEUE_OPERATION_ADDED) {
2603 priv->sighandlers = modest_signal_mgr_connect (priv->sighandlers,
2605 "operation-started",
2606 G_CALLBACK (on_mail_operation_started),
2608 priv->sighandlers = modest_signal_mgr_connect (priv->sighandlers,
2610 "operation-finished",
2611 G_CALLBACK (on_mail_operation_finished),
2613 } else if (type == MODEST_MAIL_OPERATION_QUEUE_OPERATION_REMOVED) {
2614 priv->sighandlers = modest_signal_mgr_disconnect (priv->sighandlers,
2616 "operation-started");
2617 priv->sighandlers = modest_signal_mgr_disconnect (priv->sighandlers,
2619 "operation-finished");
2624 modest_msg_view_window_get_attachments (ModestMsgViewWindow *win)
2626 ModestMsgViewWindowPrivate *priv;
2627 TnyList *selected_attachments = NULL;
2629 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (win), NULL);
2630 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (win);
2632 /* In Hildon 2.2 as there's no selection we assume we have all attachments selected */
2633 selected_attachments = modest_msg_view_get_attachments (MODEST_MSG_VIEW (priv->msg_view));
2635 return selected_attachments;
2639 ModestMsgViewWindow *self;
2641 gchar *attachment_uid;
2642 } DecodeAsyncHelper;
2645 on_decode_to_stream_async_handler (TnyMimePart *mime_part,
2651 DecodeAsyncHelper *helper = (DecodeAsyncHelper *) user_data;
2652 const gchar *content_type;
2654 if (cancelled || err) {
2657 if ((err->domain == TNY_ERROR_DOMAIN) &&
2658 (err->code == TNY_IO_ERROR_WRITE) &&
2659 (errno == ENOSPC)) {
2660 msg = g_strdup_printf (_KR("cerm_device_memory_full"), "");
2662 msg = g_strdup (_("mail_ib_file_operation_failed"));
2664 modest_platform_information_banner (NULL, NULL, msg);
2670 /* It could happen that the window was closed. So we
2671 assume it is a cancelation */
2672 if (!GTK_WIDGET_VISIBLE (helper->self))
2675 /* Remove the progress hint */
2676 set_progress_hint (helper->self, FALSE);
2678 content_type = tny_mime_part_get_content_type (mime_part);
2679 if (g_str_has_prefix (content_type, "message/rfc822")) {
2680 ModestWindowMgr *mgr;
2681 ModestWindow *msg_win = NULL;
2684 const gchar *mailbox;
2685 TnyStream *file_stream;
2688 fd = g_open (helper->file_path, O_RDONLY, 0644);
2690 file_stream = tny_fs_stream_new (fd);
2692 mgr = modest_runtime_get_window_mgr ();
2694 account = g_strdup (modest_window_get_active_account (MODEST_WINDOW (helper->self)));
2695 mailbox = modest_window_get_active_mailbox (MODEST_WINDOW (helper->self));
2698 account = modest_account_mgr_get_default_account (modest_runtime_get_account_mgr ());
2700 msg = tny_camel_msg_new ();
2701 tny_camel_msg_parse (msg, file_stream);
2702 msg_win = modest_msg_view_window_new_for_attachment (TNY_MSG (msg), account, mailbox, helper->attachment_uid);
2703 modest_window_set_zoom (MODEST_WINDOW (msg_win),
2704 modest_window_get_zoom (MODEST_WINDOW (helper->self)));
2705 if (modest_window_mgr_register_window (mgr, msg_win, MODEST_WINDOW (helper->self)))
2706 gtk_widget_show_all (GTK_WIDGET (msg_win));
2708 gtk_widget_destroy (GTK_WIDGET (msg_win));
2709 g_object_unref (msg);
2710 g_object_unref (file_stream);
2712 modest_platform_information_banner (NULL, NULL, _("mail_ib_file_operation_failed"));
2717 /* make the file read-only */
2718 g_chmod(helper->file_path, 0444);
2720 /* Activate the file */
2721 modest_platform_activate_file (helper->file_path, tny_mime_part_get_content_type (mime_part));
2726 g_object_unref (helper->self);
2727 g_free (helper->file_path);
2728 g_free (helper->attachment_uid);
2729 g_slice_free (DecodeAsyncHelper, helper);
2733 modest_msg_view_window_view_attachment (ModestMsgViewWindow *window,
2734 TnyMimePart *mime_part)
2736 ModestMsgViewWindowPrivate *priv;
2737 const gchar *msg_uid;
2738 gchar *attachment_uid = NULL;
2739 gint attachment_index = 0;
2740 TnyList *attachments;
2742 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window));
2743 g_return_if_fail (TNY_IS_MIME_PART (mime_part) || (mime_part == NULL));
2744 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
2746 msg_uid = modest_msg_view_window_get_message_uid (MODEST_MSG_VIEW_WINDOW (window));
2747 attachments = modest_msg_view_get_attachments (MODEST_MSG_VIEW (priv->msg_view));
2748 attachment_index = modest_list_index (attachments, (GObject *) mime_part);
2749 g_object_unref (attachments);
2751 if (msg_uid && attachment_index >= 0) {
2752 attachment_uid = g_strdup_printf ("%s/%d", msg_uid, attachment_index);
2755 if (mime_part == NULL) {
2756 gboolean error = FALSE;
2757 TnyList *selected_attachments = modest_msg_view_get_selected_attachments (MODEST_MSG_VIEW (priv->msg_view));
2758 if (selected_attachments == NULL || tny_list_get_length (selected_attachments) == 0) {
2760 } else if (tny_list_get_length (selected_attachments) > 1) {
2761 modest_platform_system_banner (NULL, NULL, _("mcen_ib_unable_to_display_more"));
2765 iter = tny_list_create_iterator (selected_attachments);
2766 mime_part = (TnyMimePart *) tny_iterator_get_current (iter);
2767 g_object_unref (iter);
2769 if (selected_attachments)
2770 g_object_unref (selected_attachments);
2775 g_object_ref (mime_part);
2778 if (tny_mime_part_is_purged (mime_part))
2781 if (!modest_tny_mime_part_is_msg (mime_part) && tny_mime_part_get_filename (mime_part)) {
2782 gchar *filepath = NULL;
2783 const gchar *att_filename = tny_mime_part_get_filename (mime_part);
2784 gboolean show_error_banner = FALSE;
2785 TnyFsStream *temp_stream = NULL;
2786 temp_stream = modest_utils_create_temp_stream (att_filename, attachment_uid,
2789 if (temp_stream != NULL) {
2790 ModestAccountMgr *mgr;
2791 DecodeAsyncHelper *helper;
2792 gboolean decode_in_provider;
2793 ModestProtocol *protocol;
2794 const gchar *account;
2796 /* Activate progress hint */
2797 set_progress_hint (window, TRUE);
2799 helper = g_slice_new0 (DecodeAsyncHelper);
2800 helper->self = g_object_ref (window);
2801 helper->file_path = g_strdup (filepath);
2802 helper->attachment_uid = g_strdup (attachment_uid);
2804 decode_in_provider = FALSE;
2805 mgr = modest_runtime_get_account_mgr ();
2806 account = modest_window_get_active_account (MODEST_WINDOW (window));
2807 if (modest_account_mgr_account_is_multimailbox (mgr, account, &protocol)) {
2808 if (MODEST_IS_ACCOUNT_PROTOCOL (protocol)) {
2810 uri = g_strconcat ("file://", filepath, NULL);
2811 decode_in_provider =
2812 modest_account_protocol_decode_part_to_stream_async (
2813 MODEST_ACCOUNT_PROTOCOL (protocol),
2816 TNY_STREAM (temp_stream),
2817 on_decode_to_stream_async_handler,
2824 if (!decode_in_provider)
2825 tny_mime_part_decode_to_stream_async (mime_part, TNY_STREAM (temp_stream),
2826 on_decode_to_stream_async_handler,
2829 g_object_unref (temp_stream);
2830 /* NOTE: files in the temporary area will be automatically
2831 * cleaned after some time if they are no longer in use */
2834 const gchar *content_type;
2835 /* the file may already exist but it isn't writable,
2836 * let's try to open it anyway */
2837 content_type = tny_mime_part_get_content_type (mime_part);
2838 modest_platform_activate_file (filepath, content_type);
2840 g_warning ("%s: modest_utils_create_temp_stream failed", __FUNCTION__);
2841 show_error_banner = TRUE;
2846 if (show_error_banner)
2847 modest_platform_information_banner (NULL, NULL, _("mail_ib_file_operation_failed"));
2848 } else if (!modest_tny_mime_part_is_msg (mime_part)) {
2849 ModestWindowMgr *mgr;
2850 ModestWindow *msg_win = NULL;
2851 TnyMsg *current_msg;
2855 current_msg = modest_msg_view_window_get_message (MODEST_MSG_VIEW_WINDOW (window));
2856 mgr = modest_runtime_get_window_mgr ();
2857 header = tny_msg_get_header (TNY_MSG (current_msg));
2858 found = modest_window_mgr_find_registered_message_uid (mgr,
2863 g_debug ("window for this body is already being created");
2866 /* it's not found, so create a new window for it */
2867 modest_window_mgr_register_header (mgr, header, attachment_uid); /* register the uid before building the window */
2868 gchar *account = g_strdup (modest_window_get_active_account (MODEST_WINDOW (window)));
2869 const gchar *mailbox = modest_window_get_active_mailbox (MODEST_WINDOW (window));
2871 account = modest_account_mgr_get_default_account (modest_runtime_get_account_mgr ());
2873 msg_win = modest_msg_view_window_new_with_other_body (TNY_MSG (current_msg), TNY_MIME_PART (mime_part),
2874 account, mailbox, attachment_uid);
2876 modest_window_set_zoom (MODEST_WINDOW (msg_win),
2877 modest_window_get_zoom (MODEST_WINDOW (window)));
2878 if (modest_window_mgr_register_window (mgr, msg_win, MODEST_WINDOW (window)))
2879 gtk_widget_show_all (GTK_WIDGET (msg_win));
2881 gtk_widget_destroy (GTK_WIDGET (msg_win));
2883 g_object_unref (current_msg);
2885 /* message attachment */
2886 TnyHeader *header = NULL;
2887 ModestWindowMgr *mgr;
2888 ModestWindow *msg_win = NULL;
2891 header = tny_msg_get_header (TNY_MSG (mime_part));
2892 mgr = modest_runtime_get_window_mgr ();
2893 found = modest_window_mgr_find_registered_header (mgr, header, &msg_win);
2896 /* if it's found, but there is no msg_win, it's probably in the process of being created;
2897 * thus, we don't do anything */
2898 g_debug ("window for is already being created");
2900 /* it's not found, so create a new window for it */
2901 modest_window_mgr_register_header (mgr, header, attachment_uid); /* register the uid before building the window */
2902 gchar *account = g_strdup (modest_window_get_active_account (MODEST_WINDOW (window)));
2903 const gchar *mailbox = modest_window_get_active_mailbox (MODEST_WINDOW (window));
2905 account = modest_account_mgr_get_default_account (modest_runtime_get_account_mgr ());
2906 msg_win = modest_msg_view_window_new_for_attachment (TNY_MSG (mime_part), account,
2907 mailbox, attachment_uid);
2908 modest_window_set_zoom (MODEST_WINDOW (msg_win),
2909 modest_window_get_zoom (MODEST_WINDOW (window)));
2910 if (modest_window_mgr_register_window (mgr, msg_win, MODEST_WINDOW (window)))
2911 gtk_widget_show_all (GTK_WIDGET (msg_win));
2913 gtk_widget_destroy (GTK_WIDGET (msg_win));
2919 g_free (attachment_uid);
2921 g_object_unref (mime_part);
2933 GnomeVFSResult result;
2935 ModestMsgViewWindow *window;
2938 static void save_mime_part_info_free (SaveMimePartInfo *info, gboolean with_struct);
2939 static gboolean idle_save_mime_part_show_result (SaveMimePartInfo *info);
2940 static gpointer save_mime_part_to_file (SaveMimePartInfo *info);
2941 static void save_mime_parts_to_file_with_checks (GtkWindow *parent, SaveMimePartInfo *info);
2944 save_mime_part_info_free (SaveMimePartInfo *info, gboolean with_struct)
2948 for (node = info->pairs; node != NULL; node = g_list_next (node)) {
2949 SaveMimePartPair *pair = (SaveMimePartPair *) node->data;
2950 g_free (pair->filename);
2951 g_object_unref (pair->part);
2952 g_slice_free (SaveMimePartPair, pair);
2954 g_list_free (info->pairs);
2957 g_object_unref (info->window);
2958 info->window = NULL;
2960 g_slice_free (SaveMimePartInfo, info);
2965 idle_save_mime_part_show_result (SaveMimePartInfo *info)
2967 /* This is a GDK lock because we are an idle callback and
2968 * modest_platform_system_banner is or does Gtk+ code */
2970 gdk_threads_enter (); /* CHECKED */
2971 if (info->result == GNOME_VFS_OK) {
2972 modest_platform_system_banner (NULL, NULL, _CS("sfil_ib_saved"));
2973 } else if (info->result == GNOME_VFS_ERROR_NO_SPACE) {
2976 /* Check if the uri belongs to the external mmc */
2977 if (g_str_has_prefix (info->uri, g_getenv (MODEST_MMC1_VOLUMEPATH_ENV)))
2978 msg = g_strdup_printf (_KR("cerm_device_memory_full"), "");
2980 msg = g_strdup (_KR("cerm_memory_card_full"));
2981 modest_platform_information_banner (NULL, NULL, msg);
2984 modest_platform_system_banner (NULL, NULL, _("mail_ib_file_operation_failed"));
2986 save_mime_part_info_free (info, FALSE);
2987 gdk_threads_leave (); /* CHECKED */
2993 save_mime_part_to_file (SaveMimePartInfo *info)
2995 GnomeVFSHandle *handle;
2997 SaveMimePartPair *pair = (SaveMimePartPair *) info->pairs->data;
2999 info->result = gnome_vfs_create (&handle, pair->filename, GNOME_VFS_OPEN_WRITE, FALSE, 0644);
3000 if (info->result == GNOME_VFS_OK) {
3001 GError *error = NULL;
3002 gboolean decode_in_provider;
3004 ModestAccountMgr *mgr;
3005 const gchar *account;
3006 ModestProtocol *protocol = NULL;
3008 stream = tny_vfs_stream_new (handle);
3010 decode_in_provider = FALSE;
3011 mgr = modest_runtime_get_account_mgr ();
3012 account = modest_window_get_active_account (MODEST_WINDOW (info->window));
3013 if (modest_account_mgr_account_is_multimailbox (mgr, account, &protocol)) {
3014 if (MODEST_IS_ACCOUNT_PROTOCOL (protocol)) {
3015 decode_in_provider =
3016 modest_account_protocol_decode_part_to_stream (
3017 MODEST_ACCOUNT_PROTOCOL (protocol),
3025 if (!decode_in_provider)
3026 written = tny_mime_part_decode_to_stream (pair->part, stream, &error);
3029 g_warning ("modest: could not save attachment %s: %d (%s)\n", pair->filename, error?error->code:-1, error?error->message:"Unknown error");
3031 if ((error->domain == TNY_ERROR_DOMAIN) &&
3032 (error->code == TNY_IO_ERROR_WRITE) &&
3033 (errno == ENOSPC)) {
3034 info->result = GNOME_VFS_ERROR_NO_SPACE;
3036 info->result = GNOME_VFS_ERROR_IO;
3039 g_object_unref (G_OBJECT (stream));
3041 g_warning ("Could not create save attachment %s: %s\n",
3042 pair->filename, gnome_vfs_result_to_string (info->result));
3045 /* Go on saving remaining files */
3046 info->pairs = g_list_remove_link (info->pairs, info->pairs);
3047 if (info->pairs != NULL) {
3048 save_mime_part_to_file (info);
3050 g_idle_add ((GSourceFunc) idle_save_mime_part_show_result, info);
3057 save_mime_parts_to_file_with_checks (GtkWindow *parent,
3058 SaveMimePartInfo *info)
3060 gboolean is_ok = TRUE;
3061 gint replaced_files = 0;
3062 const GList *files = info->pairs;
3063 const GList *iter, *to_replace = NULL;
3065 for (iter = files; (iter != NULL) && (replaced_files < 2); iter = g_list_next(iter)) {
3066 SaveMimePartPair *pair = iter->data;
3067 gchar *unescaped = g_uri_unescape_string (pair->filename, NULL);
3069 if (modest_utils_file_exists (unescaped)) {
3071 if (replaced_files == 1)
3076 if (replaced_files) {
3079 if (replaced_files == 1) {
3080 SaveMimePartPair *pair = to_replace->data;
3081 const gchar *basename = strrchr (pair->filename, G_DIR_SEPARATOR) + 1;
3082 gchar *escaped_basename, *message;
3084 escaped_basename = g_uri_unescape_string (basename, NULL);
3085 message = g_strdup_printf ("%s\n%s",
3086 _FM("docm_nc_replace_file"),
3087 (escaped_basename) ? escaped_basename : "");
3088 response = modest_platform_run_confirmation_dialog (parent, message);
3090 g_free (escaped_basename);
3092 response = modest_platform_run_confirmation_dialog (parent,
3093 _FM("docm_nc_replace_multiple"));
3095 if (response != GTK_RESPONSE_OK)
3100 save_mime_part_info_free (info, TRUE);
3102 g_thread_create ((GThreadFunc)save_mime_part_to_file, info, FALSE, NULL);
3107 typedef struct _SaveAttachmentsInfo {
3108 TnyList *attachments_list;
3109 ModestMsgViewWindow *window;
3110 } SaveAttachmentsInfo;
3113 save_attachments_response (GtkDialog *dialog,
3117 TnyList *mime_parts;
3119 GList *files_to_save = NULL;
3120 gchar *current_folder;
3121 SaveAttachmentsInfo *sa_info = (SaveAttachmentsInfo *) user_data;
3123 mime_parts = TNY_LIST (sa_info->attachments_list);
3125 if (arg1 != GTK_RESPONSE_OK)
3128 chooser_uri = gtk_file_chooser_get_uri (GTK_FILE_CHOOSER (dialog));
3129 current_folder = gtk_file_chooser_get_current_folder_uri (GTK_FILE_CHOOSER (dialog));
3130 if (current_folder && *current_folder != '\0') {
3132 modest_conf_set_string (modest_runtime_get_conf (), MODEST_CONF_LATEST_SAVE_ATTACHMENT_PATH,
3133 current_folder,&err);
3135 g_debug ("Error storing latest used folder: %s", err->message);
3139 g_free (current_folder);
3141 if (!modest_utils_folder_writable (chooser_uri)) {
3142 const gchar *err_msg;
3144 #ifdef MODEST_PLATFORM_MAEMO
3145 if (modest_maemo_utils_in_usb_mode ()) {
3146 err_msg = dgettext ("hildon-status-bar-usb", "usbh_ib_mmc_usb_connected");
3148 err_msg = _FM("sfil_ib_readonly_location");
3151 err_msg = _FM("sfil_ib_readonly_location");
3153 modest_platform_system_banner (NULL, NULL, err_msg);
3157 iter = tny_list_create_iterator (mime_parts);
3158 while (!tny_iterator_is_done (iter)) {
3159 TnyMimePart *mime_part = (TnyMimePart *) tny_iterator_get_current (iter);
3161 if ((modest_tny_mime_part_is_attachment_for_modest (mime_part)) &&
3162 !tny_mime_part_is_purged (mime_part) &&
3163 (tny_mime_part_get_filename (mime_part) != NULL)) {
3164 SaveMimePartPair *pair;
3166 pair = g_slice_new0 (SaveMimePartPair);
3168 if (tny_list_get_length (mime_parts) > 1) {
3170 gnome_vfs_escape_slashes (tny_mime_part_get_filename (mime_part));
3171 pair->filename = g_build_filename (chooser_uri, escaped, NULL);
3174 pair->filename = g_strdup (chooser_uri);
3176 pair->part = mime_part;
3177 files_to_save = g_list_prepend (files_to_save, pair);
3179 tny_iterator_next (iter);
3181 g_object_unref (iter);
3184 if (files_to_save != NULL) {
3185 SaveMimePartInfo *info = g_slice_new0 (SaveMimePartInfo);
3186 info->pairs = files_to_save;
3187 info->result = TRUE;
3188 info->uri = g_strdup (chooser_uri);
3189 info->window = g_object_ref (sa_info->window);
3190 save_mime_parts_to_file_with_checks ((GtkWindow *) dialog, info);
3192 g_free (chooser_uri);
3195 /* Free and close the dialog */
3196 g_object_unref (mime_parts);
3197 g_object_unref (sa_info->window);
3198 g_slice_free (SaveAttachmentsInfo, sa_info);
3199 gtk_widget_destroy (GTK_WIDGET (dialog));
3203 modest_msg_view_window_save_attachments (ModestMsgViewWindow *window,
3204 TnyList *mime_parts)
3206 ModestMsgViewWindowPrivate *priv;
3207 GtkWidget *save_dialog = NULL;
3208 gchar *conf_folder = NULL;
3209 gchar *filename = NULL;
3210 gchar *save_multiple_str = NULL;
3211 const gchar *root_folder = "file:///";
3213 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window));
3214 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
3216 if (mime_parts == NULL) {
3217 /* In Hildon 2.2 save and delete operate over all the attachments as there's no
3218 * selection available */
3219 mime_parts = modest_msg_view_get_attachments (MODEST_MSG_VIEW (priv->msg_view));
3220 if (mime_parts && !modest_maemo_utils_select_attachments (GTK_WINDOW (window), mime_parts, FALSE)) {
3221 g_object_unref (mime_parts);
3224 if (mime_parts == NULL || tny_list_get_length (mime_parts) == 0) {
3226 g_object_unref (mime_parts);
3232 g_object_ref (mime_parts);
3235 /* prepare dialog */
3236 if (tny_list_get_length (mime_parts) == 1) {
3238 /* only one attachment selected */
3239 iter = tny_list_create_iterator (mime_parts);
3240 TnyMimePart *mime_part = (TnyMimePart *) tny_iterator_get_current (iter);
3241 g_object_unref (iter);
3242 if (!modest_tny_mime_part_is_msg (mime_part) &&
3243 modest_tny_mime_part_is_attachment_for_modest (mime_part) &&
3244 !tny_mime_part_is_purged (mime_part)) {
3245 filename = g_strdup (tny_mime_part_get_filename (mime_part));
3247 /* TODO: show any error? */
3248 g_warning ("%s: Tried to save a non-file attachment", __FUNCTION__);
3249 g_object_unref (mime_parts);
3252 g_object_unref (mime_part);
3254 gint num = tny_list_get_length (mime_parts);
3255 save_multiple_str = g_strdup_printf (dngettext("hildon-fm",
3256 "sfil_va_number_of_objects_attachment",
3257 "sfil_va_number_of_objects_attachments",
3261 /* Creation of hildon file chooser dialog for saving */
3262 save_dialog = modest_toolkit_factory_create_file_chooser_dialog (modest_runtime_get_toolkit_factory (),
3264 (GtkWindow *) window,
3265 GTK_FILE_CHOOSER_ACTION_SAVE);
3267 /* Get last used folder */
3268 conf_folder = modest_conf_get_string (modest_runtime_get_conf (),
3269 MODEST_CONF_LATEST_SAVE_ATTACHMENT_PATH, NULL);
3271 /* File chooser stops working if we select "file:///" as current folder */
3272 if (conf_folder && g_ascii_strcasecmp (root_folder, conf_folder) != 0) {
3273 g_free (conf_folder);
3277 if (conf_folder && conf_folder[0] != '\0') {
3278 gtk_file_chooser_set_current_folder_uri (GTK_FILE_CHOOSER (save_dialog), conf_folder);
3281 /* Set the default folder to documents folder */
3282 docs_folder = (gchar *) g_strdup(g_get_user_special_dir (G_USER_DIRECTORY_DOCUMENTS));
3285 docs_folder = g_build_filename (g_getenv (MYDOCS_ENV), DOCS_FOLDER, NULL);
3287 gtk_file_chooser_set_current_folder (GTK_FILE_CHOOSER (save_dialog), docs_folder);
3288 g_free (docs_folder);
3290 g_free (conf_folder);
3294 gtk_file_chooser_set_current_name (GTK_FILE_CHOOSER (save_dialog),
3299 /* if multiple, set multiple string */
3300 if (save_multiple_str) {
3301 g_object_set (G_OBJECT (save_dialog), "save-multiple", save_multiple_str, NULL);
3302 gtk_window_set_title (GTK_WINDOW (save_dialog), _FM("sfil_ti_save_objects_files"));
3303 g_free (save_multiple_str);
3306 /* We must run this asynchronously, because the hildon dialog
3307 performs a gtk_dialog_run by itself which leads to gdk
3309 SaveAttachmentsInfo *sa_info;
3310 sa_info = g_slice_new (SaveAttachmentsInfo);
3311 sa_info->attachments_list = mime_parts;
3312 sa_info->window = g_object_ref (window);
3313 g_signal_connect (save_dialog, "response",
3314 G_CALLBACK (save_attachments_response), sa_info);
3316 gtk_widget_show_all (save_dialog);
3320 show_remove_attachment_information (gpointer userdata)
3322 ModestMsgViewWindow *window = (ModestMsgViewWindow *) userdata;
3323 ModestMsgViewWindowPrivate *priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
3325 /* We're outside the main lock */
3326 gdk_threads_enter ();
3328 if (priv->remove_attachment_banner != NULL) {
3329 gtk_widget_destroy (priv->remove_attachment_banner);
3330 g_object_unref (priv->remove_attachment_banner);
3333 priv->remove_attachment_banner = g_object_ref (
3334 modest_platform_animation_banner (NULL, NULL, _("mcen_me_inbox_remove_attachments")));
3336 gdk_threads_leave ();
3342 modest_msg_view_window_remove_attachments (ModestMsgViewWindow *window, gboolean get_all)
3344 ModestMsgViewWindowPrivate *priv;
3345 TnyList *mime_parts = NULL, *tmp;
3346 gchar *confirmation_message;
3352 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window));
3353 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
3355 /* In hildon 2.2 we ignore the get_all flag as we always get all attachments. This is
3356 * because we don't have selection
3358 mime_parts = modest_msg_view_get_attachments (MODEST_MSG_VIEW (priv->msg_view));
3360 /* Remove already purged messages from mime parts list. We use
3361 a copy of the list to remove items in the original one */
3362 tmp = tny_list_copy (mime_parts);
3363 iter = tny_list_create_iterator (tmp);
3364 while (!tny_iterator_is_done (iter)) {
3365 TnyMimePart *part = TNY_MIME_PART (tny_iterator_get_current (iter));
3366 if (tny_mime_part_is_purged (part))
3367 tny_list_remove (mime_parts, (GObject *) part);
3369 g_object_unref (part);
3370 tny_iterator_next (iter);
3372 g_object_unref (tmp);
3373 g_object_unref (iter);
3375 if (!modest_maemo_utils_select_attachments (GTK_WINDOW (window), mime_parts, TRUE) ||
3376 tny_list_get_length (mime_parts) == 0) {
3377 g_object_unref (mime_parts);
3381 n_attachments = tny_list_get_length (mime_parts);
3382 if (n_attachments == 1) {
3386 iter = tny_list_create_iterator (mime_parts);
3387 part = (TnyMimePart *) tny_iterator_get_current (iter);
3388 g_object_unref (iter);
3389 if (modest_tny_mime_part_is_msg (part)) {
3391 header = tny_msg_get_header (TNY_MSG (part));
3392 filename = tny_header_dup_subject (header);
3393 g_object_unref (header);
3394 if (filename == NULL)
3395 filename = g_strdup (_("mail_va_no_subject"));
3397 filename = g_strdup (tny_mime_part_get_filename (TNY_MIME_PART (part)));
3399 confirmation_message = g_strdup_printf (_("mcen_nc_purge_file_text"), filename);
3401 g_object_unref (part);
3403 confirmation_message = g_strdup_printf (ngettext("mcen_nc_purge_file_text",
3404 "mcen_nc_purge_files_text",
3405 n_attachments), n_attachments);
3407 response = modest_platform_run_confirmation_dialog (GTK_WINDOW (window),
3408 confirmation_message);
3409 g_free (confirmation_message);
3411 if (response != GTK_RESPONSE_OK) {
3412 g_object_unref (mime_parts);
3416 priv->purge_timeout = g_timeout_add (2000, show_remove_attachment_information, window);
3418 iter = tny_list_create_iterator (mime_parts);
3419 while (!tny_iterator_is_done (iter)) {
3422 part = (TnyMimePart *) tny_iterator_get_current (iter);
3423 tny_mime_part_set_purged (TNY_MIME_PART (part));
3424 g_object_unref (part);
3425 tny_iterator_next (iter);
3427 g_object_unref (iter);
3429 msg = tny_msg_view_get_msg (TNY_MSG_VIEW (priv->msg_view));
3430 tny_msg_view_clear (TNY_MSG_VIEW (priv->msg_view));
3431 tny_msg_rewrite_cache (msg);
3432 tny_msg_view_set_msg (TNY_MSG_VIEW (priv->msg_view), msg);
3433 g_object_unref (msg);
3434 update_branding (MODEST_MSG_VIEW_WINDOW (window));
3436 g_object_unref (mime_parts);
3438 if (priv->purge_timeout > 0) {
3439 g_source_remove (priv->purge_timeout);
3440 priv->purge_timeout = 0;
3443 if (priv->remove_attachment_banner) {
3444 gtk_widget_destroy (priv->remove_attachment_banner);
3445 g_object_unref (priv->remove_attachment_banner);
3446 priv->remove_attachment_banner = NULL;
3452 update_window_title (ModestMsgViewWindow *window)
3454 ModestMsgViewWindowPrivate *priv;
3456 TnyHeader *header = NULL;
3457 gchar *subject = NULL;
3459 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
3461 /* Note that if the window is closed while we're retrieving
3462 the message, this widget could de deleted */
3463 if (!priv->msg_view)
3466 msg = tny_msg_view_get_msg (TNY_MSG_VIEW (priv->msg_view));
3468 if (priv->other_body) {
3471 description = modest_tny_mime_part_get_header_value (priv->other_body, "Content-Description");
3473 g_strstrip (description);
3474 subject = description;
3476 } else if (msg != NULL) {
3477 header = tny_msg_get_header (msg);
3478 subject = tny_header_dup_subject (header);
3479 g_object_unref (header);
3480 g_object_unref (msg);
3483 if ((subject == NULL)||(subject[0] == '\0')) {
3485 subject = g_strdup (_("mail_va_no_subject"));
3488 modest_window_set_title (MODEST_WINDOW (window), subject);
3493 on_move_focus (GtkWidget *widget,
3494 GtkDirectionType direction,
3497 g_signal_stop_emission_by_name (G_OBJECT (widget), "move-focus");
3501 fetch_image_open_stream (TnyStreamCache *self, gint64 *expected_size, gchar *uri)
3503 GnomeVFSResult result;
3504 GnomeVFSHandle *handle = NULL;
3505 GnomeVFSFileInfo *info = NULL;
3508 result = gnome_vfs_open (&handle, uri, GNOME_VFS_OPEN_READ);
3509 if (result != GNOME_VFS_OK) {
3514 info = gnome_vfs_file_info_new ();
3515 result = gnome_vfs_get_file_info_from_handle (handle, info, GNOME_VFS_FILE_INFO_DEFAULT);
3516 if (result != GNOME_VFS_OK || ! (info->valid_fields & GNOME_VFS_FILE_INFO_FIELDS_SIZE)) {
3517 /* We put a "safe" default size for going to cache */
3518 *expected_size = (300*1024);
3520 *expected_size = info->size;
3522 gnome_vfs_file_info_unref (info);
3524 stream = tny_vfs_stream_new (handle);
3533 TnyStream *output_stream;
3534 GtkWidget *msg_view;
3539 on_fetch_image_idle_refresh_view (gpointer userdata)
3542 FetchImageData *fidata = (FetchImageData *) userdata;
3544 gdk_threads_enter ();
3545 if (GTK_WIDGET_DRAWABLE (fidata->msg_view)) {
3546 ModestMsgViewWindowPrivate *priv;
3548 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (fidata->window);
3549 priv->fetching_images--;
3550 gtk_widget_queue_draw (fidata->msg_view);
3551 update_progress_hint (MODEST_MSG_VIEW_WINDOW (fidata->window));
3553 gdk_threads_leave ();
3555 g_object_unref (fidata->msg_view);
3556 g_object_unref (fidata->window);
3557 g_slice_free (FetchImageData, fidata);
3562 on_fetch_image_thread (gpointer userdata)
3564 FetchImageData *fidata = (FetchImageData *) userdata;
3565 TnyStreamCache *cache;
3566 TnyStream *cache_stream;
3568 cache = modest_runtime_get_images_cache ();
3570 tny_stream_cache_get_stream (cache,
3572 (TnyStreamCacheOpenStreamFetcher) fetch_image_open_stream,
3573 (gpointer) fidata->uri);
3574 g_free (fidata->cache_id);
3575 g_free (fidata->uri);
3577 if (cache_stream != NULL) {
3580 while (G_LIKELY (!tny_stream_is_eos (cache_stream))) {
3583 nb_read = tny_stream_read (cache_stream, buffer, sizeof (buffer));
3584 if (G_UNLIKELY (nb_read < 0)) {
3586 } else if (G_LIKELY (nb_read > 0)) {
3587 gssize nb_written = 0;
3589 while (G_UNLIKELY (nb_written < nb_read)) {
3592 len = tny_stream_write (fidata->output_stream, buffer + nb_written,
3593 nb_read - nb_written);
3594 if (G_UNLIKELY (len < 0))
3600 tny_stream_close (cache_stream);
3601 g_object_unref (cache_stream);
3604 tny_stream_close (fidata->output_stream);
3605 g_object_unref (fidata->output_stream);
3607 g_idle_add (on_fetch_image_idle_refresh_view, fidata);
3613 on_fetch_image (ModestMsgView *msgview,
3616 ModestMsgViewWindow *window)
3618 const gchar *current_account;
3619 ModestMsgViewWindowPrivate *priv;
3620 FetchImageData *fidata;
3622 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
3624 current_account = modest_window_get_active_account (MODEST_WINDOW (window));
3626 fidata = g_slice_new0 (FetchImageData);
3627 fidata->msg_view = g_object_ref (msgview);
3628 fidata->window = g_object_ref (window);
3629 fidata->uri = g_strdup (uri);
3630 fidata->cache_id = modest_images_cache_get_id (current_account, uri);
3631 fidata->output_stream = g_object_ref (stream);
3633 priv->fetching_images++;
3634 if (g_thread_create (on_fetch_image_thread, fidata, FALSE, NULL) == NULL) {
3635 g_object_unref (fidata->output_stream);
3636 g_free (fidata->cache_id);
3637 g_free (fidata->uri);
3638 g_object_unref (fidata->msg_view);
3639 g_slice_free (FetchImageData, fidata);
3640 tny_stream_close (stream);
3641 priv->fetching_images--;
3642 update_progress_hint (window);
3645 update_progress_hint (window);
3651 setup_menu (ModestMsgViewWindow *self)
3653 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW(self));
3655 /* Settings menu buttons */
3656 modest_window_add_to_menu (MODEST_WINDOW (self), _("mcen_me_viewer_find"), NULL,
3657 MODEST_WINDOW_MENU_CALLBACK (modest_msg_view_window_show_isearch_toolbar),
3658 MODEST_DIMMING_CALLBACK (modest_ui_dimming_rules_on_find_in_msg));
3660 modest_window_add_to_menu (MODEST_WINDOW (self),
3661 dngettext(GETTEXT_PACKAGE,
3662 "mcen_me_move_message",
3663 "mcen_me_move_messages",
3666 MODEST_WINDOW_MENU_CALLBACK (modest_ui_actions_on_move_to),
3667 MODEST_DIMMING_CALLBACK (modest_ui_dimming_rules_on_move_to));
3669 modest_window_add_to_menu (MODEST_WINDOW (self), _("mcen_me_inbox_mark_as_read"), NULL,
3670 MODEST_WINDOW_MENU_CALLBACK (modest_ui_actions_on_mark_as_read),
3671 MODEST_DIMMING_CALLBACK (modest_ui_dimming_rules_on_mark_as_read_msg_in_view));
3673 modest_window_add_to_menu (MODEST_WINDOW (self), _("mcen_me_inbox_mark_as_unread"), NULL,
3674 MODEST_WINDOW_MENU_CALLBACK (modest_ui_actions_on_mark_as_unread),
3675 MODEST_DIMMING_CALLBACK (modest_ui_dimming_rules_on_mark_as_unread_msg_in_view));
3677 modest_window_add_to_menu (MODEST_WINDOW (self), _("mcen_me_viewer_save_attachments"), NULL,
3678 MODEST_WINDOW_MENU_CALLBACK (modest_ui_actions_save_attachments),
3679 MODEST_DIMMING_CALLBACK (modest_ui_dimming_rules_on_save_attachments));
3680 modest_window_add_to_menu (MODEST_WINDOW (self), _("mcen_me_inbox_remove_attachments"), NULL,
3681 MODEST_WINDOW_MENU_CALLBACK (modest_ui_actions_remove_attachments),
3682 MODEST_DIMMING_CALLBACK (modest_ui_dimming_rules_on_remove_attachments));
3684 modest_window_add_to_menu (MODEST_WINDOW (self), _("mcen_me_new_message"), "<Control>n",
3685 MODEST_WINDOW_MENU_CALLBACK (modest_ui_actions_on_new_msg),
3686 MODEST_DIMMING_CALLBACK (modest_ui_dimming_rules_on_new_msg));
3687 modest_window_add_to_menu (MODEST_WINDOW (self), _("mcen_me_viewer_addtocontacts"), NULL,
3688 MODEST_WINDOW_MENU_CALLBACK (modest_ui_actions_add_to_contacts),
3689 MODEST_DIMMING_CALLBACK (modest_ui_dimming_rules_on_add_to_contacts));
3691 modest_window_add_to_menu (MODEST_WINDOW (self), _("mcen_ti_message_properties"), NULL,
3692 MODEST_WINDOW_MENU_CALLBACK (modest_ui_actions_on_details),
3693 MODEST_DIMMING_CALLBACK (modest_ui_dimming_rules_on_details));
3697 modest_msg_view_window_add_to_contacts (ModestMsgViewWindow *self)
3699 ModestMsgViewWindowPrivate *priv;
3700 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
3701 GSList *recipients = NULL;
3704 msg = tny_msg_view_get_msg (TNY_MSG_VIEW (priv->msg_view));
3708 header = modest_msg_view_window_get_header (self);
3711 recipients = modest_tny_msg_header_get_all_recipients_list (header);
3712 g_object_unref (header);
3714 recipients = modest_tny_msg_get_all_recipients_list (msg);
3715 g_object_unref (msg);
3719 /* Offer the user to add recipients to the address book */
3720 modest_address_book_add_address_list_with_selector (recipients, (GtkWindow *) self);
3721 g_slist_foreach (recipients, (GFunc) g_free, NULL); g_slist_free (recipients);
3726 _modest_msg_view_window_map_event (GtkWidget *widget,
3730 ModestMsgViewWindow *self = (ModestMsgViewWindow *) userdata;
3732 update_progress_hint (self);
3738 modest_msg_view_window_fetch_images (ModestMsgViewWindow *self)
3740 ModestMsgViewWindowPrivate *priv;
3741 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
3743 modest_msg_view_request_fetch_images (MODEST_MSG_VIEW (priv->msg_view));
3747 modest_msg_view_window_has_blocked_external_images (ModestMsgViewWindow *self)
3749 ModestMsgViewWindowPrivate *priv;
3750 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
3752 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self), FALSE);
3754 return modest_msg_view_has_blocked_external_images (MODEST_MSG_VIEW (priv->msg_view));
3758 modest_msg_view_window_reload (ModestMsgViewWindow *self)
3760 ModestMsgViewWindowPrivate *priv;
3761 const gchar *msg_uid;
3762 TnyHeader *header = NULL;
3763 TnyFolder *folder = NULL;
3765 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self));
3767 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
3769 header = modest_msg_view_window_get_header (MODEST_MSG_VIEW_WINDOW (self));
3773 folder = tny_header_get_folder (header);
3774 g_object_unref (header);
3779 msg_uid = modest_msg_view_window_get_message_uid (self);
3781 GtkTreeRowReference *row_reference;
3783 if (priv->row_reference && gtk_tree_row_reference_valid (priv->row_reference)) {
3784 row_reference = priv->row_reference;
3786 row_reference = NULL;
3788 if (!message_reader (self, priv, NULL, msg_uid, folder, row_reference))
3789 g_warning ("Shouldn't happen, trying to reload a message failed");
3792 g_object_unref (folder);
3796 update_branding (ModestMsgViewWindow *self)
3798 const gchar *account;
3799 const gchar *mailbox;
3800 ModestAccountMgr *mgr;
3801 ModestProtocol *protocol = NULL;
3802 gchar *service_name = NULL;
3803 const GdkPixbuf *service_icon = NULL;
3804 ModestMsgViewWindowPrivate *priv;
3806 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
3808 account = modest_window_get_active_account (MODEST_WINDOW (self));
3809 mailbox = modest_window_get_active_mailbox (MODEST_WINDOW (self));
3811 mgr = modest_runtime_get_account_mgr ();
3813 if (modest_account_mgr_account_is_multimailbox (mgr, account, &protocol)) {
3814 if (MODEST_IS_ACCOUNT_PROTOCOL (protocol)) {
3815 service_name = modest_account_protocol_get_service_name (MODEST_ACCOUNT_PROTOCOL (protocol),
3817 service_icon = modest_account_protocol_get_service_icon (MODEST_ACCOUNT_PROTOCOL (protocol),
3818 account, mailbox, MODEST_ICON_SIZE_SMALL);
3822 modest_msg_view_set_branding (MODEST_MSG_VIEW (priv->msg_view), service_name, service_icon);
3823 g_free (service_name);
3827 sync_flags (ModestMsgViewWindow *self)
3829 TnyHeader *header = NULL;
3831 header = modest_msg_view_window_get_header (self);
3833 TnyMsg *msg = modest_msg_view_window_get_message (self);
3835 header = tny_msg_get_header (msg);
3836 g_object_unref (msg);
3841 TnyFolder *folder = tny_header_get_folder (header);
3844 ModestMailOperation *mail_op;
3846 /* Sync folder, we need this to save the seen flag */
3847 mail_op = modest_mail_operation_new (NULL);
3848 modest_mail_operation_queue_add (modest_runtime_get_mail_operation_queue (),
3850 modest_mail_operation_sync_folder (mail_op, folder, FALSE, NULL, NULL);
3851 g_object_unref (mail_op);
3852 g_object_unref (folder);
3854 g_object_unref (header);