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-toolkit-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-window-mgr.h>
71 #include <tny-camel-msg.h>
72 #include <modest-icon-names.h>
74 #ifdef MODEST_PLATFORM_MAEMO
75 #include <modest-maemo-utils.h>
78 #define MYDOCS_ENV "MYDOCSDIR"
79 #define DOCS_FOLDER ".documents"
81 typedef struct _ModestMsgViewWindowPrivate ModestMsgViewWindowPrivate;
82 struct _ModestMsgViewWindowPrivate {
85 GtkWidget *main_scroll;
86 GtkWidget *isearch_toolbar;
89 /* Progress observers */
90 GSList *progress_widgets;
93 GtkWidget *prev_toolitem;
94 GtkWidget *next_toolitem;
95 gboolean progress_hint;
98 /* Optimized view enabled */
99 gboolean optimized_view;
101 /* Whether this was created via the *_new_for_search_result() function. */
102 gboolean is_search_result;
104 /* Whether the message is in outbox */
107 /* A reference to the @model of the header view
108 * to allow selecting previous/next messages,
109 * if the message is currently selected in the header view.
111 const gchar *header_folder_id;
112 GtkTreeModel *header_model;
113 GtkTreeRowReference *row_reference;
114 GtkTreeRowReference *next_row_reference;
116 gulong clipboard_change_handler;
117 gulong queue_change_handler;
118 gulong account_removed_handler;
119 gulong row_changed_handler;
120 gulong row_deleted_handler;
121 gulong row_inserted_handler;
122 gulong rows_reordered_handler;
125 GtkWidget *remove_attachment_banner;
128 TnyMimePart *other_body;
133 static void modest_msg_view_window_class_init (ModestMsgViewWindowClass *klass);
134 static void modest_msg_view_window_init (ModestMsgViewWindow *obj);
135 static void modest_header_view_observer_init (ModestHeaderViewObserverIface *iface_class);
136 static void modest_msg_view_window_finalize (GObject *obj);
137 static void modest_msg_view_window_show_isearch_toolbar (GtkWidget *obj, gpointer data);
138 static void modest_msg_view_window_isearch_toolbar_close (GtkWidget *widget,
139 ModestMsgViewWindow *obj);
140 static void modest_msg_view_window_isearch_toolbar_search (GtkWidget *widget,
141 ModestMsgViewWindow *obj);
142 static void modest_msg_view_window_toggle_isearch_toolbar (GtkWidget *obj,
144 static void modest_msg_view_window_disconnect_signals (ModestWindow *self);
146 static gdouble modest_msg_view_window_get_zoom (ModestWindow *window);
147 static void modest_msg_view_window_set_zoom (ModestWindow *window,
149 static gboolean modest_msg_view_window_zoom_minus (ModestWindow *window);
150 static gboolean modest_msg_view_window_zoom_plus (ModestWindow *window);
151 static gboolean modest_msg_view_window_key_event (GtkWidget *window,
154 static void modest_msg_view_window_update_priority (ModestMsgViewWindow *window);
156 static void modest_msg_view_window_show_toolbar (ModestWindow *window,
157 gboolean show_toolbar);
159 static void modest_msg_view_window_clipboard_owner_change (GtkClipboard *clipboard,
161 ModestMsgViewWindow *window);
163 static void modest_msg_view_window_on_row_changed (GtkTreeModel *header_model,
166 ModestMsgViewWindow *window);
168 static void modest_msg_view_window_on_row_deleted (GtkTreeModel *header_model,
170 ModestMsgViewWindow *window);
172 static void modest_msg_view_window_on_row_inserted (GtkTreeModel *header_model,
173 GtkTreePath *tree_path,
174 GtkTreeIter *tree_iter,
175 ModestMsgViewWindow *window);
177 static void modest_msg_view_window_on_row_reordered (GtkTreeModel *header_model,
181 ModestMsgViewWindow *window);
183 static void modest_msg_view_window_update_model_replaced (ModestHeaderViewObserver *window,
185 const gchar *tny_folder_id);
187 static void on_queue_changed (ModestMailOperationQueue *queue,
188 ModestMailOperation *mail_op,
189 ModestMailOperationQueueNotification type,
190 ModestMsgViewWindow *self);
192 static void on_account_removed (TnyAccountStore *account_store,
196 static void on_move_focus (GtkWidget *widget,
197 GtkDirectionType direction,
200 static void view_msg_cb (ModestMailOperation *mail_op,
207 static void set_progress_hint (ModestMsgViewWindow *self,
210 static void update_window_title (ModestMsgViewWindow *window);
212 static void init_window (ModestMsgViewWindow *obj);
214 static gboolean msg_is_visible (TnyHeader *header, gboolean check_outbox);
216 static void check_dimming_rules_after_change (ModestMsgViewWindow *window);
218 static gboolean on_fetch_image (ModestMsgView *msgview,
221 ModestMsgViewWindow *window);
223 static gboolean modest_msg_view_window_scroll_child (ModestMsgViewWindow *self,
224 GtkScrollType scroll_type,
227 static gboolean message_reader (ModestMsgViewWindow *window,
228 ModestMsgViewWindowPrivate *priv,
230 const gchar *msg_uid,
232 GtkTreeRowReference *row_reference);
234 static void setup_menu (ModestMsgViewWindow *self);
235 static gboolean _modest_msg_view_window_map_event (GtkWidget *widget,
238 static void update_branding (ModestMsgViewWindow *self);
239 static void sync_flags (ModestMsgViewWindow *self);
241 /* list my signals */
248 static const GtkActionEntry msg_view_toolbar_action_entries [] = {
251 { "ToolbarMessageReply", MODEST_STOCK_REPLY, N_("mcen_me_inbox_reply"), "<CTRL>R", NULL, G_CALLBACK (modest_ui_actions_on_reply) },
252 { "ToolbarMessageReplyAll", MODEST_STOCK_REPLY_ALL, N_("mcen_me_inbox_replytoall"), NULL, NULL, G_CALLBACK (modest_ui_actions_on_reply_all) },
253 { "ToolbarMessageForward", MODEST_STOCK_FORWARD, N_("mcen_me_inbox_forward"), NULL, NULL, G_CALLBACK (modest_ui_actions_on_forward) },
254 { "ToolbarDeleteMessage", MODEST_STOCK_DELETE, N_("qgn_toolb_gene_deletebutton"), NULL, NULL, G_CALLBACK (modest_ui_actions_on_delete_message_or_folder) },
255 { "ToolbarMessageBack", MODEST_TOOLBAR_ICON_PREV, N_("qgn_toolb_gene_back"), NULL, NULL, G_CALLBACK (modest_ui_actions_on_prev) },
256 { "ToolbarMessageNext", MODEST_TOOLBAR_ICON_NEXT, N_("qgn_toolb_gene_forward"), NULL, NULL, G_CALLBACK (modest_ui_actions_on_next) },
257 { "ToolbarDownloadExternalImages", MODEST_TOOLBAR_ICON_DOWNLOAD_IMAGES, N_("mail_bd_external_images"), NULL, NULL, G_CALLBACK (modest_ui_actions_on_fetch_images) },
260 static const GtkToggleActionEntry msg_view_toggle_action_entries [] = {
261 { "FindInMessage", MODEST_TOOLBAR_ICON_FIND, N_("qgn_toolb_gene_find"), "<CTRL>F", NULL, G_CALLBACK (modest_msg_view_window_toggle_isearch_toolbar), FALSE },
264 #define MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE((o), \
265 MODEST_TYPE_MSG_VIEW_WINDOW, \
266 ModestMsgViewWindowPrivate))
268 static GtkWindowClass *parent_class = NULL;
270 /* uncomment the following if you have defined any signals */
271 static guint signals[LAST_SIGNAL] = {0};
274 modest_msg_view_window_get_type (void)
276 static GType my_type = 0;
278 static const GTypeInfo my_info = {
279 sizeof(ModestMsgViewWindowClass),
280 NULL, /* base init */
281 NULL, /* base finalize */
282 (GClassInitFunc) modest_msg_view_window_class_init,
283 NULL, /* class finalize */
284 NULL, /* class data */
285 sizeof(ModestMsgViewWindow),
287 (GInstanceInitFunc) modest_msg_view_window_init,
290 #ifndef MODEST_TOOLKIT_HILDON2
291 my_type = g_type_register_static (MODEST_TYPE_SHELL_WINDOW,
292 "ModestMsgViewWindow",
295 my_type = g_type_register_static (MODEST_TYPE_HILDON2_WINDOW,
296 "ModestMsgViewWindow",
300 static const GInterfaceInfo modest_header_view_observer_info =
302 (GInterfaceInitFunc) modest_header_view_observer_init,
303 NULL, /* interface_finalize */
304 NULL /* interface_data */
307 g_type_add_interface_static (my_type,
308 MODEST_TYPE_HEADER_VIEW_OBSERVER,
309 &modest_header_view_observer_info);
315 save_state (ModestWindow *self)
317 modest_widget_memory_save (modest_runtime_get_conf (),
319 MODEST_CONF_MSG_VIEW_WINDOW_KEY);
323 modest_msg_view_window_scroll_child (ModestMsgViewWindow *self,
324 GtkScrollType scroll_type,
328 ModestMsgViewWindowPrivate *priv;
331 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(self);
333 switch (scroll_type) {
334 case GTK_SCROLL_STEP_UP:
337 case GTK_SCROLL_STEP_DOWN:
340 case GTK_SCROLL_PAGE_UP:
343 case GTK_SCROLL_PAGE_DOWN:
346 case GTK_SCROLL_START:
357 modest_scrollable_scroll ((ModestScrollable *) priv->main_scroll, 0, step);
359 return (gboolean) step;
363 add_scroll_binding (GtkBindingSet *binding_set,
365 GtkScrollType scroll)
367 guint keypad_keyval = keyval - GDK_Left + GDK_KP_Left;
369 gtk_binding_entry_add_signal (binding_set, keyval, 0,
371 GTK_TYPE_SCROLL_TYPE, scroll,
372 G_TYPE_BOOLEAN, FALSE);
373 gtk_binding_entry_add_signal (binding_set, keypad_keyval, 0,
375 GTK_TYPE_SCROLL_TYPE, scroll,
376 G_TYPE_BOOLEAN, FALSE);
380 modest_msg_view_window_class_init (ModestMsgViewWindowClass *klass)
382 GObjectClass *gobject_class;
383 ModestWindowClass *modest_window_class;
384 GtkBindingSet *binding_set;
386 gobject_class = (GObjectClass*) klass;
387 modest_window_class = (ModestWindowClass *) klass;
389 parent_class = g_type_class_peek_parent (klass);
390 gobject_class->finalize = modest_msg_view_window_finalize;
392 modest_window_class->set_zoom_func = modest_msg_view_window_set_zoom;
393 modest_window_class->get_zoom_func = modest_msg_view_window_get_zoom;
394 modest_window_class->zoom_plus_func = modest_msg_view_window_zoom_plus;
395 modest_window_class->zoom_minus_func = modest_msg_view_window_zoom_minus;
396 modest_window_class->show_toolbar_func = modest_msg_view_window_show_toolbar;
397 modest_window_class->disconnect_signals_func = modest_msg_view_window_disconnect_signals;
399 modest_window_class->save_state_func = save_state;
401 klass->scroll_child = modest_msg_view_window_scroll_child;
403 signals[MSG_CHANGED_SIGNAL] =
404 g_signal_new ("msg-changed",
405 G_TYPE_FROM_CLASS (gobject_class),
407 G_STRUCT_OFFSET (ModestMsgViewWindowClass, msg_changed),
409 modest_marshal_VOID__POINTER_POINTER,
410 G_TYPE_NONE, 2, G_TYPE_POINTER, G_TYPE_POINTER);
412 signals[SCROLL_CHILD_SIGNAL] =
413 g_signal_new ("scroll-child",
414 G_TYPE_FROM_CLASS (gobject_class),
415 G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
416 G_STRUCT_OFFSET (ModestMsgViewWindowClass, scroll_child),
418 modest_marshal_BOOLEAN__ENUM_BOOLEAN,
419 G_TYPE_BOOLEAN, 2, GTK_TYPE_SCROLL_TYPE, G_TYPE_BOOLEAN);
421 binding_set = gtk_binding_set_by_class (klass);
422 add_scroll_binding (binding_set, GDK_Up, GTK_SCROLL_STEP_UP);
423 add_scroll_binding (binding_set, GDK_Down, GTK_SCROLL_STEP_DOWN);
424 add_scroll_binding (binding_set, GDK_Page_Up, GTK_SCROLL_PAGE_UP);
425 add_scroll_binding (binding_set, GDK_Page_Down, GTK_SCROLL_PAGE_DOWN);
426 add_scroll_binding (binding_set, GDK_Home, GTK_SCROLL_START);
427 add_scroll_binding (binding_set, GDK_End, GTK_SCROLL_END);
429 g_type_class_add_private (gobject_class, sizeof(ModestMsgViewWindowPrivate));
433 static void modest_header_view_observer_init(
434 ModestHeaderViewObserverIface *iface_class)
436 iface_class->update_func = modest_msg_view_window_update_model_replaced;
440 modest_msg_view_window_init (ModestMsgViewWindow *obj)
442 ModestMsgViewWindowPrivate *priv;
443 ModestWindowPrivate *parent_priv = NULL;
444 GtkActionGroup *action_group = NULL;
445 GError *error = NULL;
447 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(obj);
448 parent_priv = MODEST_WINDOW_GET_PRIVATE(obj);
449 parent_priv->ui_manager = gtk_ui_manager_new();
451 action_group = gtk_action_group_new ("ModestMsgViewWindowActions");
452 gtk_action_group_set_translation_domain (action_group, GETTEXT_PACKAGE);
454 /* Add common actions */
455 gtk_action_group_add_actions (action_group,
456 msg_view_toolbar_action_entries,
457 G_N_ELEMENTS (msg_view_toolbar_action_entries),
459 gtk_action_group_add_toggle_actions (action_group,
460 msg_view_toggle_action_entries,
461 G_N_ELEMENTS (msg_view_toggle_action_entries),
464 gtk_ui_manager_insert_action_group (parent_priv->ui_manager, action_group, 0);
465 g_object_unref (action_group);
467 /* Load the UI definition */
468 gtk_ui_manager_add_ui_from_file (parent_priv->ui_manager, MODEST_UIDIR "modest-msg-view-window-ui.xml",
471 g_printerr ("modest: could not merge modest-msg-view-window-ui.xml: %s\n", error->message);
472 g_error_free (error);
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));
1895 modest_ui_actions_on_close_window (NULL, MODEST_WINDOW (parent_window));
1899 /* Register the header - it'll be unregistered in the callback */
1901 modest_window_mgr_register_header (modest_runtime_get_window_mgr (), info->header, NULL);
1903 /* New mail operation */
1904 mail_op = modest_mail_operation_new_with_error_handling (G_OBJECT(parent_window),
1905 modest_ui_actions_disk_operations_error_handler,
1908 modest_mail_operation_queue_add (modest_runtime_get_mail_operation_queue (), mail_op);
1910 modest_mail_operation_get_msg (mail_op, info->header, TRUE, view_msg_cb, info->row_reference);
1912 modest_mail_operation_find_msg (mail_op, info->folder, info->msg_uid, TRUE, view_msg_cb, NULL);
1913 g_object_unref (mail_op);
1915 /* Update dimming rules */
1916 modest_ui_actions_check_toolbar_dimming_rules (MODEST_WINDOW (parent_window));
1917 modest_ui_actions_check_menu_dimming_rules (MODEST_WINDOW (parent_window));
1920 /* Frees. The row_reference will be freed by the view_msg_cb callback */
1921 g_free (info->msg_uid);
1923 g_object_unref (info->folder);
1925 g_object_unref (info->header);
1926 g_slice_free (MsgReaderInfo, info);
1931 * Reads the message whose summary item is @header. It takes care of
1932 * several things, among others:
1934 * If the message was not previously downloaded then ask the user
1935 * before downloading. If there is no connection launch the connection
1936 * dialog. Update toolbar dimming rules.
1938 * Returns: TRUE if the mail operation was started, otherwise if the
1939 * user do not want to download the message, or if the user do not
1940 * want to connect, then the operation is not issued
1943 message_reader (ModestMsgViewWindow *window,
1944 ModestMsgViewWindowPrivate *priv,
1946 const gchar *msg_uid,
1948 GtkTreeRowReference *row_reference)
1950 ModestWindowMgr *mgr;
1951 TnyAccount *account = NULL;
1952 MsgReaderInfo *info;
1954 /* We set the header from model while we're loading */
1955 tny_header_view_set_header (TNY_HEADER_VIEW (priv->msg_view), header);
1956 modest_window_set_title (MODEST_WINDOW (window), _CS("ckdg_pb_updating"));
1962 g_object_ref (folder);
1964 mgr = modest_runtime_get_window_mgr ();
1965 /* Msg download completed */
1966 if (!header || !(tny_header_get_flags (header) & TNY_HEADER_FLAG_CACHED)) {
1968 /* Ask the user if he wants to download the message if
1970 if (!tny_device_is_online (modest_runtime_get_device())) {
1971 GtkResponseType response;
1973 response = modest_platform_run_confirmation_dialog (GTK_WINDOW (window),
1974 _("mcen_nc_get_msg"));
1975 if (response == GTK_RESPONSE_CANCEL) {
1976 update_window_title (window);
1981 folder = tny_header_get_folder (header);
1983 info = g_slice_new (MsgReaderInfo);
1984 info->msg_uid = g_strdup (msg_uid);
1986 info->header = g_object_ref (header);
1988 info->header = NULL;
1990 info->folder = g_object_ref (folder);
1992 info->folder = NULL;
1993 if (row_reference) {
1994 info->row_reference = gtk_tree_row_reference_copy (row_reference);
1996 info->row_reference = NULL;
1999 /* Offer the connection dialog if necessary */
2000 modest_platform_connect_if_remote_and_perform ((GtkWindow *) window,
2002 TNY_FOLDER_STORE (folder),
2003 message_reader_performer,
2006 g_object_unref (folder);
2012 folder = tny_header_get_folder (header);
2015 account = tny_folder_get_account (folder);
2017 info = g_slice_new (MsgReaderInfo);
2018 info->msg_uid = g_strdup (msg_uid);
2020 info->folder = g_object_ref (folder);
2022 info->folder = NULL;
2024 info->header = g_object_ref (header);
2026 info->header = NULL;
2028 info->row_reference = gtk_tree_row_reference_copy (row_reference);
2030 info->row_reference = NULL;
2032 message_reader_performer (FALSE, NULL, (GtkWindow *) window, account, info);
2034 g_object_unref (account);
2036 g_object_unref (folder);
2042 modest_msg_view_window_select_next_message (ModestMsgViewWindow *window)
2044 ModestMsgViewWindowPrivate *priv;
2045 GtkTreePath *path= NULL;
2046 GtkTreeIter tmp_iter;
2048 gboolean retval = TRUE;
2049 GtkTreeRowReference *row_reference = NULL;
2051 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), FALSE);
2052 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
2054 if (!priv->row_reference)
2057 /* Update the next row reference if it's not valid. This could
2058 happen if for example the header which it was pointing to,
2059 was deleted. The best place to do it is in the row-deleted
2060 handler but the tinymail model do not work like the glib
2061 tree models and reports the deletion when the row is still
2063 if (!gtk_tree_row_reference_valid (priv->next_row_reference)) {
2064 if (priv->next_row_reference) {
2065 gtk_tree_row_reference_free (priv->next_row_reference);
2067 if (gtk_tree_row_reference_valid (priv->row_reference)) {
2068 priv->next_row_reference = gtk_tree_row_reference_copy (priv->row_reference);
2069 select_next_valid_row (priv->header_model, &(priv->next_row_reference), FALSE, priv->is_outbox);
2071 priv->next_row_reference = NULL;
2074 if (priv->next_row_reference)
2075 path = gtk_tree_row_reference_get_path (priv->next_row_reference);
2079 row_reference = gtk_tree_row_reference_copy (priv->next_row_reference);
2081 gtk_tree_model_get_iter (priv->header_model,
2084 gtk_tree_path_free (path);
2086 gtk_tree_model_get (priv->header_model, &tmp_iter,
2087 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
2090 /* Read the message & show it */
2091 if (!message_reader (window, priv, header, NULL, NULL, row_reference)) {
2094 gtk_tree_row_reference_free (row_reference);
2097 g_object_unref (header);
2103 modest_msg_view_window_select_previous_message (ModestMsgViewWindow *window)
2105 ModestMsgViewWindowPrivate *priv = NULL;
2107 gboolean finished = FALSE;
2108 gboolean retval = FALSE;
2110 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), FALSE);
2111 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
2113 if (priv->row_reference && !gtk_tree_row_reference_valid (priv->row_reference)) {
2114 gtk_tree_row_reference_free (priv->row_reference);
2115 priv->row_reference = NULL;
2118 /* Return inmediatly if there is no header model */
2119 if (!priv->header_model || !priv->row_reference)
2122 path = gtk_tree_row_reference_get_path (priv->row_reference);
2123 while (!finished && gtk_tree_path_prev (path)) {
2127 gtk_tree_model_get_iter (priv->header_model, &iter, path);
2128 gtk_tree_model_get (priv->header_model, &iter,
2129 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
2133 if (msg_is_visible (header, priv->is_outbox)) {
2134 GtkTreeRowReference *row_reference;
2135 row_reference = gtk_tree_row_reference_new (priv->header_model, path);
2136 /* Read the message & show it */
2137 retval = message_reader (window, priv, header, NULL, NULL, row_reference);
2138 gtk_tree_row_reference_free (row_reference);
2142 g_object_unref (header);
2146 gtk_tree_path_free (path);
2151 view_msg_cb (ModestMailOperation *mail_op,
2158 ModestMsgViewWindow *self = NULL;
2159 ModestMsgViewWindowPrivate *priv = NULL;
2160 GtkTreeRowReference *row_reference = NULL;
2162 /* Unregister the header (it was registered before creating the mail operation) */
2163 modest_window_mgr_unregister_header (modest_runtime_get_window_mgr (), header);
2165 row_reference = (GtkTreeRowReference *) user_data;
2168 gtk_tree_row_reference_free (row_reference);
2169 self = (ModestMsgViewWindow *) modest_mail_operation_get_source (mail_op);
2171 /* Restore window title */
2172 update_window_title (self);
2173 modest_ui_actions_on_close_window (NULL, MODEST_WINDOW (self));
2174 g_object_unref (self);
2179 /* If there was any error */
2180 if (!modest_ui_actions_msg_retrieval_check (mail_op, header, msg)) {
2182 gtk_tree_row_reference_free (row_reference);
2183 self = (ModestMsgViewWindow *) modest_mail_operation_get_source (mail_op);
2185 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
2186 /* First we check if the parent is a folder window */
2187 if (priv->msg_uid && !modest_window_mgr_get_folder_window (MODEST_WINDOW_MGR (modest_runtime_get_window_mgr ()))) {
2189 TnyAccount *account = NULL;
2190 GtkWidget *header_window = NULL;
2192 is_merge = g_str_has_prefix (priv->msg_uid, "merge:");
2194 /* Get the account */
2196 account = tny_account_store_find_account (TNY_ACCOUNT_STORE (modest_runtime_get_account_store ()),
2199 if (is_merge || account) {
2200 TnyFolder *folder = NULL;
2202 /* Try to get the message, if it's already downloaded
2203 we don't need to connect */
2205 folder = modest_tny_folder_store_find_folder_from_uri (TNY_FOLDER_STORE (account),
2208 ModestTnyAccountStore *account_store;
2209 ModestTnyLocalFoldersAccount *local_folders_account;
2211 account_store = modest_runtime_get_account_store ();
2212 local_folders_account = MODEST_TNY_LOCAL_FOLDERS_ACCOUNT (
2213 modest_tny_account_store_get_local_folders_account (account_store));
2214 folder = modest_tny_local_folders_account_get_merged_outbox (local_folders_account);
2215 g_object_unref (local_folders_account);
2217 if (account) g_object_unref (account);
2220 header_window = (GtkWidget *)
2221 modest_header_window_new (
2223 modest_window_get_active_account (MODEST_WINDOW (self)),
2224 modest_window_get_active_mailbox (MODEST_WINDOW (self)));
2225 if (!modest_window_mgr_register_window (modest_runtime_get_window_mgr (),
2226 MODEST_WINDOW (header_window),
2228 gtk_widget_destroy (GTK_WIDGET (header_window));
2230 gtk_widget_show_all (GTK_WIDGET (header_window));
2232 g_object_unref (folder);
2238 /* Restore window title */
2239 update_window_title (self);
2240 modest_ui_actions_on_close_window (NULL, MODEST_WINDOW (self));
2241 g_object_unref (self);
2246 /* Get the window */
2247 self = (ModestMsgViewWindow *) modest_mail_operation_get_source (mail_op);
2248 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self));
2249 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
2251 /* Update the row reference */
2252 if (priv->row_reference != NULL) {
2253 gtk_tree_row_reference_free (priv->row_reference);
2254 priv->row_reference = (row_reference && gtk_tree_row_reference_valid (row_reference))?gtk_tree_row_reference_copy (row_reference):NULL;
2255 if (priv->next_row_reference != NULL) {
2256 gtk_tree_row_reference_free (priv->next_row_reference);
2258 if (priv->row_reference) {
2259 priv->next_row_reference = gtk_tree_row_reference_copy (priv->row_reference);
2260 select_next_valid_row (priv->header_model, &(priv->next_row_reference), TRUE, priv->is_outbox);
2262 priv->next_row_reference = NULL;
2266 /* Mark header as read */
2267 if (!(tny_header_get_flags (header) & TNY_HEADER_FLAG_SEEN))
2268 tny_header_set_flag (header, TNY_HEADER_FLAG_SEEN);
2270 /* Set new message */
2271 if (priv->msg_view != NULL && TNY_IS_MSG_VIEW (priv->msg_view)) {
2272 tny_msg_view_set_msg (TNY_MSG_VIEW (priv->msg_view), msg);
2273 modest_msg_view_window_update_priority (self);
2274 update_window_title (MODEST_MSG_VIEW_WINDOW (self));
2275 update_branding (MODEST_MSG_VIEW_WINDOW (self));
2276 modest_msg_view_grab_focus (MODEST_MSG_VIEW (priv->msg_view));
2279 /* Set the new message uid of the window */
2280 if (priv->msg_uid) {
2281 g_free (priv->msg_uid);
2282 priv->msg_uid = modest_tny_folder_get_header_unique_id (header);
2285 /* Notify the observers */
2286 g_signal_emit (G_OBJECT (self), signals[MSG_CHANGED_SIGNAL],
2287 0, priv->header_model, priv->row_reference);
2289 /* Sync the flags if the message is not opened from a header
2290 model, i.e, if it's opened from a notification */
2291 if (!priv->header_model)
2295 g_object_unref (self);
2297 gtk_tree_row_reference_free (row_reference);
2301 modest_msg_view_window_get_folder_type (ModestMsgViewWindow *window)
2303 ModestMsgViewWindowPrivate *priv;
2305 TnyFolderType folder_type;
2307 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
2309 folder_type = TNY_FOLDER_TYPE_UNKNOWN;
2311 msg = tny_msg_view_get_msg (TNY_MSG_VIEW (priv->msg_view));
2315 folder = tny_msg_get_folder (msg);
2317 folder_type = modest_tny_folder_guess_folder_type (folder);
2318 g_object_unref (folder);
2320 g_object_unref (msg);
2328 modest_msg_view_window_update_priority (ModestMsgViewWindow *window)
2330 ModestMsgViewWindowPrivate *priv;
2331 TnyHeader *header = NULL;
2332 TnyHeaderFlags flags = 0;
2334 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
2336 if (priv->header_model && priv->row_reference && gtk_tree_row_reference_valid (priv->row_reference)) {
2338 GtkTreePath *path = NULL;
2340 path = gtk_tree_row_reference_get_path (priv->row_reference);
2341 g_return_if_fail (path != NULL);
2342 gtk_tree_model_get_iter (priv->header_model,
2344 gtk_tree_row_reference_get_path (priv->row_reference));
2346 gtk_tree_model_get (priv->header_model, &iter, TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
2348 gtk_tree_path_free (path);
2351 msg = tny_msg_view_get_msg (TNY_MSG_VIEW (priv->msg_view));
2353 header = tny_msg_get_header (msg);
2354 g_object_unref (msg);
2359 flags = tny_header_get_flags (header);
2360 g_object_unref(G_OBJECT(header));
2363 modest_msg_view_set_priority (MODEST_MSG_VIEW(priv->msg_view), flags);
2368 toolbar_resize (ModestMsgViewWindow *self)
2370 ModestMsgViewWindowPrivate *priv = NULL;
2371 ModestWindowPrivate *parent_priv = NULL;
2373 gint static_button_size;
2374 ModestWindowMgr *mgr;
2376 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self));
2377 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
2378 parent_priv = MODEST_WINDOW_GET_PRIVATE(self);
2380 mgr = modest_runtime_get_window_mgr ();
2381 static_button_size = modest_window_mgr_get_fullscreen_mode (mgr)?120:120;
2383 if (parent_priv->toolbar) {
2384 /* Set expandable and homogeneous tool buttons */
2385 widget = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/ToolbarMessageReply");
2386 gtk_tool_item_set_expand (GTK_TOOL_ITEM (widget), TRUE);
2387 gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (widget), TRUE);
2388 widget = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/ToolbarMessageReplyAll");
2389 gtk_tool_item_set_expand (GTK_TOOL_ITEM (widget), TRUE);
2390 gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (widget), TRUE);
2391 widget = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/ToolbarMessageForward");
2392 gtk_tool_item_set_expand (GTK_TOOL_ITEM (widget), TRUE);
2393 gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (widget), TRUE);
2394 widget = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/ToolbarDeleteMessage");
2395 gtk_tool_item_set_expand (GTK_TOOL_ITEM (widget), TRUE);
2396 gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (widget), TRUE);
2397 widget = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/ToolbarDownloadExternalImages");
2398 gtk_tool_item_set_expand (GTK_TOOL_ITEM (widget), TRUE);
2399 gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (widget), TRUE);
2400 gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (priv->next_toolitem), TRUE);
2401 gtk_tool_item_set_expand (GTK_TOOL_ITEM (priv->next_toolitem), TRUE);
2402 gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (priv->prev_toolitem), TRUE);
2403 gtk_tool_item_set_expand (GTK_TOOL_ITEM (priv->prev_toolitem), TRUE);
2408 modest_msg_view_window_show_toolbar (ModestWindow *self,
2409 gboolean show_toolbar)
2411 ModestMsgViewWindowPrivate *priv = NULL;
2412 ModestWindowPrivate *parent_priv;
2414 parent_priv = MODEST_WINDOW_GET_PRIVATE(self);
2415 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(self);
2417 /* Set optimized view status */
2418 priv->optimized_view = !show_toolbar;
2420 if (!parent_priv->toolbar) {
2421 parent_priv->toolbar = gtk_ui_manager_get_widget (parent_priv->ui_manager,
2424 /* We don't use HILDON_ICON_SIZE_FINGER in order to avoid the ifdef here */
2425 gtk_toolbar_set_icon_size (GTK_TOOLBAR (parent_priv->toolbar), gtk_icon_size_from_name ("hildon-finger"));
2426 gtk_widget_set_no_show_all (parent_priv->toolbar, TRUE);
2428 priv->next_toolitem = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/ToolbarMessageNext");
2429 priv->prev_toolitem = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/ToolbarMessageBack");
2430 toolbar_resize (MODEST_MSG_VIEW_WINDOW (self));
2432 modest_window_add_toolbar (MODEST_WINDOW (self),
2433 GTK_TOOLBAR (parent_priv->toolbar));
2438 /* Quick hack: this prevents toolbar icons "dance" when progress bar show status is changed */
2439 /* TODO: resize mode migth be GTK_RESIZE_QUEUE, in order to avoid unneccesary shows */
2440 gtk_container_set_resize_mode (GTK_CONTAINER(parent_priv->toolbar), GTK_RESIZE_IMMEDIATE);
2442 gtk_widget_show (GTK_WIDGET (parent_priv->toolbar));
2443 if (modest_msg_view_window_transfer_mode_enabled (MODEST_MSG_VIEW_WINDOW (self)))
2444 set_progress_hint (MODEST_MSG_VIEW_WINDOW (self), TRUE);
2446 set_progress_hint (MODEST_MSG_VIEW_WINDOW (self), FALSE);
2449 gtk_widget_set_no_show_all (parent_priv->toolbar, TRUE);
2450 gtk_widget_hide (GTK_WIDGET (parent_priv->toolbar));
2455 modest_msg_view_window_clipboard_owner_change (GtkClipboard *clipboard,
2457 ModestMsgViewWindow *window)
2459 if (!GTK_WIDGET_VISIBLE (window))
2462 modest_window_check_dimming_rules_group (MODEST_WINDOW (window), MODEST_DIMMING_RULES_CLIPBOARD);
2466 modest_msg_view_window_transfer_mode_enabled (ModestMsgViewWindow *self)
2468 ModestMsgViewWindowPrivate *priv;
2470 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self), FALSE);
2471 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(self);
2473 return priv->progress_hint;
2477 observers_empty (ModestMsgViewWindow *self)
2480 ModestMsgViewWindowPrivate *priv;
2481 gboolean is_empty = TRUE;
2482 guint pending_ops = 0;
2484 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(self);
2485 tmp = priv->progress_widgets;
2487 /* Check all observers */
2488 while (tmp && is_empty) {
2489 pending_ops = modest_progress_object_num_pending_operations (MODEST_PROGRESS_OBJECT(tmp->data));
2490 is_empty = pending_ops == 0;
2492 tmp = g_slist_next(tmp);
2499 on_account_removed (TnyAccountStore *account_store,
2500 TnyAccount *account,
2503 /* Do nothing if it's a transport account, because we only
2504 show the messages of a store account */
2505 if (tny_account_get_account_type(account) == TNY_ACCOUNT_TYPE_STORE) {
2506 const gchar *parent_acc = NULL;
2507 const gchar *our_acc = NULL;
2509 our_acc = modest_window_get_active_account (MODEST_WINDOW (user_data));
2510 parent_acc = modest_tny_account_get_parent_modest_account_name_for_server_account (account);
2512 /* Close this window if I'm showing a message of the removed account */
2513 if (our_acc && parent_acc && strcmp (parent_acc, our_acc) == 0)
2514 modest_ui_actions_on_close_window (NULL, MODEST_WINDOW (user_data));
2519 on_mail_operation_started (ModestMailOperation *mail_op,
2522 ModestMsgViewWindow *self;
2523 ModestMailOperationTypeOperation op_type;
2525 ModestMsgViewWindowPrivate *priv;
2526 GObject *source = NULL;
2528 self = MODEST_MSG_VIEW_WINDOW (user_data);
2529 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
2530 op_type = modest_mail_operation_get_type_operation (mail_op);
2531 tmp = priv->progress_widgets;
2532 source = modest_mail_operation_get_source(mail_op);
2533 if (G_OBJECT (self) == source) {
2534 if (op_type == MODEST_MAIL_OPERATION_TYPE_RECEIVE ||
2535 op_type == MODEST_MAIL_OPERATION_TYPE_OPEN ||
2536 op_type == MODEST_MAIL_OPERATION_TYPE_DELETE) {
2537 set_progress_hint (self, TRUE);
2539 modest_progress_object_add_operation (
2540 MODEST_PROGRESS_OBJECT (tmp->data),
2542 tmp = g_slist_next (tmp);
2546 g_object_unref (source);
2548 /* Update dimming rules */
2549 check_dimming_rules_after_change (self);
2553 on_mail_operation_finished (ModestMailOperation *mail_op,
2556 ModestMsgViewWindow *self;
2557 ModestMailOperationTypeOperation op_type;
2559 ModestMsgViewWindowPrivate *priv;
2561 self = MODEST_MSG_VIEW_WINDOW (user_data);
2562 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
2563 op_type = modest_mail_operation_get_type_operation (mail_op);
2564 tmp = priv->progress_widgets;
2566 if (op_type == MODEST_MAIL_OPERATION_TYPE_RECEIVE ||
2567 op_type == MODEST_MAIL_OPERATION_TYPE_OPEN ||
2568 op_type == MODEST_MAIL_OPERATION_TYPE_DELETE) {
2570 modest_progress_object_remove_operation (MODEST_PROGRESS_OBJECT (tmp->data),
2572 tmp = g_slist_next (tmp);
2575 /* If no more operations are being observed, NORMAL mode is enabled again */
2576 if (observers_empty (self)) {
2577 set_progress_hint (self, FALSE);
2581 /* Update dimming rules. We have to do this right here
2582 and not in view_msg_cb because at that point the
2583 transfer mode is still enabled so the dimming rule
2584 won't let the user delete the message that has been
2585 readed for example */
2586 check_dimming_rules_after_change (self);
2590 on_queue_changed (ModestMailOperationQueue *queue,
2591 ModestMailOperation *mail_op,
2592 ModestMailOperationQueueNotification type,
2593 ModestMsgViewWindow *self)
2595 ModestMsgViewWindowPrivate *priv;
2597 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
2599 /* If this operations was created by another window, do nothing */
2600 if (!modest_mail_operation_is_mine (mail_op, G_OBJECT(self)))
2603 if (type == MODEST_MAIL_OPERATION_QUEUE_OPERATION_ADDED) {
2604 priv->sighandlers = modest_signal_mgr_connect (priv->sighandlers,
2606 "operation-started",
2607 G_CALLBACK (on_mail_operation_started),
2609 priv->sighandlers = modest_signal_mgr_connect (priv->sighandlers,
2611 "operation-finished",
2612 G_CALLBACK (on_mail_operation_finished),
2614 } else if (type == MODEST_MAIL_OPERATION_QUEUE_OPERATION_REMOVED) {
2615 priv->sighandlers = modest_signal_mgr_disconnect (priv->sighandlers,
2617 "operation-started");
2618 priv->sighandlers = modest_signal_mgr_disconnect (priv->sighandlers,
2620 "operation-finished");
2625 modest_msg_view_window_get_attachments (ModestMsgViewWindow *win)
2627 ModestMsgViewWindowPrivate *priv;
2628 TnyList *selected_attachments = NULL;
2630 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (win), NULL);
2631 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (win);
2633 /* In Hildon 2.2 as there's no selection we assume we have all attachments selected */
2634 selected_attachments = modest_msg_view_get_attachments (MODEST_MSG_VIEW (priv->msg_view));
2636 return selected_attachments;
2640 ModestMsgViewWindow *self;
2642 gchar *attachment_uid;
2643 } DecodeAsyncHelper;
2646 on_decode_to_stream_async_handler (TnyMimePart *mime_part,
2652 DecodeAsyncHelper *helper = (DecodeAsyncHelper *) user_data;
2653 const gchar *content_type;
2655 if (cancelled || err) {
2658 if ((err->domain == TNY_ERROR_DOMAIN) &&
2659 (err->code == TNY_IO_ERROR_WRITE) &&
2660 (errno == ENOSPC)) {
2661 msg = g_strdup_printf (_KR("cerm_device_memory_full"), "");
2663 msg = g_strdup (_("mail_ib_file_operation_failed"));
2665 modest_platform_information_banner (NULL, NULL, msg);
2671 /* It could happen that the window was closed. So we
2672 assume it is a cancelation */
2673 if (!GTK_WIDGET_VISIBLE (helper->self))
2676 /* Remove the progress hint */
2677 set_progress_hint (helper->self, FALSE);
2679 content_type = tny_mime_part_get_content_type (mime_part);
2680 if (g_str_has_prefix (content_type, "message/rfc822")) {
2681 ModestWindowMgr *mgr;
2682 ModestWindow *msg_win = NULL;
2685 const gchar *mailbox;
2686 TnyStream *file_stream;
2689 fd = g_open (helper->file_path, O_RDONLY, 0644);
2691 file_stream = tny_fs_stream_new (fd);
2693 mgr = modest_runtime_get_window_mgr ();
2695 account = g_strdup (modest_window_get_active_account (MODEST_WINDOW (helper->self)));
2696 mailbox = modest_window_get_active_mailbox (MODEST_WINDOW (helper->self));
2699 account = modest_account_mgr_get_default_account (modest_runtime_get_account_mgr ());
2701 msg = tny_camel_msg_new ();
2702 tny_camel_msg_parse (msg, file_stream);
2703 msg_win = modest_msg_view_window_new_for_attachment (TNY_MSG (msg), account, mailbox, helper->attachment_uid);
2704 modest_window_set_zoom (MODEST_WINDOW (msg_win),
2705 modest_window_get_zoom (MODEST_WINDOW (helper->self)));
2706 if (modest_window_mgr_register_window (mgr, msg_win, MODEST_WINDOW (helper->self)))
2707 gtk_widget_show_all (GTK_WIDGET (msg_win));
2709 gtk_widget_destroy (GTK_WIDGET (msg_win));
2710 g_object_unref (msg);
2711 g_object_unref (file_stream);
2713 modest_platform_information_banner (NULL, NULL, _("mail_ib_file_operation_failed"));
2718 /* make the file read-only */
2719 g_chmod(helper->file_path, 0444);
2721 /* Activate the file */
2722 modest_platform_activate_file (helper->file_path, tny_mime_part_get_content_type (mime_part));
2727 g_object_unref (helper->self);
2728 g_free (helper->file_path);
2729 g_free (helper->attachment_uid);
2730 g_slice_free (DecodeAsyncHelper, helper);
2734 modest_msg_view_window_view_attachment (ModestMsgViewWindow *window,
2735 TnyMimePart *mime_part)
2737 ModestMsgViewWindowPrivate *priv;
2738 const gchar *msg_uid;
2739 gchar *attachment_uid = NULL;
2740 gint attachment_index = 0;
2741 TnyList *attachments;
2743 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window));
2744 g_return_if_fail (TNY_IS_MIME_PART (mime_part) || (mime_part == NULL));
2745 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
2747 msg_uid = modest_msg_view_window_get_message_uid (MODEST_MSG_VIEW_WINDOW (window));
2748 attachments = modest_msg_view_get_attachments (MODEST_MSG_VIEW (priv->msg_view));
2749 attachment_index = modest_list_index (attachments, (GObject *) mime_part);
2750 g_object_unref (attachments);
2752 if (msg_uid && attachment_index >= 0) {
2753 attachment_uid = g_strdup_printf ("%s/%d", msg_uid, attachment_index);
2756 if (mime_part == NULL) {
2757 gboolean error = FALSE;
2758 TnyList *selected_attachments = modest_msg_view_get_selected_attachments (MODEST_MSG_VIEW (priv->msg_view));
2759 if (selected_attachments == NULL || tny_list_get_length (selected_attachments) == 0) {
2761 } else if (tny_list_get_length (selected_attachments) > 1) {
2762 modest_platform_system_banner (NULL, NULL, _("mcen_ib_unable_to_display_more"));
2766 iter = tny_list_create_iterator (selected_attachments);
2767 mime_part = (TnyMimePart *) tny_iterator_get_current (iter);
2768 g_object_unref (iter);
2770 if (selected_attachments)
2771 g_object_unref (selected_attachments);
2776 g_object_ref (mime_part);
2779 if (tny_mime_part_is_purged (mime_part))
2782 if (!modest_tny_mime_part_is_msg (mime_part) && tny_mime_part_get_filename (mime_part)) {
2783 gchar *filepath = NULL;
2784 const gchar *att_filename = tny_mime_part_get_filename (mime_part);
2785 gboolean show_error_banner = FALSE;
2786 TnyFsStream *temp_stream = NULL;
2787 temp_stream = modest_utils_create_temp_stream (att_filename, attachment_uid,
2790 if (temp_stream != NULL) {
2791 ModestAccountMgr *mgr;
2792 DecodeAsyncHelper *helper;
2793 gboolean decode_in_provider;
2794 ModestProtocol *protocol;
2795 const gchar *account;
2797 /* Activate progress hint */
2798 set_progress_hint (window, TRUE);
2800 helper = g_slice_new0 (DecodeAsyncHelper);
2801 helper->self = g_object_ref (window);
2802 helper->file_path = g_strdup (filepath);
2803 helper->attachment_uid = g_strdup (attachment_uid);
2805 decode_in_provider = FALSE;
2806 mgr = modest_runtime_get_account_mgr ();
2807 account = modest_window_get_active_account (MODEST_WINDOW (window));
2808 if (modest_account_mgr_account_is_multimailbox (mgr, account, &protocol)) {
2809 if (MODEST_IS_ACCOUNT_PROTOCOL (protocol)) {
2811 uri = g_strconcat ("file://", filepath, NULL);
2812 decode_in_provider =
2813 modest_account_protocol_decode_part_to_stream_async (
2814 MODEST_ACCOUNT_PROTOCOL (protocol),
2817 TNY_STREAM (temp_stream),
2818 on_decode_to_stream_async_handler,
2825 if (!decode_in_provider)
2826 tny_mime_part_decode_to_stream_async (mime_part, TNY_STREAM (temp_stream),
2827 on_decode_to_stream_async_handler,
2830 g_object_unref (temp_stream);
2831 /* NOTE: files in the temporary area will be automatically
2832 * cleaned after some time if they are no longer in use */
2835 const gchar *content_type;
2836 /* the file may already exist but it isn't writable,
2837 * let's try to open it anyway */
2838 content_type = tny_mime_part_get_content_type (mime_part);
2839 modest_platform_activate_file (filepath, content_type);
2841 g_warning ("%s: modest_utils_create_temp_stream failed", __FUNCTION__);
2842 show_error_banner = TRUE;
2847 if (show_error_banner)
2848 modest_platform_information_banner (NULL, NULL, _("mail_ib_file_operation_failed"));
2849 } else if (!modest_tny_mime_part_is_msg (mime_part)) {
2850 ModestWindowMgr *mgr;
2851 ModestWindow *msg_win = NULL;
2852 TnyMsg *current_msg;
2856 current_msg = modest_msg_view_window_get_message (MODEST_MSG_VIEW_WINDOW (window));
2857 mgr = modest_runtime_get_window_mgr ();
2858 header = tny_msg_get_header (TNY_MSG (current_msg));
2859 found = modest_window_mgr_find_registered_message_uid (mgr,
2864 g_debug ("window for this body is already being created");
2867 /* it's not found, so create a new window for it */
2868 modest_window_mgr_register_header (mgr, header, attachment_uid); /* register the uid before building the window */
2869 gchar *account = g_strdup (modest_window_get_active_account (MODEST_WINDOW (window)));
2870 const gchar *mailbox = modest_window_get_active_mailbox (MODEST_WINDOW (window));
2872 account = modest_account_mgr_get_default_account (modest_runtime_get_account_mgr ());
2874 msg_win = modest_msg_view_window_new_with_other_body (TNY_MSG (current_msg), TNY_MIME_PART (mime_part),
2875 account, mailbox, attachment_uid);
2877 modest_window_set_zoom (MODEST_WINDOW (msg_win),
2878 modest_window_get_zoom (MODEST_WINDOW (window)));
2879 if (modest_window_mgr_register_window (mgr, msg_win, MODEST_WINDOW (window)))
2880 gtk_widget_show_all (GTK_WIDGET (msg_win));
2882 gtk_widget_destroy (GTK_WIDGET (msg_win));
2884 g_object_unref (current_msg);
2886 /* message attachment */
2887 TnyHeader *header = NULL;
2888 ModestWindowMgr *mgr;
2889 ModestWindow *msg_win = NULL;
2892 header = tny_msg_get_header (TNY_MSG (mime_part));
2893 mgr = modest_runtime_get_window_mgr ();
2894 found = modest_window_mgr_find_registered_header (mgr, header, &msg_win);
2897 /* if it's found, but there is no msg_win, it's probably in the process of being created;
2898 * thus, we don't do anything */
2899 g_debug ("window for is already being created");
2901 /* it's not found, so create a new window for it */
2902 modest_window_mgr_register_header (mgr, header, attachment_uid); /* register the uid before building the window */
2903 gchar *account = g_strdup (modest_window_get_active_account (MODEST_WINDOW (window)));
2904 const gchar *mailbox = modest_window_get_active_mailbox (MODEST_WINDOW (window));
2906 account = modest_account_mgr_get_default_account (modest_runtime_get_account_mgr ());
2907 msg_win = modest_msg_view_window_new_for_attachment (TNY_MSG (mime_part), account,
2908 mailbox, attachment_uid);
2909 modest_window_set_zoom (MODEST_WINDOW (msg_win),
2910 modest_window_get_zoom (MODEST_WINDOW (window)));
2911 if (modest_window_mgr_register_window (mgr, msg_win, MODEST_WINDOW (window)))
2912 gtk_widget_show_all (GTK_WIDGET (msg_win));
2914 gtk_widget_destroy (GTK_WIDGET (msg_win));
2920 g_free (attachment_uid);
2922 g_object_unref (mime_part);
2934 GnomeVFSResult result;
2936 ModestMsgViewWindow *window;
2939 static void save_mime_part_info_free (SaveMimePartInfo *info, gboolean with_struct);
2940 static gboolean idle_save_mime_part_show_result (SaveMimePartInfo *info);
2941 static gpointer save_mime_part_to_file (SaveMimePartInfo *info);
2942 static void save_mime_parts_to_file_with_checks (GtkWindow *parent, SaveMimePartInfo *info);
2945 save_mime_part_info_free (SaveMimePartInfo *info, gboolean with_struct)
2949 for (node = info->pairs; node != NULL; node = g_list_next (node)) {
2950 SaveMimePartPair *pair = (SaveMimePartPair *) node->data;
2951 g_free (pair->filename);
2952 g_object_unref (pair->part);
2953 g_slice_free (SaveMimePartPair, pair);
2955 g_list_free (info->pairs);
2958 g_object_unref (info->window);
2959 info->window = NULL;
2961 g_slice_free (SaveMimePartInfo, info);
2966 idle_save_mime_part_show_result (SaveMimePartInfo *info)
2968 /* This is a GDK lock because we are an idle callback and
2969 * modest_platform_system_banner is or does Gtk+ code */
2971 gdk_threads_enter (); /* CHECKED */
2972 if (info->result == GNOME_VFS_OK) {
2973 modest_platform_system_banner (NULL, NULL, _CS("sfil_ib_saved"));
2974 } else if (info->result == GNOME_VFS_ERROR_NO_SPACE) {
2977 /* Check if the uri belongs to the external mmc */
2978 if (g_str_has_prefix (info->uri, g_getenv (MODEST_MMC1_VOLUMEPATH_ENV)))
2979 msg = g_strdup_printf (_KR("cerm_device_memory_full"), "");
2981 msg = g_strdup (_KR("cerm_memory_card_full"));
2982 modest_platform_information_banner (NULL, NULL, msg);
2985 modest_platform_system_banner (NULL, NULL, _("mail_ib_file_operation_failed"));
2987 save_mime_part_info_free (info, FALSE);
2988 gdk_threads_leave (); /* CHECKED */
2994 save_mime_part_to_file (SaveMimePartInfo *info)
2996 GnomeVFSHandle *handle;
2998 SaveMimePartPair *pair = (SaveMimePartPair *) info->pairs->data;
3000 info->result = gnome_vfs_create (&handle, pair->filename, GNOME_VFS_OPEN_WRITE, FALSE, 0644);
3001 if (info->result == GNOME_VFS_OK) {
3002 GError *error = NULL;
3003 gboolean decode_in_provider;
3005 ModestAccountMgr *mgr;
3006 const gchar *account;
3007 ModestProtocol *protocol = NULL;
3009 stream = tny_vfs_stream_new (handle);
3011 decode_in_provider = FALSE;
3012 mgr = modest_runtime_get_account_mgr ();
3013 account = modest_window_get_active_account (MODEST_WINDOW (info->window));
3014 if (modest_account_mgr_account_is_multimailbox (mgr, account, &protocol)) {
3015 if (MODEST_IS_ACCOUNT_PROTOCOL (protocol)) {
3016 decode_in_provider =
3017 modest_account_protocol_decode_part_to_stream (
3018 MODEST_ACCOUNT_PROTOCOL (protocol),
3026 if (!decode_in_provider)
3027 written = tny_mime_part_decode_to_stream (pair->part, stream, &error);
3030 g_warning ("modest: could not save attachment %s: %d (%s)\n", pair->filename, error?error->code:-1, error?error->message:"Unknown error");
3032 if ((error->domain == TNY_ERROR_DOMAIN) &&
3033 (error->code == TNY_IO_ERROR_WRITE) &&
3034 (errno == ENOSPC)) {
3035 info->result = GNOME_VFS_ERROR_NO_SPACE;
3037 info->result = GNOME_VFS_ERROR_IO;
3040 g_object_unref (G_OBJECT (stream));
3042 g_warning ("Could not create save attachment %s: %s\n",
3043 pair->filename, gnome_vfs_result_to_string (info->result));
3046 /* Go on saving remaining files */
3047 info->pairs = g_list_remove_link (info->pairs, info->pairs);
3048 if (info->pairs != NULL) {
3049 save_mime_part_to_file (info);
3051 g_idle_add ((GSourceFunc) idle_save_mime_part_show_result, info);
3058 save_mime_parts_to_file_with_checks (GtkWindow *parent,
3059 SaveMimePartInfo *info)
3061 gboolean is_ok = TRUE;
3062 gint replaced_files = 0;
3063 const GList *files = info->pairs;
3064 const GList *iter, *to_replace = NULL;
3066 for (iter = files; (iter != NULL) && (replaced_files < 2); iter = g_list_next(iter)) {
3067 SaveMimePartPair *pair = iter->data;
3068 gchar *unescaped = g_uri_unescape_string (pair->filename, NULL);
3070 if (modest_utils_file_exists (unescaped)) {
3072 if (replaced_files == 1)
3077 if (replaced_files) {
3080 if (replaced_files == 1) {
3081 SaveMimePartPair *pair = to_replace->data;
3082 const gchar *basename = strrchr (pair->filename, G_DIR_SEPARATOR) + 1;
3083 gchar *escaped_basename, *message;
3085 escaped_basename = g_uri_unescape_string (basename, NULL);
3086 message = g_strdup_printf ("%s\n%s",
3087 _FM("docm_nc_replace_file"),
3088 (escaped_basename) ? escaped_basename : "");
3089 response = modest_platform_run_confirmation_dialog (parent, message);
3091 g_free (escaped_basename);
3093 response = modest_platform_run_confirmation_dialog (parent,
3094 _FM("docm_nc_replace_multiple"));
3096 if (response != GTK_RESPONSE_OK)
3101 save_mime_part_info_free (info, TRUE);
3103 g_thread_create ((GThreadFunc)save_mime_part_to_file, info, FALSE, NULL);
3108 typedef struct _SaveAttachmentsInfo {
3109 TnyList *attachments_list;
3110 ModestMsgViewWindow *window;
3111 } SaveAttachmentsInfo;
3114 save_attachments_response (GtkDialog *dialog,
3118 TnyList *mime_parts;
3120 GList *files_to_save = NULL;
3121 gchar *current_folder;
3122 SaveAttachmentsInfo *sa_info = (SaveAttachmentsInfo *) user_data;
3124 mime_parts = TNY_LIST (sa_info->attachments_list);
3126 if (arg1 != GTK_RESPONSE_OK)
3129 chooser_uri = gtk_file_chooser_get_uri (GTK_FILE_CHOOSER (dialog));
3130 current_folder = gtk_file_chooser_get_current_folder_uri (GTK_FILE_CHOOSER (dialog));
3131 if (current_folder && *current_folder != '\0') {
3133 modest_conf_set_string (modest_runtime_get_conf (), MODEST_CONF_LATEST_SAVE_ATTACHMENT_PATH,
3134 current_folder,&err);
3136 g_debug ("Error storing latest used folder: %s", err->message);
3140 g_free (current_folder);
3142 if (!modest_utils_folder_writable (chooser_uri)) {
3143 const gchar *err_msg;
3145 #ifdef MODEST_PLATFORM_MAEMO
3146 if (modest_maemo_utils_in_usb_mode ()) {
3147 err_msg = dgettext ("hildon-status-bar-usb", "usbh_ib_mmc_usb_connected");
3149 err_msg = _FM("sfil_ib_readonly_location");
3152 err_msg = _FM("sfil_ib_readonly_location");
3154 modest_platform_system_banner (NULL, NULL, err_msg);
3158 iter = tny_list_create_iterator (mime_parts);
3159 while (!tny_iterator_is_done (iter)) {
3160 TnyMimePart *mime_part = (TnyMimePart *) tny_iterator_get_current (iter);
3162 if ((modest_tny_mime_part_is_attachment_for_modest (mime_part)) &&
3163 !tny_mime_part_is_purged (mime_part) &&
3164 (tny_mime_part_get_filename (mime_part) != NULL)) {
3165 SaveMimePartPair *pair;
3167 pair = g_slice_new0 (SaveMimePartPair);
3169 if (tny_list_get_length (mime_parts) > 1) {
3171 gnome_vfs_escape_slashes (tny_mime_part_get_filename (mime_part));
3172 pair->filename = g_build_filename (chooser_uri, escaped, NULL);
3175 pair->filename = g_strdup (chooser_uri);
3177 pair->part = mime_part;
3178 files_to_save = g_list_prepend (files_to_save, pair);
3180 tny_iterator_next (iter);
3182 g_object_unref (iter);
3185 if (files_to_save != NULL) {
3186 SaveMimePartInfo *info = g_slice_new0 (SaveMimePartInfo);
3187 info->pairs = files_to_save;
3188 info->result = TRUE;
3189 info->uri = g_strdup (chooser_uri);
3190 info->window = g_object_ref (sa_info->window);
3191 save_mime_parts_to_file_with_checks ((GtkWindow *) dialog, info);
3193 g_free (chooser_uri);
3196 /* Free and close the dialog */
3197 g_object_unref (mime_parts);
3198 g_object_unref (sa_info->window);
3199 g_slice_free (SaveAttachmentsInfo, sa_info);
3200 gtk_widget_destroy (GTK_WIDGET (dialog));
3204 modest_msg_view_window_save_attachments (ModestMsgViewWindow *window,
3205 TnyList *mime_parts)
3207 ModestMsgViewWindowPrivate *priv;
3208 GtkWidget *save_dialog = NULL;
3209 gchar *conf_folder = NULL;
3210 gchar *filename = NULL;
3211 gchar *save_multiple_str = NULL;
3212 const gchar *root_folder = "file:///";
3214 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window));
3215 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
3217 if (mime_parts == NULL) {
3218 /* In Hildon 2.2 save and delete operate over all the attachments as there's no
3219 * selection available */
3220 mime_parts = modest_msg_view_get_attachments (MODEST_MSG_VIEW (priv->msg_view));
3221 if (mime_parts && !modest_toolkit_utils_select_attachments (GTK_WINDOW (window), mime_parts, FALSE)) {
3222 g_object_unref (mime_parts);
3225 if (mime_parts == NULL || tny_list_get_length (mime_parts) == 0) {
3227 g_object_unref (mime_parts);
3233 g_object_ref (mime_parts);
3236 /* prepare dialog */
3237 if (tny_list_get_length (mime_parts) == 1) {
3239 /* only one attachment selected */
3240 iter = tny_list_create_iterator (mime_parts);
3241 TnyMimePart *mime_part = (TnyMimePart *) tny_iterator_get_current (iter);
3242 g_object_unref (iter);
3243 if (!modest_tny_mime_part_is_msg (mime_part) &&
3244 modest_tny_mime_part_is_attachment_for_modest (mime_part) &&
3245 !tny_mime_part_is_purged (mime_part)) {
3246 filename = g_strdup (tny_mime_part_get_filename (mime_part));
3248 /* TODO: show any error? */
3249 g_warning ("%s: Tried to save a non-file attachment", __FUNCTION__);
3250 g_object_unref (mime_parts);
3253 g_object_unref (mime_part);
3255 gint num = tny_list_get_length (mime_parts);
3256 save_multiple_str = g_strdup_printf (dngettext("hildon-fm",
3257 "sfil_va_number_of_objects_attachment",
3258 "sfil_va_number_of_objects_attachments",
3262 /* Creation of hildon file chooser dialog for saving */
3263 save_dialog = modest_toolkit_factory_create_file_chooser_dialog (modest_runtime_get_toolkit_factory (),
3265 (GtkWindow *) window,
3266 GTK_FILE_CHOOSER_ACTION_SAVE);
3268 /* Get last used folder */
3269 conf_folder = modest_conf_get_string (modest_runtime_get_conf (),
3270 MODEST_CONF_LATEST_SAVE_ATTACHMENT_PATH, NULL);
3272 /* File chooser stops working if we select "file:///" as current folder */
3273 if (conf_folder && g_ascii_strcasecmp (root_folder, conf_folder) != 0) {
3274 g_free (conf_folder);
3278 if (conf_folder && conf_folder[0] != '\0') {
3279 gtk_file_chooser_set_current_folder_uri (GTK_FILE_CHOOSER (save_dialog), conf_folder);
3282 /* Set the default folder to documents folder */
3283 docs_folder = (gchar *) g_strdup(g_get_user_special_dir (G_USER_DIRECTORY_DOCUMENTS));
3286 docs_folder = g_build_filename (g_getenv (MYDOCS_ENV), DOCS_FOLDER, NULL);
3288 gtk_file_chooser_set_current_folder (GTK_FILE_CHOOSER (save_dialog), docs_folder);
3289 g_free (docs_folder);
3291 g_free (conf_folder);
3295 gtk_file_chooser_set_current_name (GTK_FILE_CHOOSER (save_dialog),
3300 /* if multiple, set multiple string */
3301 if (save_multiple_str) {
3302 g_object_set (G_OBJECT (save_dialog), "save-multiple", save_multiple_str, NULL);
3303 gtk_window_set_title (GTK_WINDOW (save_dialog), _FM("sfil_ti_save_objects_files"));
3304 g_free (save_multiple_str);
3307 /* We must run this asynchronously, because the hildon dialog
3308 performs a gtk_dialog_run by itself which leads to gdk
3310 SaveAttachmentsInfo *sa_info;
3311 sa_info = g_slice_new (SaveAttachmentsInfo);
3312 sa_info->attachments_list = mime_parts;
3313 sa_info->window = g_object_ref (window);
3314 g_signal_connect (save_dialog, "response",
3315 G_CALLBACK (save_attachments_response), sa_info);
3317 gtk_widget_show_all (save_dialog);
3321 show_remove_attachment_information (gpointer userdata)
3323 ModestMsgViewWindow *window = (ModestMsgViewWindow *) userdata;
3324 ModestMsgViewWindowPrivate *priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
3326 /* We're outside the main lock */
3327 gdk_threads_enter ();
3329 if (priv->remove_attachment_banner != NULL) {
3330 gtk_widget_destroy (priv->remove_attachment_banner);
3331 g_object_unref (priv->remove_attachment_banner);
3334 priv->remove_attachment_banner = g_object_ref (
3335 modest_platform_animation_banner (NULL, NULL, _("mcen_me_inbox_remove_attachments")));
3337 gdk_threads_leave ();
3343 modest_msg_view_window_remove_attachments (ModestMsgViewWindow *window, gboolean get_all)
3345 ModestMsgViewWindowPrivate *priv;
3346 TnyList *mime_parts = NULL, *tmp;
3347 gchar *confirmation_message;
3353 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window));
3354 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
3356 /* In hildon 2.2 we ignore the get_all flag as we always get all attachments. This is
3357 * because we don't have selection
3359 mime_parts = modest_msg_view_get_attachments (MODEST_MSG_VIEW (priv->msg_view));
3361 /* Remove already purged messages from mime parts list. We use
3362 a copy of the list to remove items in the original one */
3363 tmp = tny_list_copy (mime_parts);
3364 iter = tny_list_create_iterator (tmp);
3365 while (!tny_iterator_is_done (iter)) {
3366 TnyMimePart *part = TNY_MIME_PART (tny_iterator_get_current (iter));
3367 if (tny_mime_part_is_purged (part))
3368 tny_list_remove (mime_parts, (GObject *) part);
3370 g_object_unref (part);
3371 tny_iterator_next (iter);
3373 g_object_unref (tmp);
3374 g_object_unref (iter);
3376 if (!modest_toolkit_utils_select_attachments (GTK_WINDOW (window), mime_parts, TRUE) ||
3377 tny_list_get_length (mime_parts) == 0) {
3378 g_object_unref (mime_parts);
3382 n_attachments = tny_list_get_length (mime_parts);
3383 if (n_attachments == 1) {
3387 iter = tny_list_create_iterator (mime_parts);
3388 part = (TnyMimePart *) tny_iterator_get_current (iter);
3389 g_object_unref (iter);
3390 if (modest_tny_mime_part_is_msg (part)) {
3392 header = tny_msg_get_header (TNY_MSG (part));
3393 filename = tny_header_dup_subject (header);
3394 g_object_unref (header);
3395 if (filename == NULL)
3396 filename = g_strdup (_("mail_va_no_subject"));
3398 filename = g_strdup (tny_mime_part_get_filename (TNY_MIME_PART (part)));
3400 confirmation_message = g_strdup_printf (_("mcen_nc_purge_file_text"), filename);
3402 g_object_unref (part);
3404 confirmation_message = g_strdup_printf (ngettext("mcen_nc_purge_file_text",
3405 "mcen_nc_purge_files_text",
3406 n_attachments), n_attachments);
3408 response = modest_platform_run_confirmation_dialog (GTK_WINDOW (window),
3409 confirmation_message);
3410 g_free (confirmation_message);
3412 if (response != GTK_RESPONSE_OK) {
3413 g_object_unref (mime_parts);
3417 priv->purge_timeout = g_timeout_add (2000, show_remove_attachment_information, window);
3419 iter = tny_list_create_iterator (mime_parts);
3420 while (!tny_iterator_is_done (iter)) {
3423 part = (TnyMimePart *) tny_iterator_get_current (iter);
3424 tny_mime_part_set_purged (TNY_MIME_PART (part));
3425 g_object_unref (part);
3426 tny_iterator_next (iter);
3428 g_object_unref (iter);
3430 msg = tny_msg_view_get_msg (TNY_MSG_VIEW (priv->msg_view));
3431 tny_msg_view_clear (TNY_MSG_VIEW (priv->msg_view));
3432 tny_msg_rewrite_cache (msg);
3433 tny_msg_view_set_msg (TNY_MSG_VIEW (priv->msg_view), msg);
3434 g_object_unref (msg);
3435 update_branding (MODEST_MSG_VIEW_WINDOW (window));
3437 g_object_unref (mime_parts);
3439 if (priv->purge_timeout > 0) {
3440 g_source_remove (priv->purge_timeout);
3441 priv->purge_timeout = 0;
3444 if (priv->remove_attachment_banner) {
3445 gtk_widget_destroy (priv->remove_attachment_banner);
3446 g_object_unref (priv->remove_attachment_banner);
3447 priv->remove_attachment_banner = NULL;
3453 update_window_title (ModestMsgViewWindow *window)
3455 ModestMsgViewWindowPrivate *priv;
3457 TnyHeader *header = NULL;
3458 gchar *subject = NULL;
3460 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
3462 /* Note that if the window is closed while we're retrieving
3463 the message, this widget could de deleted */
3464 if (!priv->msg_view)
3467 msg = tny_msg_view_get_msg (TNY_MSG_VIEW (priv->msg_view));
3469 if (priv->other_body) {
3472 description = modest_tny_mime_part_get_header_value (priv->other_body, "Content-Description");
3474 g_strstrip (description);
3475 subject = description;
3477 } else if (msg != NULL) {
3478 header = tny_msg_get_header (msg);
3479 subject = tny_header_dup_subject (header);
3480 g_object_unref (header);
3481 g_object_unref (msg);
3484 if ((subject == NULL)||(subject[0] == '\0')) {
3486 subject = g_strdup (_("mail_va_no_subject"));
3489 modest_window_set_title (MODEST_WINDOW (window), subject);
3494 on_move_focus (GtkWidget *widget,
3495 GtkDirectionType direction,
3498 g_signal_stop_emission_by_name (G_OBJECT (widget), "move-focus");
3502 fetch_image_open_stream (TnyStreamCache *self, gint64 *expected_size, gchar *uri)
3504 GnomeVFSResult result;
3505 GnomeVFSHandle *handle = NULL;
3506 GnomeVFSFileInfo *info = NULL;
3509 result = gnome_vfs_open (&handle, uri, GNOME_VFS_OPEN_READ);
3510 if (result != GNOME_VFS_OK) {
3515 info = gnome_vfs_file_info_new ();
3516 result = gnome_vfs_get_file_info_from_handle (handle, info, GNOME_VFS_FILE_INFO_DEFAULT);
3517 if (result != GNOME_VFS_OK || ! (info->valid_fields & GNOME_VFS_FILE_INFO_FIELDS_SIZE)) {
3518 /* We put a "safe" default size for going to cache */
3519 *expected_size = (300*1024);
3521 *expected_size = info->size;
3523 gnome_vfs_file_info_unref (info);
3525 stream = tny_vfs_stream_new (handle);
3534 TnyStream *output_stream;
3535 GtkWidget *msg_view;
3540 on_fetch_image_idle_refresh_view (gpointer userdata)
3543 FetchImageData *fidata = (FetchImageData *) userdata;
3545 gdk_threads_enter ();
3546 if (GTK_WIDGET_DRAWABLE (fidata->msg_view)) {
3547 ModestMsgViewWindowPrivate *priv;
3549 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (fidata->window);
3550 priv->fetching_images--;
3551 gtk_widget_queue_draw (fidata->msg_view);
3552 update_progress_hint (MODEST_MSG_VIEW_WINDOW (fidata->window));
3554 gdk_threads_leave ();
3556 g_object_unref (fidata->msg_view);
3557 g_object_unref (fidata->window);
3558 g_slice_free (FetchImageData, fidata);
3563 on_fetch_image_thread (gpointer userdata)
3565 FetchImageData *fidata = (FetchImageData *) userdata;
3566 TnyStreamCache *cache;
3567 TnyStream *cache_stream;
3569 cache = modest_runtime_get_images_cache ();
3571 tny_stream_cache_get_stream (cache,
3573 (TnyStreamCacheOpenStreamFetcher) fetch_image_open_stream,
3574 (gpointer) fidata->uri);
3575 g_free (fidata->cache_id);
3576 g_free (fidata->uri);
3578 if (cache_stream != NULL) {
3581 while (G_LIKELY (!tny_stream_is_eos (cache_stream))) {
3584 nb_read = tny_stream_read (cache_stream, buffer, sizeof (buffer));
3585 if (G_UNLIKELY (nb_read < 0)) {
3587 } else if (G_LIKELY (nb_read > 0)) {
3588 gssize nb_written = 0;
3590 while (G_UNLIKELY (nb_written < nb_read)) {
3593 len = tny_stream_write (fidata->output_stream, buffer + nb_written,
3594 nb_read - nb_written);
3595 if (G_UNLIKELY (len < 0))
3601 tny_stream_close (cache_stream);
3602 g_object_unref (cache_stream);
3605 tny_stream_close (fidata->output_stream);
3606 g_object_unref (fidata->output_stream);
3608 g_idle_add (on_fetch_image_idle_refresh_view, fidata);
3614 on_fetch_image (ModestMsgView *msgview,
3617 ModestMsgViewWindow *window)
3619 const gchar *current_account;
3620 ModestMsgViewWindowPrivate *priv;
3621 FetchImageData *fidata;
3623 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
3625 current_account = modest_window_get_active_account (MODEST_WINDOW (window));
3627 fidata = g_slice_new0 (FetchImageData);
3628 fidata->msg_view = g_object_ref (msgview);
3629 fidata->window = g_object_ref (window);
3630 fidata->uri = g_strdup (uri);
3631 fidata->cache_id = modest_images_cache_get_id (current_account, uri);
3632 fidata->output_stream = g_object_ref (stream);
3634 priv->fetching_images++;
3635 if (g_thread_create (on_fetch_image_thread, fidata, FALSE, NULL) == NULL) {
3636 g_object_unref (fidata->output_stream);
3637 g_free (fidata->cache_id);
3638 g_free (fidata->uri);
3639 g_object_unref (fidata->msg_view);
3640 g_slice_free (FetchImageData, fidata);
3641 tny_stream_close (stream);
3642 priv->fetching_images--;
3643 update_progress_hint (window);
3646 update_progress_hint (window);
3652 setup_menu (ModestMsgViewWindow *self)
3654 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW(self));
3656 /* Settings menu buttons */
3657 modest_window_add_to_menu (MODEST_WINDOW (self), _("mcen_me_viewer_find"), NULL,
3658 MODEST_WINDOW_MENU_CALLBACK (modest_msg_view_window_show_isearch_toolbar),
3659 MODEST_DIMMING_CALLBACK (modest_ui_dimming_rules_on_find_in_msg));
3661 modest_window_add_to_menu (MODEST_WINDOW (self),
3662 dngettext(GETTEXT_PACKAGE,
3663 "mcen_me_move_message",
3664 "mcen_me_move_messages",
3667 MODEST_WINDOW_MENU_CALLBACK (modest_ui_actions_on_move_to),
3668 MODEST_DIMMING_CALLBACK (modest_ui_dimming_rules_on_move_to));
3670 modest_window_add_to_menu (MODEST_WINDOW (self), _("mcen_me_inbox_mark_as_read"), NULL,
3671 MODEST_WINDOW_MENU_CALLBACK (modest_ui_actions_on_mark_as_read),
3672 MODEST_DIMMING_CALLBACK (modest_ui_dimming_rules_on_mark_as_read_msg_in_view));
3674 modest_window_add_to_menu (MODEST_WINDOW (self), _("mcen_me_inbox_mark_as_unread"), NULL,
3675 MODEST_WINDOW_MENU_CALLBACK (modest_ui_actions_on_mark_as_unread),
3676 MODEST_DIMMING_CALLBACK (modest_ui_dimming_rules_on_mark_as_unread_msg_in_view));
3678 modest_window_add_to_menu (MODEST_WINDOW (self), _("mcen_me_viewer_save_attachments"), NULL,
3679 MODEST_WINDOW_MENU_CALLBACK (modest_ui_actions_save_attachments),
3680 MODEST_DIMMING_CALLBACK (modest_ui_dimming_rules_on_save_attachments));
3681 modest_window_add_to_menu (MODEST_WINDOW (self), _("mcen_me_inbox_remove_attachments"), NULL,
3682 MODEST_WINDOW_MENU_CALLBACK (modest_ui_actions_remove_attachments),
3683 MODEST_DIMMING_CALLBACK (modest_ui_dimming_rules_on_remove_attachments));
3685 modest_window_add_to_menu (MODEST_WINDOW (self), _("mcen_me_new_message"), "<Control>n",
3686 MODEST_WINDOW_MENU_CALLBACK (modest_ui_actions_on_new_msg),
3687 MODEST_DIMMING_CALLBACK (modest_ui_dimming_rules_on_new_msg));
3688 modest_window_add_to_menu (MODEST_WINDOW (self), _("mcen_me_viewer_addtocontacts"), NULL,
3689 MODEST_WINDOW_MENU_CALLBACK (modest_ui_actions_add_to_contacts),
3690 MODEST_DIMMING_CALLBACK (modest_ui_dimming_rules_on_add_to_contacts));
3692 modest_window_add_to_menu (MODEST_WINDOW (self), _("mcen_ti_message_properties"), NULL,
3693 MODEST_WINDOW_MENU_CALLBACK (modest_ui_actions_on_details),
3694 MODEST_DIMMING_CALLBACK (modest_ui_dimming_rules_on_details));
3698 modest_msg_view_window_add_to_contacts (ModestMsgViewWindow *self)
3700 ModestMsgViewWindowPrivate *priv;
3701 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
3702 GSList *recipients = NULL;
3705 msg = tny_msg_view_get_msg (TNY_MSG_VIEW (priv->msg_view));
3709 header = modest_msg_view_window_get_header (self);
3712 recipients = modest_tny_msg_header_get_all_recipients_list (header);
3713 g_object_unref (header);
3715 recipients = modest_tny_msg_get_all_recipients_list (msg);
3716 g_object_unref (msg);
3720 /* Offer the user to add recipients to the address book */
3721 modest_address_book_add_address_list_with_selector (recipients, (GtkWindow *) self);
3722 g_slist_foreach (recipients, (GFunc) g_free, NULL); g_slist_free (recipients);
3727 _modest_msg_view_window_map_event (GtkWidget *widget,
3731 ModestMsgViewWindow *self = (ModestMsgViewWindow *) userdata;
3733 update_progress_hint (self);
3739 modest_msg_view_window_fetch_images (ModestMsgViewWindow *self)
3741 ModestMsgViewWindowPrivate *priv;
3742 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
3744 modest_msg_view_request_fetch_images (MODEST_MSG_VIEW (priv->msg_view));
3748 modest_msg_view_window_has_blocked_external_images (ModestMsgViewWindow *self)
3750 ModestMsgViewWindowPrivate *priv;
3751 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
3753 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self), FALSE);
3755 return modest_msg_view_has_blocked_external_images (MODEST_MSG_VIEW (priv->msg_view));
3759 modest_msg_view_window_reload (ModestMsgViewWindow *self)
3761 ModestMsgViewWindowPrivate *priv;
3762 const gchar *msg_uid;
3763 TnyHeader *header = NULL;
3764 TnyFolder *folder = NULL;
3766 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self));
3768 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
3770 header = modest_msg_view_window_get_header (MODEST_MSG_VIEW_WINDOW (self));
3774 folder = tny_header_get_folder (header);
3775 g_object_unref (header);
3780 msg_uid = modest_msg_view_window_get_message_uid (self);
3782 GtkTreeRowReference *row_reference;
3784 if (priv->row_reference && gtk_tree_row_reference_valid (priv->row_reference)) {
3785 row_reference = priv->row_reference;
3787 row_reference = NULL;
3789 if (!message_reader (self, priv, NULL, msg_uid, folder, row_reference))
3790 g_warning ("Shouldn't happen, trying to reload a message failed");
3793 g_object_unref (folder);
3797 update_branding (ModestMsgViewWindow *self)
3799 const gchar *account;
3800 const gchar *mailbox;
3801 ModestAccountMgr *mgr;
3802 ModestProtocol *protocol = NULL;
3803 gchar *service_name = NULL;
3804 const GdkPixbuf *service_icon = NULL;
3805 ModestMsgViewWindowPrivate *priv;
3807 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
3809 account = modest_window_get_active_account (MODEST_WINDOW (self));
3810 mailbox = modest_window_get_active_mailbox (MODEST_WINDOW (self));
3812 mgr = modest_runtime_get_account_mgr ();
3814 if (modest_account_mgr_account_is_multimailbox (mgr, account, &protocol)) {
3815 if (MODEST_IS_ACCOUNT_PROTOCOL (protocol)) {
3816 service_name = modest_account_protocol_get_service_name (MODEST_ACCOUNT_PROTOCOL (protocol),
3818 service_icon = modest_account_protocol_get_service_icon (MODEST_ACCOUNT_PROTOCOL (protocol),
3819 account, mailbox, MODEST_ICON_SIZE_SMALL);
3823 modest_msg_view_set_branding (MODEST_MSG_VIEW (priv->msg_view), service_name, service_icon);
3824 g_free (service_name);
3828 sync_flags (ModestMsgViewWindow *self)
3830 TnyHeader *header = NULL;
3832 header = modest_msg_view_window_get_header (self);
3834 TnyMsg *msg = modest_msg_view_window_get_message (self);
3836 header = tny_msg_get_header (msg);
3837 g_object_unref (msg);
3842 TnyFolder *folder = tny_header_get_folder (header);
3845 ModestMailOperation *mail_op;
3847 /* Sync folder, we need this to save the seen flag */
3848 mail_op = modest_mail_operation_new (NULL);
3849 modest_mail_operation_queue_add (modest_runtime_get_mail_operation_queue (),
3851 modest_mail_operation_sync_folder (mail_op, folder, FALSE, NULL, NULL);
3852 g_object_unref (mail_op);
3853 g_object_unref (folder);
3855 g_object_unref (header);