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 #ifdef MODEST_TOOLKIT_HILDON2
79 #include <hildon/hildon.h>
81 #include <tny-camel-bs-mime-part.h>
82 #include <tny-camel-bs-msg.h>
84 #define MYDOCS_ENV "MYDOCSDIR"
85 #define DOCS_FOLDER ".documents"
87 typedef struct _ModestMsgViewWindowPrivate ModestMsgViewWindowPrivate;
88 struct _ModestMsgViewWindowPrivate {
91 GtkWidget *main_scroll;
92 GtkWidget *isearch_toolbar;
95 /* Progress observers */
96 GSList *progress_widgets;
99 GtkWidget *prev_toolitem;
100 GtkWidget *next_toolitem;
101 gboolean progress_hint;
102 gint fetching_images;
104 /* Optimized view enabled */
105 gboolean optimized_view;
107 /* Whether this was created via the *_new_for_search_result() function. */
108 gboolean is_search_result;
110 /* Whether the message is in outbox */
113 /* A reference to the @model of the header view
114 * to allow selecting previous/next messages,
115 * if the message is currently selected in the header view.
117 const gchar *header_folder_id;
118 GtkTreeModel *header_model;
119 GtkTreeRowReference *row_reference;
120 GtkTreeRowReference *next_row_reference;
122 gulong clipboard_change_handler;
123 gulong queue_change_handler;
124 gulong account_removed_handler;
125 gulong row_changed_handler;
126 gulong row_deleted_handler;
127 gulong row_inserted_handler;
128 gulong rows_reordered_handler;
131 GtkWidget *remove_attachment_banner;
134 TnyMimePart *other_body;
139 static void modest_msg_view_window_class_init (ModestMsgViewWindowClass *klass);
140 static void modest_msg_view_window_init (ModestMsgViewWindow *obj);
141 static void modest_header_view_observer_init (ModestHeaderViewObserverIface *iface_class);
142 static void modest_msg_view_window_finalize (GObject *obj);
143 static void modest_msg_view_window_show_isearch_toolbar (GtkWidget *obj, gpointer data);
144 static void modest_msg_view_window_isearch_toolbar_close (GtkWidget *widget,
145 ModestMsgViewWindow *obj);
146 static void modest_msg_view_window_isearch_toolbar_search (GtkWidget *widget,
147 ModestMsgViewWindow *obj);
148 static void modest_msg_view_window_toggle_isearch_toolbar (GtkWidget *obj,
150 static void modest_msg_view_window_disconnect_signals (ModestWindow *self);
152 static gdouble modest_msg_view_window_get_zoom (ModestWindow *window);
153 static void modest_msg_view_window_set_zoom (ModestWindow *window,
155 static gboolean modest_msg_view_window_zoom_minus (ModestWindow *window);
156 static gboolean modest_msg_view_window_zoom_plus (ModestWindow *window);
157 static gboolean modest_msg_view_window_key_event (GtkWidget *window,
160 static void modest_msg_view_window_update_priority (ModestMsgViewWindow *window);
162 static void modest_msg_view_window_show_toolbar (ModestWindow *window,
163 gboolean show_toolbar);
165 static void modest_msg_view_window_clipboard_owner_change (GtkClipboard *clipboard,
167 ModestMsgViewWindow *window);
169 static void modest_msg_view_window_on_row_changed (GtkTreeModel *header_model,
172 ModestMsgViewWindow *window);
174 static void modest_msg_view_window_on_row_deleted (GtkTreeModel *header_model,
176 ModestMsgViewWindow *window);
178 static void modest_msg_view_window_on_row_inserted (GtkTreeModel *header_model,
179 GtkTreePath *tree_path,
180 GtkTreeIter *tree_iter,
181 ModestMsgViewWindow *window);
183 static void modest_msg_view_window_on_row_reordered (GtkTreeModel *header_model,
187 ModestMsgViewWindow *window);
189 static void modest_msg_view_window_update_model_replaced (ModestHeaderViewObserver *window,
191 const gchar *tny_folder_id);
193 static void on_queue_changed (ModestMailOperationQueue *queue,
194 ModestMailOperation *mail_op,
195 ModestMailOperationQueueNotification type,
196 ModestMsgViewWindow *self);
198 static void on_account_removed (TnyAccountStore *account_store,
202 static void on_move_focus (GtkWidget *widget,
203 GtkDirectionType direction,
206 static void view_msg_cb (ModestMailOperation *mail_op,
213 static void set_progress_hint (ModestMsgViewWindow *self,
216 static void update_window_title (ModestMsgViewWindow *window);
218 static void init_window (ModestMsgViewWindow *obj);
220 static gboolean msg_is_visible (TnyHeader *header, gboolean check_outbox);
222 static void check_dimming_rules_after_change (ModestMsgViewWindow *window);
224 static gboolean on_fetch_image (ModestMsgView *msgview,
227 ModestMsgViewWindow *window);
229 static gboolean modest_msg_view_window_scroll_child (ModestMsgViewWindow *self,
230 GtkScrollType scroll_type,
233 static gboolean message_reader (ModestMsgViewWindow *window,
234 ModestMsgViewWindowPrivate *priv,
236 const gchar *msg_uid,
238 GtkTreeRowReference *row_reference);
240 static void setup_menu (ModestMsgViewWindow *self);
241 static gboolean _modest_msg_view_window_map_event (GtkWidget *widget,
244 static void update_branding (ModestMsgViewWindow *self);
245 static void sync_flags (ModestMsgViewWindow *self);
247 /* list my signals */
254 static const GtkActionEntry msg_view_toolbar_action_entries [] = {
257 { "ToolbarMessageReply", MODEST_STOCK_REPLY, N_("mcen_me_inbox_reply"), "<CTRL>R", NULL, G_CALLBACK (modest_ui_actions_on_reply) },
258 { "ToolbarMessageReplyAll", MODEST_STOCK_REPLY_ALL, N_("mcen_me_inbox_replytoall"), NULL, NULL, G_CALLBACK (modest_ui_actions_on_reply_all) },
259 { "ToolbarMessageForward", MODEST_STOCK_FORWARD, N_("mcen_me_inbox_forward"), NULL, NULL, G_CALLBACK (modest_ui_actions_on_forward) },
260 { "ToolbarDeleteMessage", MODEST_STOCK_DELETE, N_("qgn_toolb_gene_deletebutton"), NULL, NULL, G_CALLBACK (modest_ui_actions_on_delete_message_or_folder) },
261 { "ToolbarMessageBack", MODEST_TOOLBAR_ICON_PREV, N_("qgn_toolb_gene_back"), NULL, NULL, G_CALLBACK (modest_ui_actions_on_prev) },
262 { "ToolbarMessageNext", MODEST_TOOLBAR_ICON_NEXT, N_("qgn_toolb_gene_forward"), NULL, NULL, G_CALLBACK (modest_ui_actions_on_next) },
263 { "ToolbarDownloadExternalImages", MODEST_TOOLBAR_ICON_DOWNLOAD_IMAGES, N_("mail_bd_external_images"), NULL, NULL, G_CALLBACK (modest_ui_actions_on_fetch_images) },
266 static const GtkToggleActionEntry msg_view_toggle_action_entries [] = {
267 { "FindInMessage", MODEST_TOOLBAR_ICON_FIND, N_("qgn_toolb_gene_find"), "<CTRL>F", NULL, G_CALLBACK (modest_msg_view_window_toggle_isearch_toolbar), FALSE },
270 #define MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE((o), \
271 MODEST_TYPE_MSG_VIEW_WINDOW, \
272 ModestMsgViewWindowPrivate))
274 static GtkWindowClass *parent_class = NULL;
276 /* uncomment the following if you have defined any signals */
277 static guint signals[LAST_SIGNAL] = {0};
280 modest_msg_view_window_get_type (void)
282 static GType my_type = 0;
284 static const GTypeInfo my_info = {
285 sizeof(ModestMsgViewWindowClass),
286 NULL, /* base init */
287 NULL, /* base finalize */
288 (GClassInitFunc) modest_msg_view_window_class_init,
289 NULL, /* class finalize */
290 NULL, /* class data */
291 sizeof(ModestMsgViewWindow),
293 (GInstanceInitFunc) modest_msg_view_window_init,
296 #ifndef MODEST_TOOLKIT_HILDON2
297 my_type = g_type_register_static (MODEST_TYPE_SHELL_WINDOW,
298 "ModestMsgViewWindow",
301 my_type = g_type_register_static (MODEST_TYPE_HILDON2_WINDOW,
302 "ModestMsgViewWindow",
306 static const GInterfaceInfo modest_header_view_observer_info =
308 (GInterfaceInitFunc) modest_header_view_observer_init,
309 NULL, /* interface_finalize */
310 NULL /* interface_data */
313 g_type_add_interface_static (my_type,
314 MODEST_TYPE_HEADER_VIEW_OBSERVER,
315 &modest_header_view_observer_info);
321 save_state (ModestWindow *self)
323 modest_widget_memory_save (modest_runtime_get_conf (),
325 MODEST_CONF_MSG_VIEW_WINDOW_KEY);
329 modest_msg_view_window_scroll_child (ModestMsgViewWindow *self,
330 GtkScrollType scroll_type,
334 ModestMsgViewWindowPrivate *priv;
337 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(self);
339 switch (scroll_type) {
340 case GTK_SCROLL_STEP_UP:
343 case GTK_SCROLL_STEP_DOWN:
346 case GTK_SCROLL_PAGE_UP:
349 case GTK_SCROLL_PAGE_DOWN:
352 case GTK_SCROLL_START:
363 modest_scrollable_scroll ((ModestScrollable *) priv->main_scroll, 0, step);
365 return (gboolean) step;
369 add_scroll_binding (GtkBindingSet *binding_set,
371 GtkScrollType scroll)
373 guint keypad_keyval = keyval - GDK_Left + GDK_KP_Left;
375 gtk_binding_entry_add_signal (binding_set, keyval, 0,
377 GTK_TYPE_SCROLL_TYPE, scroll,
378 G_TYPE_BOOLEAN, FALSE);
379 gtk_binding_entry_add_signal (binding_set, keypad_keyval, 0,
381 GTK_TYPE_SCROLL_TYPE, scroll,
382 G_TYPE_BOOLEAN, FALSE);
386 modest_msg_view_window_class_init (ModestMsgViewWindowClass *klass)
388 GObjectClass *gobject_class;
389 ModestWindowClass *modest_window_class;
390 GtkBindingSet *binding_set;
392 gobject_class = (GObjectClass*) klass;
393 modest_window_class = (ModestWindowClass *) klass;
395 parent_class = g_type_class_peek_parent (klass);
396 gobject_class->finalize = modest_msg_view_window_finalize;
398 modest_window_class->set_zoom_func = modest_msg_view_window_set_zoom;
399 modest_window_class->get_zoom_func = modest_msg_view_window_get_zoom;
400 modest_window_class->zoom_plus_func = modest_msg_view_window_zoom_plus;
401 modest_window_class->zoom_minus_func = modest_msg_view_window_zoom_minus;
402 modest_window_class->show_toolbar_func = modest_msg_view_window_show_toolbar;
403 modest_window_class->disconnect_signals_func = modest_msg_view_window_disconnect_signals;
405 modest_window_class->save_state_func = save_state;
407 klass->scroll_child = modest_msg_view_window_scroll_child;
409 signals[MSG_CHANGED_SIGNAL] =
410 g_signal_new ("msg-changed",
411 G_TYPE_FROM_CLASS (gobject_class),
413 G_STRUCT_OFFSET (ModestMsgViewWindowClass, msg_changed),
415 modest_marshal_VOID__POINTER_POINTER,
416 G_TYPE_NONE, 2, G_TYPE_POINTER, G_TYPE_POINTER);
418 signals[SCROLL_CHILD_SIGNAL] =
419 g_signal_new ("scroll-child",
420 G_TYPE_FROM_CLASS (gobject_class),
421 G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
422 G_STRUCT_OFFSET (ModestMsgViewWindowClass, scroll_child),
424 modest_marshal_BOOLEAN__ENUM_BOOLEAN,
425 G_TYPE_BOOLEAN, 2, GTK_TYPE_SCROLL_TYPE, G_TYPE_BOOLEAN);
427 binding_set = gtk_binding_set_by_class (klass);
428 add_scroll_binding (binding_set, GDK_Up, GTK_SCROLL_STEP_UP);
429 add_scroll_binding (binding_set, GDK_Down, GTK_SCROLL_STEP_DOWN);
430 add_scroll_binding (binding_set, GDK_Page_Up, GTK_SCROLL_PAGE_UP);
431 add_scroll_binding (binding_set, GDK_Page_Down, GTK_SCROLL_PAGE_DOWN);
432 add_scroll_binding (binding_set, GDK_Home, GTK_SCROLL_START);
433 add_scroll_binding (binding_set, GDK_End, GTK_SCROLL_END);
435 g_type_class_add_private (gobject_class, sizeof(ModestMsgViewWindowPrivate));
439 static void modest_header_view_observer_init(
440 ModestHeaderViewObserverIface *iface_class)
442 iface_class->update_func = modest_msg_view_window_update_model_replaced;
446 modest_msg_view_window_init (ModestMsgViewWindow *obj)
448 ModestMsgViewWindowPrivate *priv;
449 ModestWindowPrivate *parent_priv = NULL;
450 GtkActionGroup *action_group = NULL;
451 GError *error = NULL;
453 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(obj);
454 parent_priv = MODEST_WINDOW_GET_PRIVATE(obj);
455 parent_priv->ui_manager = gtk_ui_manager_new();
457 action_group = gtk_action_group_new ("ModestMsgViewWindowActions");
458 gtk_action_group_set_translation_domain (action_group, GETTEXT_PACKAGE);
460 /* Add common actions */
461 gtk_action_group_add_actions (action_group,
462 msg_view_toolbar_action_entries,
463 G_N_ELEMENTS (msg_view_toolbar_action_entries),
465 gtk_action_group_add_toggle_actions (action_group,
466 msg_view_toggle_action_entries,
467 G_N_ELEMENTS (msg_view_toggle_action_entries),
470 gtk_ui_manager_insert_action_group (parent_priv->ui_manager, action_group, 0);
471 g_object_unref (action_group);
473 /* Load the UI definition */
474 gtk_ui_manager_add_ui_from_file (parent_priv->ui_manager, MODEST_UIDIR "modest-msg-view-window-ui.xml",
477 g_printerr ("modest: could not merge modest-msg-view-window-ui.xml: %s\n", error->message);
478 g_error_free (error);
482 priv->is_search_result = FALSE;
483 priv->is_outbox = FALSE;
485 priv->msg_view = NULL;
486 priv->header_model = NULL;
487 priv->header_folder_id = NULL;
488 priv->clipboard_change_handler = 0;
489 priv->queue_change_handler = 0;
490 priv->account_removed_handler = 0;
491 priv->row_changed_handler = 0;
492 priv->row_deleted_handler = 0;
493 priv->row_inserted_handler = 0;
494 priv->rows_reordered_handler = 0;
495 priv->progress_hint = FALSE;
496 priv->fetching_images = 0;
498 priv->optimized_view = FALSE;
499 priv->purge_timeout = 0;
500 priv->remove_attachment_banner = NULL;
501 priv->msg_uid = NULL;
502 priv->other_body = NULL;
504 priv->sighandlers = NULL;
507 init_window (MODEST_MSG_VIEW_WINDOW(obj));
512 update_progress_hint (ModestMsgViewWindow *self)
514 ModestMsgViewWindowPrivate *priv;
515 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(self);
517 if (GTK_WIDGET_VISIBLE (self)) {
518 modest_window_show_progress (MODEST_WINDOW (self),
519 (priv->progress_hint || (priv->fetching_images > 0))?1:0);
524 set_progress_hint (ModestMsgViewWindow *self,
527 ModestWindowPrivate *parent_priv;
528 ModestMsgViewWindowPrivate *priv;
530 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self));
532 parent_priv = MODEST_WINDOW_GET_PRIVATE(self);
533 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(self);
535 /* Sets current progress hint */
536 priv->progress_hint = enabled;
538 update_progress_hint (self);
544 init_window (ModestMsgViewWindow *obj)
546 GtkWidget *main_vbox;
547 ModestMsgViewWindowPrivate *priv;
549 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(obj);
551 priv->msg_view = GTK_WIDGET (tny_platform_factory_new_msg_view (modest_tny_platform_factory_get_instance ()));
552 modest_msg_view_set_shadow_type (MODEST_MSG_VIEW (priv->msg_view), GTK_SHADOW_NONE);
553 main_vbox = gtk_vbox_new (FALSE, 6);
555 priv->main_scroll = modest_toolkit_factory_create_scrollable (modest_runtime_get_toolkit_factory ());
556 modest_scrollable_set_horizontal_policy (MODEST_SCROLLABLE (priv->main_scroll), GTK_POLICY_AUTOMATIC);
557 g_object_set (G_OBJECT (priv->main_scroll),
558 "movement-mode", MODEST_MOVEMENT_MODE_BOTH,
559 "horizontal-max-overshoot", 0,
561 gtk_container_add (GTK_CONTAINER (priv->main_scroll), priv->msg_view);
562 gtk_box_pack_start (GTK_BOX(main_vbox), priv->main_scroll, TRUE, TRUE, 0);
563 gtk_container_add (GTK_CONTAINER(obj), main_vbox);
565 /* NULL-ize fields if the window is destroyed */
566 g_signal_connect (priv->msg_view, "destroy", G_CALLBACK (gtk_widget_destroyed), &(priv->msg_view));
568 gtk_widget_show_all (GTK_WIDGET(main_vbox));
572 modest_msg_view_window_disconnect_signals (ModestWindow *self)
574 ModestMsgViewWindowPrivate *priv;
575 GtkWidget *header_view = NULL;
576 GtkWindow *parent_window = NULL;
578 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
580 if (gtk_clipboard_get (GDK_SELECTION_PRIMARY) &&
581 g_signal_handler_is_connected (gtk_clipboard_get (GDK_SELECTION_PRIMARY),
582 priv->clipboard_change_handler))
583 g_signal_handler_disconnect (gtk_clipboard_get (GDK_SELECTION_PRIMARY),
584 priv->clipboard_change_handler);
586 if (g_signal_handler_is_connected (G_OBJECT (modest_runtime_get_mail_operation_queue ()),
587 priv->queue_change_handler))
588 g_signal_handler_disconnect (G_OBJECT (modest_runtime_get_mail_operation_queue ()),
589 priv->queue_change_handler);
591 if (g_signal_handler_is_connected (G_OBJECT (modest_runtime_get_account_store ()),
592 priv->account_removed_handler))
593 g_signal_handler_disconnect (G_OBJECT (modest_runtime_get_account_store ()),
594 priv->account_removed_handler);
596 if (priv->header_model) {
597 if (g_signal_handler_is_connected(G_OBJECT (priv->header_model),
598 priv->row_changed_handler))
599 g_signal_handler_disconnect(G_OBJECT (priv->header_model),
600 priv->row_changed_handler);
602 if (g_signal_handler_is_connected(G_OBJECT (priv->header_model),
603 priv->row_deleted_handler))
604 g_signal_handler_disconnect(G_OBJECT (priv->header_model),
605 priv->row_deleted_handler);
607 if (g_signal_handler_is_connected(G_OBJECT (priv->header_model),
608 priv->row_inserted_handler))
609 g_signal_handler_disconnect(G_OBJECT (priv->header_model),
610 priv->row_inserted_handler);
612 if (g_signal_handler_is_connected(G_OBJECT (priv->header_model),
613 priv->rows_reordered_handler))
614 g_signal_handler_disconnect(G_OBJECT (priv->header_model),
615 priv->rows_reordered_handler);
618 modest_signal_mgr_disconnect_all_and_destroy (priv->sighandlers);
619 priv->sighandlers = NULL;
621 parent_window = gtk_window_get_transient_for (GTK_WINDOW (self));
622 if (parent_window && MODEST_IS_HEADER_WINDOW (parent_window)) {
623 header_view = GTK_WIDGET (modest_header_window_get_header_view (MODEST_HEADER_WINDOW (parent_window)));
625 modest_header_view_remove_observer(MODEST_HEADER_VIEW (header_view),
626 MODEST_HEADER_VIEW_OBSERVER(self));
632 modest_msg_view_window_finalize (GObject *obj)
634 ModestMsgViewWindowPrivate *priv;
636 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (obj);
638 /* Sanity check: shouldn't be needed, the window mgr should
639 call this function before */
640 modest_msg_view_window_disconnect_signals (MODEST_WINDOW (obj));
642 if (priv->other_body != NULL) {
643 g_object_unref (priv->other_body);
644 priv->other_body = NULL;
647 if (priv->header_model != NULL) {
648 g_object_unref (priv->header_model);
649 priv->header_model = NULL;
652 if (priv->remove_attachment_banner) {
653 gtk_widget_destroy (priv->remove_attachment_banner);
654 g_object_unref (priv->remove_attachment_banner);
655 priv->remove_attachment_banner = NULL;
658 if (priv->purge_timeout > 0) {
659 g_source_remove (priv->purge_timeout);
660 priv->purge_timeout = 0;
663 if (priv->row_reference) {
664 gtk_tree_row_reference_free (priv->row_reference);
665 priv->row_reference = NULL;
668 if (priv->next_row_reference) {
669 gtk_tree_row_reference_free (priv->next_row_reference);
670 priv->next_row_reference = NULL;
674 g_free (priv->msg_uid);
675 priv->msg_uid = NULL;
678 G_OBJECT_CLASS(parent_class)->finalize (obj);
682 select_next_valid_row (GtkTreeModel *model,
683 GtkTreeRowReference **row_reference,
687 GtkTreeIter tmp_iter;
689 GtkTreePath *next = NULL;
690 gboolean retval = FALSE, finished;
692 g_return_val_if_fail (gtk_tree_row_reference_valid (*row_reference), FALSE);
694 path = gtk_tree_row_reference_get_path (*row_reference);
695 gtk_tree_model_get_iter (model, &tmp_iter, path);
696 gtk_tree_row_reference_free (*row_reference);
697 *row_reference = NULL;
701 TnyHeader *header = NULL;
703 if (gtk_tree_model_iter_next (model, &tmp_iter)) {
704 gtk_tree_model_get (model, &tmp_iter,
705 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
709 if (msg_is_visible (header, is_outbox)) {
710 next = gtk_tree_model_get_path (model, &tmp_iter);
711 *row_reference = gtk_tree_row_reference_new (model, next);
712 gtk_tree_path_free (next);
716 g_object_unref (header);
719 } else if (cycle && gtk_tree_model_get_iter_first (model, &tmp_iter)) {
720 next = gtk_tree_model_get_path (model, &tmp_iter);
722 /* Ensure that we are not selecting the same */
723 if (gtk_tree_path_compare (path, next) != 0) {
724 gtk_tree_model_get (model, &tmp_iter,
725 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
728 if (msg_is_visible (header, is_outbox)) {
729 *row_reference = gtk_tree_row_reference_new (model, next);
733 g_object_unref (header);
737 /* If we ended up in the same message
738 then there is no valid next
742 gtk_tree_path_free (next);
744 /* If there are no more messages and we don't
745 want to start again in the first one then
746 there is no valid next message */
752 gtk_tree_path_free (path);
757 /* TODO: This should be in _init(), with the parameters as properties. */
759 modest_msg_view_window_construct (ModestMsgViewWindow *self,
760 const gchar *modest_account_name,
761 const gchar *mailbox,
762 const gchar *msg_uid)
765 ModestMsgViewWindowPrivate *priv = NULL;
766 ModestWindowPrivate *parent_priv = NULL;
767 ModestDimmingRulesGroup *toolbar_rules_group = NULL;
768 ModestDimmingRulesGroup *clipboard_rules_group = NULL;
770 obj = G_OBJECT (self);
771 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(obj);
772 parent_priv = MODEST_WINDOW_GET_PRIVATE(obj);
774 priv->msg_uid = g_strdup (msg_uid);
777 parent_priv->menubar = NULL;
779 toolbar_rules_group = modest_dimming_rules_group_new (MODEST_DIMMING_RULES_TOOLBAR, TRUE);
780 clipboard_rules_group = modest_dimming_rules_group_new (MODEST_DIMMING_RULES_CLIPBOARD, FALSE);
783 /* Add common dimming rules */
784 modest_dimming_rules_group_add_rules (toolbar_rules_group,
785 modest_msg_view_toolbar_dimming_entries,
786 G_N_ELEMENTS (modest_msg_view_toolbar_dimming_entries),
787 MODEST_WINDOW (self));
788 modest_dimming_rules_group_add_rules (clipboard_rules_group,
789 modest_msg_view_clipboard_dimming_entries,
790 G_N_ELEMENTS (modest_msg_view_clipboard_dimming_entries),
791 MODEST_WINDOW (self));
793 /* Insert dimming rules group for this window */
794 modest_ui_dimming_manager_insert_rules_group (parent_priv->ui_dimming_manager, toolbar_rules_group);
795 modest_ui_dimming_manager_insert_rules_group (parent_priv->ui_dimming_manager, clipboard_rules_group);
796 g_object_unref (toolbar_rules_group);
797 g_object_unref (clipboard_rules_group);
799 /* g_signal_connect (G_OBJECT(obj), "delete-event", G_CALLBACK(on_delete_event), obj); */
801 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);
802 g_signal_connect (G_OBJECT(priv->msg_view), "activate_link",
803 G_CALLBACK (modest_ui_actions_on_msg_link_clicked), obj);
804 g_signal_connect (G_OBJECT(priv->msg_view), "link_hover",
805 G_CALLBACK (modest_ui_actions_on_msg_link_hover), obj);
806 g_signal_connect (G_OBJECT(priv->msg_view), "attachment_clicked",
807 G_CALLBACK (modest_ui_actions_on_msg_attachment_clicked), obj);
808 g_signal_connect (G_OBJECT(priv->msg_view), "recpt_activated",
809 G_CALLBACK (modest_ui_actions_on_msg_recpt_activated), obj);
810 g_signal_connect (G_OBJECT(priv->msg_view), "show_details",
811 G_CALLBACK (modest_ui_actions_on_details), obj);
812 g_signal_connect (G_OBJECT(priv->msg_view), "link_contextual",
813 G_CALLBACK (modest_ui_actions_on_msg_link_contextual), obj);
814 g_signal_connect (G_OBJECT(priv->msg_view), "limit_error",
815 G_CALLBACK (modest_ui_actions_on_limit_error), obj);
816 g_signal_connect (G_OBJECT (priv->msg_view), "fetch_image",
817 G_CALLBACK (on_fetch_image), obj);
819 g_signal_connect (G_OBJECT (obj), "key-release-event",
820 G_CALLBACK (modest_msg_view_window_key_event),
823 g_signal_connect (G_OBJECT (obj), "key-press-event",
824 G_CALLBACK (modest_msg_view_window_key_event),
827 g_signal_connect (G_OBJECT (obj), "move-focus",
828 G_CALLBACK (on_move_focus), obj);
830 g_signal_connect (G_OBJECT (obj), "map-event",
831 G_CALLBACK (_modest_msg_view_window_map_event),
834 /* Mail Operation Queue */
835 priv->queue_change_handler = g_signal_connect (G_OBJECT (modest_runtime_get_mail_operation_queue ()),
837 G_CALLBACK (on_queue_changed),
840 /* Account manager */
841 priv->account_removed_handler = g_signal_connect (G_OBJECT (modest_runtime_get_account_store ()),
843 G_CALLBACK(on_account_removed),
846 modest_window_set_active_account (MODEST_WINDOW(obj), modest_account_name);
847 modest_window_set_active_mailbox (MODEST_WINDOW(obj), mailbox);
849 /* First add out toolbar ... */
850 modest_msg_view_window_show_toolbar (MODEST_WINDOW (obj), TRUE);
852 priv->isearch_toolbar = modest_toolkit_factory_create_isearch_toolbar (modest_runtime_get_toolkit_factory (),
854 modest_window_add_toolbar (MODEST_WINDOW (obj), GTK_TOOLBAR (priv->isearch_toolbar));
855 gtk_widget_set_no_show_all (priv->isearch_toolbar, TRUE);
856 g_signal_connect (G_OBJECT (priv->isearch_toolbar), "isearch-close",
857 G_CALLBACK (modest_msg_view_window_isearch_toolbar_close), obj);
858 g_signal_connect (G_OBJECT (priv->isearch_toolbar), "isearch-search",
859 G_CALLBACK (modest_msg_view_window_isearch_toolbar_search), obj);
860 priv->last_search = NULL;
862 /* Init the clipboard actions dim status */
863 modest_msg_view_grab_focus(MODEST_MSG_VIEW (priv->msg_view));
865 update_window_title (MODEST_MSG_VIEW_WINDOW (obj));
870 /* FIXME: parameter checks */
872 modest_msg_view_window_new_with_header_model (TnyMsg *msg,
873 const gchar *modest_account_name,
874 const gchar *mailbox,
875 const gchar *msg_uid,
877 GtkTreeRowReference *row_reference)
879 ModestMsgViewWindow *window = NULL;
880 ModestMsgViewWindowPrivate *priv = NULL;
881 TnyFolder *header_folder = NULL;
882 ModestHeaderView *header_view = NULL;
883 ModestWindowMgr *mgr = NULL;
886 modest_tny_mime_part_to_string (TNY_MIME_PART (msg), 0);
889 mgr = modest_runtime_get_window_mgr ();
890 window = MODEST_MSG_VIEW_WINDOW (modest_window_mgr_get_msg_view_window (mgr));
891 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), NULL);
893 modest_msg_view_window_construct (window, modest_account_name, mailbox, msg_uid);
895 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
897 /* Remember the message list's TreeModel so we can detect changes
898 * and change the list selection when necessary: */
899 header_folder = modest_header_view_get_folder (header_view);
901 priv->is_outbox = (modest_tny_folder_guess_folder_type (header_folder) ==
902 TNY_FOLDER_TYPE_OUTBOX);
903 priv->header_folder_id = tny_folder_get_id (header_folder);
904 g_object_unref(header_folder);
907 /* Setup row references and connect signals */
908 priv->header_model = g_object_ref (model);
910 if (row_reference && gtk_tree_row_reference_valid (row_reference)) {
911 priv->row_reference = gtk_tree_row_reference_copy (row_reference);
912 priv->next_row_reference = gtk_tree_row_reference_copy (row_reference);
913 select_next_valid_row (model, &(priv->next_row_reference), TRUE, priv->is_outbox);
915 priv->row_reference = NULL;
916 priv->next_row_reference = NULL;
919 /* Connect signals */
920 priv->row_changed_handler =
921 g_signal_connect (GTK_TREE_MODEL(model), "row-changed",
922 G_CALLBACK(modest_msg_view_window_on_row_changed),
924 priv->row_deleted_handler =
925 g_signal_connect (GTK_TREE_MODEL(model), "row-deleted",
926 G_CALLBACK(modest_msg_view_window_on_row_deleted),
928 priv->row_inserted_handler =
929 g_signal_connect (GTK_TREE_MODEL(model), "row-inserted",
930 G_CALLBACK(modest_msg_view_window_on_row_inserted),
932 priv->rows_reordered_handler =
933 g_signal_connect(GTK_TREE_MODEL(model), "rows-reordered",
934 G_CALLBACK(modest_msg_view_window_on_row_reordered),
937 if (header_view != NULL){
938 modest_header_view_add_observer(header_view,
939 MODEST_HEADER_VIEW_OBSERVER(window));
942 tny_msg_view_set_msg (TNY_MSG_VIEW (priv->msg_view), msg);
943 update_window_title (MODEST_MSG_VIEW_WINDOW (window));
944 update_branding (MODEST_MSG_VIEW_WINDOW (window));
946 /* gtk_widget_show_all (GTK_WIDGET (window)); */
947 modest_msg_view_window_update_priority (window);
948 /* Check dimming rules */
949 modest_ui_actions_check_toolbar_dimming_rules (MODEST_WINDOW (window));
950 modest_ui_actions_check_menu_dimming_rules (MODEST_WINDOW (window));
951 modest_window_check_dimming_rules_group (MODEST_WINDOW (window), MODEST_DIMMING_RULES_CLIPBOARD);
953 return MODEST_WINDOW(window);
957 modest_msg_view_window_new_from_uid (const gchar *modest_account_name,
958 const gchar *mailbox,
959 const gchar *msg_uid)
961 ModestMsgViewWindow *window = NULL;
962 ModestMsgViewWindowPrivate *priv = NULL;
963 ModestWindowMgr *mgr = NULL;
965 TnyAccount *account = NULL;
967 mgr = modest_runtime_get_window_mgr ();
968 window = MODEST_MSG_VIEW_WINDOW (modest_window_mgr_get_msg_view_window (mgr));
969 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), NULL);
971 modest_msg_view_window_construct (window, modest_account_name, mailbox, msg_uid);
973 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
975 is_merge = g_str_has_prefix (msg_uid, "merge:");
977 /* Get the account */
979 account = tny_account_store_find_account (TNY_ACCOUNT_STORE (modest_runtime_get_account_store ()),
982 if (is_merge || account) {
983 TnyFolder *folder = NULL;
985 /* Try to get the message, if it's already downloaded
986 we don't need to connect */
988 folder = modest_tny_folder_store_find_folder_from_uri (TNY_FOLDER_STORE (account), msg_uid);
990 ModestTnyAccountStore *account_store;
991 ModestTnyLocalFoldersAccount *local_folders_account;
993 account_store = modest_runtime_get_account_store ();
994 local_folders_account = MODEST_TNY_LOCAL_FOLDERS_ACCOUNT (
995 modest_tny_account_store_get_local_folders_account (account_store));
996 folder = modest_tny_local_folders_account_get_merged_outbox (local_folders_account);
997 g_object_unref (local_folders_account);
1001 gboolean device_online;
1003 device = modest_runtime_get_device();
1004 device_online = tny_device_is_online (device);
1005 if (device_online) {
1006 message_reader (window, priv, NULL, msg_uid, folder, NULL);
1008 TnyMsg *msg = tny_folder_find_msg (folder, msg_uid, NULL);
1010 tny_msg_view_set_msg (TNY_MSG_VIEW (priv->msg_view), msg);
1011 update_window_title (MODEST_MSG_VIEW_WINDOW (window));
1012 update_branding (MODEST_MSG_VIEW_WINDOW (window));
1013 g_object_unref (msg);
1014 /* Sync flags to server */
1015 sync_flags (MODEST_MSG_VIEW_WINDOW (window));
1017 message_reader (window, priv, NULL, msg_uid, folder, NULL);
1020 g_object_unref (folder);
1025 /* Check dimming rules */
1026 modest_ui_actions_check_toolbar_dimming_rules (MODEST_WINDOW (window));
1027 modest_ui_actions_check_menu_dimming_rules (MODEST_WINDOW (window));
1028 modest_window_check_dimming_rules_group (MODEST_WINDOW (window), MODEST_DIMMING_RULES_CLIPBOARD);
1030 return MODEST_WINDOW(window);
1034 modest_msg_view_window_new_from_header_view (ModestHeaderView *header_view,
1035 const gchar *modest_account_name,
1036 const gchar *mailbox,
1037 const gchar *msg_uid,
1038 GtkTreeRowReference *row_reference)
1040 ModestMsgViewWindow *window = NULL;
1041 ModestMsgViewWindowPrivate *priv = NULL;
1042 TnyFolder *header_folder = NULL;
1043 ModestWindowMgr *mgr = NULL;
1047 mgr = modest_runtime_get_window_mgr ();
1048 window = MODEST_MSG_VIEW_WINDOW (modest_window_mgr_get_msg_view_window (mgr));
1049 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), NULL);
1051 modest_msg_view_window_construct (window, modest_account_name, mailbox, msg_uid);
1053 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1055 /* Remember the message list's TreeModel so we can detect changes
1056 * and change the list selection when necessary: */
1058 if (header_view != NULL){
1059 header_folder = modest_header_view_get_folder(header_view);
1060 /* This could happen if the header folder was
1061 unseleted before opening this msg window (for
1062 example if the user selects an account in the
1063 folder view of the main window */
1064 if (header_folder) {
1065 priv->is_outbox = (modest_tny_folder_guess_folder_type (header_folder) ==
1066 TNY_FOLDER_TYPE_OUTBOX);
1067 priv->header_folder_id = tny_folder_get_id(header_folder);
1068 g_object_unref(header_folder);
1072 /* Setup row references and connect signals */
1073 priv->header_model = gtk_tree_view_get_model (GTK_TREE_VIEW (header_view));
1074 g_object_ref (priv->header_model);
1076 if (row_reference && gtk_tree_row_reference_valid (row_reference)) {
1077 priv->row_reference = gtk_tree_row_reference_copy (row_reference);
1078 priv->next_row_reference = gtk_tree_row_reference_copy (row_reference);
1079 select_next_valid_row (priv->header_model, &(priv->next_row_reference), TRUE, priv->is_outbox);
1081 priv->row_reference = NULL;
1082 priv->next_row_reference = NULL;
1085 /* Connect signals */
1086 priv->row_changed_handler =
1087 g_signal_connect (GTK_TREE_MODEL(priv->header_model), "row-changed",
1088 G_CALLBACK(modest_msg_view_window_on_row_changed),
1090 priv->row_deleted_handler =
1091 g_signal_connect (GTK_TREE_MODEL(priv->header_model), "row-deleted",
1092 G_CALLBACK(modest_msg_view_window_on_row_deleted),
1094 priv->row_inserted_handler =
1095 g_signal_connect (GTK_TREE_MODEL(priv->header_model), "row-inserted",
1096 G_CALLBACK(modest_msg_view_window_on_row_inserted),
1098 priv->rows_reordered_handler =
1099 g_signal_connect(GTK_TREE_MODEL(priv->header_model), "rows-reordered",
1100 G_CALLBACK(modest_msg_view_window_on_row_reordered),
1103 if (header_view != NULL){
1104 modest_header_view_add_observer(header_view,
1105 MODEST_HEADER_VIEW_OBSERVER(window));
1108 tny_msg_view_set_msg (TNY_MSG_VIEW (priv->msg_view), NULL);
1109 update_branding (MODEST_MSG_VIEW_WINDOW (window));
1111 if (priv->row_reference) {
1112 path = gtk_tree_row_reference_get_path (priv->row_reference);
1113 if (gtk_tree_model_get_iter (priv->header_model, &iter, path)) {
1115 gtk_tree_model_get (priv->header_model, &iter,
1116 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
1118 message_reader (window, priv, header, NULL, NULL, priv->row_reference);
1119 g_object_unref (header);
1121 gtk_tree_path_free (path);
1123 /* Check dimming rules */
1124 modest_ui_actions_check_toolbar_dimming_rules (MODEST_WINDOW (window));
1125 modest_ui_actions_check_menu_dimming_rules (MODEST_WINDOW (window));
1126 modest_window_check_dimming_rules_group (MODEST_WINDOW (window), MODEST_DIMMING_RULES_CLIPBOARD);
1128 return MODEST_WINDOW(window);
1132 modest_msg_view_window_new_for_search_result (TnyMsg *msg,
1133 const gchar *modest_account_name,
1134 const gchar *mailbox,
1135 const gchar *msg_uid)
1137 ModestMsgViewWindow *window = NULL;
1138 ModestMsgViewWindowPrivate *priv = NULL;
1139 ModestWindowMgr *mgr = NULL;
1141 mgr = modest_runtime_get_window_mgr ();
1142 window = MODEST_MSG_VIEW_WINDOW (modest_window_mgr_get_msg_view_window (mgr));
1143 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), NULL);
1144 modest_msg_view_window_construct (window, modest_account_name, mailbox, msg_uid);
1146 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1148 /* Remember that this is a search result,
1149 * so we can disable some UI appropriately: */
1150 priv->is_search_result = TRUE;
1152 tny_msg_view_set_msg (TNY_MSG_VIEW (priv->msg_view), msg);
1153 update_branding (MODEST_MSG_VIEW_WINDOW (window));
1155 update_window_title (window);
1156 /* gtk_widget_show_all (GTK_WIDGET (window));*/
1157 modest_msg_view_window_update_priority (window);
1159 /* Check dimming rules */
1160 modest_ui_actions_check_toolbar_dimming_rules (MODEST_WINDOW (window));
1161 modest_ui_actions_check_menu_dimming_rules (MODEST_WINDOW (window));
1162 modest_window_check_dimming_rules_group (MODEST_WINDOW (window), MODEST_DIMMING_RULES_CLIPBOARD);
1164 return MODEST_WINDOW(window);
1168 modest_msg_view_window_is_other_body (ModestMsgViewWindow *self)
1170 ModestMsgViewWindowPrivate *priv = NULL;
1172 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self), FALSE);
1173 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
1175 return (priv->other_body != NULL);
1179 modest_msg_view_window_new_with_other_body (TnyMsg *msg,
1180 TnyMimePart *other_body,
1181 const gchar *modest_account_name,
1182 const gchar *mailbox,
1183 const gchar *msg_uid)
1185 GObject *obj = NULL;
1186 ModestMsgViewWindowPrivate *priv;
1187 ModestWindowMgr *mgr = NULL;
1189 g_return_val_if_fail (msg, NULL);
1190 mgr = modest_runtime_get_window_mgr ();
1191 obj = G_OBJECT (modest_window_mgr_get_msg_view_window (mgr));
1192 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (obj);
1193 modest_msg_view_window_construct (MODEST_MSG_VIEW_WINDOW (obj),
1194 modest_account_name, mailbox, msg_uid);
1197 priv->other_body = g_object_ref (other_body);
1198 modest_msg_view_set_msg_with_other_body (MODEST_MSG_VIEW (priv->msg_view), msg, other_body);
1200 tny_msg_view_set_msg (TNY_MSG_VIEW (priv->msg_view), msg);
1202 update_window_title (MODEST_MSG_VIEW_WINDOW (obj));
1203 update_branding (MODEST_MSG_VIEW_WINDOW (obj));
1205 /* gtk_widget_show_all (GTK_WIDGET (obj)); */
1207 /* Check dimming rules */
1208 modest_ui_actions_check_toolbar_dimming_rules (MODEST_WINDOW (obj));
1209 modest_ui_actions_check_menu_dimming_rules (MODEST_WINDOW (obj));
1210 modest_window_check_dimming_rules_group (MODEST_WINDOW (obj), MODEST_DIMMING_RULES_CLIPBOARD);
1212 return MODEST_WINDOW(obj);
1216 modest_msg_view_window_new_for_attachment (TnyMsg *msg,
1217 const gchar *modest_account_name,
1218 const gchar *mailbox,
1219 const gchar *msg_uid)
1221 return modest_msg_view_window_new_with_other_body (msg, NULL, modest_account_name, mailbox, msg_uid);
1225 modest_msg_view_window_on_row_changed (GtkTreeModel *header_model,
1228 ModestMsgViewWindow *window)
1230 check_dimming_rules_after_change (window);
1234 modest_msg_view_window_on_row_deleted(GtkTreeModel *header_model,
1236 ModestMsgViewWindow *window)
1238 check_dimming_rules_after_change (window);
1240 /* The window could have dissapeared */
1243 check_dimming_rules_after_change (ModestMsgViewWindow *window)
1245 modest_ui_actions_check_menu_dimming_rules (MODEST_WINDOW (window));
1246 modest_ui_actions_check_toolbar_dimming_rules (MODEST_WINDOW (window));
1250 /* On insertions we check if the folder still has the message we are
1251 * showing or do not. If do not, we do nothing. Which means we are still
1252 * not attached to any header folder and thus next/prev buttons are
1253 * still dimmed. Once the message that is shown by msg-view is found, the
1254 * new model of header-view will be attached and the references will be set.
1255 * On each further insertions dimming rules will be checked. However
1256 * this requires extra CPU time at least works.
1257 * (An message might be deleted from TnyFolder and thus will not be
1258 * inserted into the model again for example if it is removed by the
1259 * imap server and the header view is refreshed.)
1262 modest_msg_view_window_on_row_inserted (GtkTreeModel *model,
1263 GtkTreePath *tree_path,
1264 GtkTreeIter *tree_iter,
1265 ModestMsgViewWindow *window)
1267 ModestMsgViewWindowPrivate *priv = NULL;
1268 TnyHeader *header = NULL;
1270 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window));
1271 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1273 g_assert (model == priv->header_model);
1275 /* Check if the newly inserted message is the same we are actually
1276 * showing. IF not, we should remain detached from the header model
1277 * and thus prev and next toolbar buttons should remain dimmed. */
1278 gtk_tree_model_get (model, tree_iter,
1279 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
1282 if (TNY_IS_HEADER (header)) {
1285 uid = modest_tny_folder_get_header_unique_id (header);
1286 if (!g_str_equal(priv->msg_uid, uid)) {
1287 check_dimming_rules_after_change (window);
1289 g_object_unref (G_OBJECT(header));
1293 g_object_unref(G_OBJECT(header));
1296 if (priv->row_reference) {
1297 gtk_tree_row_reference_free (priv->row_reference);
1300 /* Setup row_reference for the actual msg. */
1301 priv->row_reference = gtk_tree_row_reference_new (priv->header_model, tree_path);
1302 if (priv->row_reference == NULL) {
1303 g_warning("%s: No reference for msg header item.", __FUNCTION__);
1307 /* Now set up next_row_reference. */
1308 if (priv->next_row_reference) {
1309 gtk_tree_row_reference_free (priv->next_row_reference);
1312 priv->next_row_reference = gtk_tree_row_reference_copy (priv->row_reference);
1313 select_next_valid_row (priv->header_model,
1314 &(priv->next_row_reference), FALSE, priv->is_outbox);
1316 /* Connect the remaining callbacks to become able to detect
1317 * changes in header-view. */
1318 priv->row_changed_handler =
1319 g_signal_connect (priv->header_model, "row-changed",
1320 G_CALLBACK (modest_msg_view_window_on_row_changed),
1322 priv->row_deleted_handler =
1323 g_signal_connect (priv->header_model, "row-deleted",
1324 G_CALLBACK (modest_msg_view_window_on_row_deleted),
1326 priv->rows_reordered_handler =
1327 g_signal_connect (priv->header_model, "rows-reordered",
1328 G_CALLBACK (modest_msg_view_window_on_row_reordered),
1331 check_dimming_rules_after_change (window);
1335 modest_msg_view_window_on_row_reordered (GtkTreeModel *header_model,
1339 ModestMsgViewWindow *window)
1341 ModestMsgViewWindowPrivate *priv = NULL;
1342 gboolean already_changed = FALSE;
1344 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(window);
1346 /* If the current row was reordered select the proper next
1347 valid row. The same if the next row reference changes */
1348 if (!priv->row_reference ||
1349 !gtk_tree_row_reference_valid (priv->row_reference))
1352 if (priv->next_row_reference &&
1353 gtk_tree_row_reference_valid (priv->next_row_reference)) {
1354 GtkTreePath *cur, *next;
1355 /* Check that the order is still the correct one */
1356 cur = gtk_tree_row_reference_get_path (priv->row_reference);
1357 next = gtk_tree_row_reference_get_path (priv->next_row_reference);
1358 gtk_tree_path_next (cur);
1359 if (gtk_tree_path_compare (cur, next) != 0) {
1360 gtk_tree_row_reference_free (priv->next_row_reference);
1361 priv->next_row_reference = gtk_tree_row_reference_copy (priv->row_reference);
1362 select_next_valid_row (header_model, &(priv->next_row_reference), FALSE, priv->is_outbox);
1363 already_changed = TRUE;
1365 gtk_tree_path_free (cur);
1366 gtk_tree_path_free (next);
1368 if (priv->next_row_reference)
1369 gtk_tree_row_reference_free (priv->next_row_reference);
1370 /* Update next row reference */
1371 priv->next_row_reference = gtk_tree_row_reference_copy (priv->row_reference);
1372 select_next_valid_row (header_model, &(priv->next_row_reference), FALSE, priv->is_outbox);
1373 already_changed = TRUE;
1376 check_dimming_rules_after_change (window);
1379 /* The modest_msg_view_window_update_model_replaced implements update
1380 * function for ModestHeaderViewObserver. Checks whether the TnyFolder
1381 * actually belongs to the header-view is the same as the TnyFolder of
1382 * the message of msg-view or not. If they are different, there is
1383 * nothing to do. If they are the same, then the model has replaced and
1384 * the reference in msg-view shall be replaced from the old model to
1385 * the new model. In this case the view will be detached from it's
1386 * header folder. From this point the next/prev buttons are dimmed.
1389 modest_msg_view_window_update_model_replaced (ModestHeaderViewObserver *observer,
1390 GtkTreeModel *model,
1391 const gchar *tny_folder_id)
1393 ModestMsgViewWindowPrivate *priv = NULL;
1394 ModestMsgViewWindow *window = NULL;
1396 g_assert(MODEST_IS_HEADER_VIEW_OBSERVER(observer));
1397 g_assert(MODEST_IS_MSG_VIEW_WINDOW(observer));
1399 window = MODEST_MSG_VIEW_WINDOW(observer);
1400 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(window);
1402 /* If there is an other folder in the header-view then we do
1403 * not care about it's model (msg list). Else if the
1404 * header-view shows the folder the msg shown by us is in, we
1405 * shall replace our model reference and make some check. */
1406 if(model == NULL || tny_folder_id == NULL ||
1407 (priv->header_folder_id && !g_str_equal(tny_folder_id, priv->header_folder_id)))
1410 /* Model is changed(replaced), so we should forget the old
1411 * one. Because there might be other references and there
1412 * might be some change on the model even if we unreferenced
1413 * it, we need to disconnect our signals here. */
1414 if (priv->header_model) {
1415 if (g_signal_handler_is_connected(G_OBJECT (priv->header_model),
1416 priv->row_changed_handler))
1417 g_signal_handler_disconnect(G_OBJECT (priv->header_model),
1418 priv->row_changed_handler);
1419 if (g_signal_handler_is_connected(G_OBJECT (priv->header_model),
1420 priv->row_deleted_handler))
1421 g_signal_handler_disconnect(G_OBJECT (priv->header_model),
1422 priv->row_deleted_handler);
1423 if (g_signal_handler_is_connected(G_OBJECT (priv->header_model),
1424 priv->row_inserted_handler))
1425 g_signal_handler_disconnect(G_OBJECT (priv->header_model),
1426 priv->row_inserted_handler);
1427 if (g_signal_handler_is_connected(G_OBJECT (priv->header_model),
1428 priv->rows_reordered_handler))
1429 g_signal_handler_disconnect(G_OBJECT (priv->header_model),
1430 priv->rows_reordered_handler);
1433 if (priv->row_reference)
1434 gtk_tree_row_reference_free (priv->row_reference);
1435 if (priv->next_row_reference)
1436 gtk_tree_row_reference_free (priv->next_row_reference);
1437 g_object_unref(priv->header_model);
1440 priv->row_changed_handler = 0;
1441 priv->row_deleted_handler = 0;
1442 priv->row_inserted_handler = 0;
1443 priv->rows_reordered_handler = 0;
1444 priv->next_row_reference = NULL;
1445 priv->row_reference = NULL;
1446 priv->header_model = NULL;
1449 priv->header_model = g_object_ref (model);
1451 /* Also we must connect to the new model for row insertions.
1452 * Only for insertions now. We will need other ones only after
1453 * the msg is show by msg-view is added to the new model. */
1454 priv->row_inserted_handler =
1455 g_signal_connect (priv->header_model, "row-inserted",
1456 G_CALLBACK(modest_msg_view_window_on_row_inserted),
1459 modest_ui_actions_check_menu_dimming_rules(MODEST_WINDOW(window));
1460 modest_ui_actions_check_toolbar_dimming_rules(MODEST_WINDOW(window));
1464 modest_msg_view_window_toolbar_on_transfer_mode (ModestMsgViewWindow *self)
1466 ModestMsgViewWindowPrivate *priv= NULL;
1468 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self), FALSE);
1469 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
1471 return priv->progress_hint;
1475 modest_msg_view_window_get_header (ModestMsgViewWindow *self)
1477 ModestMsgViewWindowPrivate *priv= NULL;
1479 TnyHeader *header = NULL;
1480 GtkTreePath *path = NULL;
1483 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self), NULL);
1484 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
1486 /* If the message was not obtained from a treemodel,
1487 * for instance if it was opened directly by the search UI:
1489 if (priv->header_model == NULL ||
1490 priv->row_reference == NULL ||
1491 !gtk_tree_row_reference_valid (priv->row_reference)) {
1492 msg = modest_msg_view_window_get_message (self);
1494 header = tny_msg_get_header (msg);
1495 g_object_unref (msg);
1500 /* Get iter of the currently selected message in the header view: */
1501 path = gtk_tree_row_reference_get_path (priv->row_reference);
1502 g_return_val_if_fail (path != NULL, NULL);
1503 gtk_tree_model_get_iter (priv->header_model,
1507 /* Get current message header */
1508 gtk_tree_model_get (priv->header_model, &iter,
1509 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
1512 gtk_tree_path_free (path);
1517 modest_msg_view_window_get_message (ModestMsgViewWindow *self)
1519 ModestMsgViewWindowPrivate *priv;
1521 g_return_val_if_fail (self, NULL);
1523 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(self);
1525 return tny_msg_view_get_msg (TNY_MSG_VIEW (priv->msg_view));
1529 modest_msg_view_window_get_message_uid (ModestMsgViewWindow *self)
1531 ModestMsgViewWindowPrivate *priv;
1533 g_return_val_if_fail (self, NULL);
1535 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
1537 return (const gchar*) priv->msg_uid;
1540 /* Used for the Ctrl+F accelerator */
1542 modest_msg_view_window_toggle_isearch_toolbar (GtkWidget *obj,
1545 ModestMsgViewWindow *window = MODEST_MSG_VIEW_WINDOW (data);
1546 ModestMsgViewWindowPrivate *priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1548 if (GTK_WIDGET_VISIBLE (priv->isearch_toolbar)) {
1549 modest_msg_view_window_isearch_toolbar_close (obj, data);
1551 modest_msg_view_window_show_isearch_toolbar (obj, data);
1555 /* Handler for menu option */
1557 modest_msg_view_window_show_isearch_toolbar (GtkWidget *obj,
1560 ModestMsgViewWindow *window = MODEST_MSG_VIEW_WINDOW (data);
1561 ModestMsgViewWindowPrivate *priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1563 gtk_widget_show (priv->isearch_toolbar);
1564 modest_isearch_toolbar_highlight_entry (MODEST_ISEARCH_TOOLBAR (priv->isearch_toolbar), TRUE);
1567 /* Handler for click on the "X" close button in isearch toolbar */
1569 modest_msg_view_window_isearch_toolbar_close (GtkWidget *widget,
1570 ModestMsgViewWindow *obj)
1572 ModestMsgViewWindowPrivate *priv;
1574 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (obj);
1577 gtk_widget_hide (priv->isearch_toolbar);
1578 modest_msg_view_grab_focus (MODEST_MSG_VIEW (priv->msg_view));
1582 modest_msg_view_window_isearch_toolbar_search (GtkWidget *widget,
1583 ModestMsgViewWindow *obj)
1585 const gchar *current_search;
1586 ModestMsgViewWindowPrivate *priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (obj);
1588 if (modest_mime_part_view_is_empty (MODEST_MIME_PART_VIEW (priv->msg_view))) {
1589 modest_platform_system_banner (NULL, NULL, _("mail_ib_nothing_to_find"));
1593 current_search = modest_isearch_toolbar_get_search (MODEST_ISEARCH_TOOLBAR (widget));
1595 if ((current_search == NULL) || (strcmp (current_search, "") == 0)) {
1596 modest_platform_system_banner (NULL, NULL, _CS_FIND_REP_ENTER_TEXT);
1600 if ((priv->last_search == NULL) || (strcmp (priv->last_search, current_search) != 0)) {
1602 g_free (priv->last_search);
1603 priv->last_search = g_strdup (current_search);
1604 result = modest_isearch_view_search (MODEST_ISEARCH_VIEW (priv->msg_view),
1607 modest_platform_system_banner (NULL, NULL,
1608 _HL_IB_FIND_NO_MATCHES);
1609 g_free (priv->last_search);
1610 priv->last_search = NULL;
1612 modest_isearch_toolbar_highlight_entry (MODEST_ISEARCH_TOOLBAR (priv->isearch_toolbar), TRUE);
1615 if (!modest_isearch_view_search_next (MODEST_ISEARCH_VIEW (priv->msg_view))) {
1616 modest_platform_system_banner (NULL, NULL,
1617 _HL_IB_FIND_COMPLETE);
1618 g_free (priv->last_search);
1619 priv->last_search = NULL;
1621 modest_isearch_toolbar_highlight_entry (MODEST_ISEARCH_TOOLBAR (priv->isearch_toolbar), TRUE);
1628 modest_msg_view_window_set_zoom (ModestWindow *window,
1631 ModestMsgViewWindowPrivate *priv;
1632 ModestWindowPrivate *parent_priv;
1634 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window));
1636 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1637 parent_priv = MODEST_WINDOW_GET_PRIVATE (window);
1638 modest_zoomable_set_zoom (MODEST_ZOOMABLE (priv->msg_view), zoom);
1643 modest_msg_view_window_get_zoom (ModestWindow *window)
1645 ModestMsgViewWindowPrivate *priv;
1647 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), 1.0);
1649 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1650 return modest_zoomable_get_zoom (MODEST_ZOOMABLE (priv->msg_view));
1654 modest_msg_view_window_zoom_plus (ModestWindow *window)
1657 ModestMsgViewWindowPrivate *priv;
1661 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), 1.0);
1662 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1664 zoom_level = modest_zoomable_get_zoom (MODEST_ZOOMABLE (priv->msg_view));
1666 if (zoom_level >= 2.0) {
1667 modest_platform_system_banner (NULL, NULL,
1668 _CS_MAX_ZOOM_LEVEL_REACHED);
1670 } else if (zoom_level >= 1.5) {
1672 } else if (zoom_level >= 1.2) {
1674 } else if (zoom_level >= 1.0) {
1676 } else if (zoom_level >= 0.8) {
1678 } else if (zoom_level >= 0.5) {
1684 /* set zoom level */
1685 int_zoom = (gint) rint (zoom_level*100.0+0.1);
1686 banner_text = g_strdup_printf (_HL_IB_ZOOM, int_zoom);
1687 modest_platform_information_banner (GTK_WIDGET (window), NULL, banner_text);
1688 g_free (banner_text);
1689 modest_zoomable_set_zoom (MODEST_ZOOMABLE (priv->msg_view), zoom_level);
1695 modest_msg_view_window_zoom_minus (ModestWindow *window)
1698 ModestMsgViewWindowPrivate *priv;
1702 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), 1.0);
1703 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1705 zoom_level = modest_zoomable_get_zoom (MODEST_ZOOMABLE (priv->msg_view));
1707 if (zoom_level <= 0.5) {
1708 modest_platform_system_banner (NULL, NULL,
1709 _CS_MIN_ZOOM_LEVEL_REACHED);
1711 } else if (zoom_level <= 0.8) {
1713 } else if (zoom_level <= 1.0) {
1715 } else if (zoom_level <= 1.2) {
1717 } else if (zoom_level <= 1.5) {
1719 } else if (zoom_level <= 2.0) {
1725 /* set zoom level */
1726 int_zoom = (gint) rint (zoom_level*100.0+0.1);
1727 banner_text = g_strdup_printf (_HL_IB_ZOOM, int_zoom);
1728 modest_platform_information_banner (GTK_WIDGET (window), NULL, banner_text);
1729 g_free (banner_text);
1730 modest_zoomable_set_zoom (MODEST_ZOOMABLE (priv->msg_view), zoom_level);
1736 modest_msg_view_window_key_event (GtkWidget *window,
1742 focus = gtk_window_get_focus (GTK_WINDOW (window));
1744 /* for the isearch toolbar case */
1745 if (focus && GTK_IS_ENTRY (focus)) {
1746 if (event->keyval == GDK_BackSpace) {
1748 copy = gdk_event_copy ((GdkEvent *) event);
1749 gtk_widget_event (focus, copy);
1750 gdk_event_free (copy);
1760 modest_msg_view_window_last_message_selected (ModestMsgViewWindow *window)
1763 ModestMsgViewWindowPrivate *priv;
1764 GtkTreeIter tmp_iter;
1765 gboolean is_last_selected;
1767 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), TRUE);
1768 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1770 /*if no model (so no rows at all), then virtually we are the last*/
1771 if (!priv->header_model || !priv->row_reference)
1774 if (!gtk_tree_row_reference_valid (priv->row_reference))
1777 path = gtk_tree_row_reference_get_path (priv->row_reference);
1781 is_last_selected = TRUE;
1782 while (is_last_selected) {
1784 gtk_tree_path_next (path);
1785 if (!gtk_tree_model_get_iter (priv->header_model, &tmp_iter, path))
1787 gtk_tree_model_get (priv->header_model, &tmp_iter,
1788 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
1791 if (msg_is_visible (header, priv->is_outbox))
1792 is_last_selected = FALSE;
1793 g_object_unref(G_OBJECT(header));
1796 gtk_tree_path_free (path);
1797 return is_last_selected;
1801 modest_msg_view_window_has_headers_model (ModestMsgViewWindow *window)
1803 ModestMsgViewWindowPrivate *priv;
1805 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), TRUE);
1806 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1808 return priv->header_model != NULL;
1812 modest_msg_view_window_is_search_result (ModestMsgViewWindow *window)
1814 ModestMsgViewWindowPrivate *priv;
1816 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), TRUE);
1817 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1819 return priv->is_search_result;
1823 msg_is_visible (TnyHeader *header, gboolean check_outbox)
1825 if ((tny_header_get_flags(header) & TNY_HEADER_FLAG_DELETED))
1827 if (!check_outbox) {
1830 ModestTnySendQueueStatus status;
1831 status = modest_tny_all_send_queues_get_msg_status (header);
1832 return ((status != MODEST_TNY_SEND_QUEUE_FAILED) &&
1833 (status != MODEST_TNY_SEND_QUEUE_SENDING));
1838 modest_msg_view_window_first_message_selected (ModestMsgViewWindow *window)
1841 ModestMsgViewWindowPrivate *priv;
1842 gboolean is_first_selected;
1843 GtkTreeIter tmp_iter;
1845 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), TRUE);
1846 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1848 /*if no model (so no rows at all), then virtually we are the first*/
1849 if (!priv->header_model || !priv->row_reference)
1852 if (!gtk_tree_row_reference_valid (priv->row_reference))
1855 path = gtk_tree_row_reference_get_path (priv->row_reference);
1859 is_first_selected = TRUE;
1860 while (is_first_selected) {
1862 if(!gtk_tree_path_prev (path))
1864 /* Here the 'if' is needless for logic, but let make sure
1865 * iter is valid for gtk_tree_model_get. */
1866 if (!gtk_tree_model_get_iter (priv->header_model, &tmp_iter, path))
1868 gtk_tree_model_get (priv->header_model, &tmp_iter,
1869 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
1872 if (msg_is_visible (header, priv->is_outbox))
1873 is_first_selected = FALSE;
1874 g_object_unref(G_OBJECT(header));
1877 gtk_tree_path_free (path);
1878 return is_first_selected;
1885 GtkTreeRowReference *row_reference;
1889 message_reader_performer (gboolean canceled,
1891 GtkWindow *parent_window,
1892 TnyAccount *account,
1895 ModestMailOperation *mail_op = NULL;
1896 MsgReaderInfo *info;
1898 info = (MsgReaderInfo *) user_data;
1899 if (canceled || err) {
1900 update_window_title (MODEST_MSG_VIEW_WINDOW (parent_window));
1901 modest_ui_actions_on_close_window (NULL, MODEST_WINDOW (parent_window));
1905 /* Register the header - it'll be unregistered in the callback */
1907 modest_window_mgr_register_header (modest_runtime_get_window_mgr (), info->header, NULL);
1909 /* New mail operation */
1910 mail_op = modest_mail_operation_new_with_error_handling (G_OBJECT(parent_window),
1911 modest_ui_actions_disk_operations_error_handler,
1914 modest_mail_operation_queue_add (modest_runtime_get_mail_operation_queue (), mail_op);
1916 modest_mail_operation_get_msg (mail_op, info->header, TRUE, view_msg_cb, info->row_reference);
1918 modest_mail_operation_find_msg (mail_op, info->folder, info->msg_uid, TRUE, view_msg_cb, NULL);
1919 g_object_unref (mail_op);
1921 /* Update dimming rules */
1922 modest_ui_actions_check_toolbar_dimming_rules (MODEST_WINDOW (parent_window));
1923 modest_ui_actions_check_menu_dimming_rules (MODEST_WINDOW (parent_window));
1926 /* Frees. The row_reference will be freed by the view_msg_cb callback */
1927 g_free (info->msg_uid);
1929 g_object_unref (info->folder);
1931 g_object_unref (info->header);
1932 g_slice_free (MsgReaderInfo, info);
1937 * Reads the message whose summary item is @header. It takes care of
1938 * several things, among others:
1940 * If the message was not previously downloaded then ask the user
1941 * before downloading. If there is no connection launch the connection
1942 * dialog. Update toolbar dimming rules.
1944 * Returns: TRUE if the mail operation was started, otherwise if the
1945 * user do not want to download the message, or if the user do not
1946 * want to connect, then the operation is not issued
1949 message_reader (ModestMsgViewWindow *window,
1950 ModestMsgViewWindowPrivate *priv,
1952 const gchar *msg_uid,
1954 GtkTreeRowReference *row_reference)
1956 ModestWindowMgr *mgr;
1957 TnyAccount *account = NULL;
1958 MsgReaderInfo *info;
1960 /* We set the header from model while we're loading */
1961 tny_header_view_set_header (TNY_HEADER_VIEW (priv->msg_view), header);
1962 modest_window_set_title (MODEST_WINDOW (window), _CS_UPDATING);
1968 g_object_ref (folder);
1970 mgr = modest_runtime_get_window_mgr ();
1971 /* Msg download completed */
1972 if (!header || !(tny_header_get_flags (header) & TNY_HEADER_FLAG_CACHED)) {
1974 /* Ask the user if he wants to download the message if
1976 if (!tny_device_is_online (modest_runtime_get_device())) {
1977 GtkResponseType response;
1979 response = modest_platform_run_confirmation_dialog (GTK_WINDOW (window),
1980 _("mcen_nc_get_msg"));
1981 if (response == GTK_RESPONSE_CANCEL) {
1982 update_window_title (window);
1987 folder = tny_header_get_folder (header);
1989 info = g_slice_new (MsgReaderInfo);
1990 info->msg_uid = g_strdup (msg_uid);
1992 info->header = g_object_ref (header);
1994 info->header = NULL;
1996 info->folder = g_object_ref (folder);
1998 info->folder = NULL;
1999 if (row_reference) {
2000 info->row_reference = gtk_tree_row_reference_copy (row_reference);
2002 info->row_reference = NULL;
2005 /* Offer the connection dialog if necessary */
2006 modest_platform_connect_if_remote_and_perform ((GtkWindow *) window,
2008 TNY_FOLDER_STORE (folder),
2009 message_reader_performer,
2012 g_object_unref (folder);
2018 folder = tny_header_get_folder (header);
2021 account = tny_folder_get_account (folder);
2023 info = g_slice_new (MsgReaderInfo);
2024 info->msg_uid = g_strdup (msg_uid);
2026 info->folder = g_object_ref (folder);
2028 info->folder = NULL;
2030 info->header = g_object_ref (header);
2032 info->header = NULL;
2034 info->row_reference = gtk_tree_row_reference_copy (row_reference);
2036 info->row_reference = NULL;
2038 message_reader_performer (FALSE, NULL, (GtkWindow *) window, account, info);
2040 g_object_unref (account);
2042 g_object_unref (folder);
2048 modest_msg_view_window_select_next_message (ModestMsgViewWindow *window)
2050 ModestMsgViewWindowPrivate *priv;
2051 GtkTreePath *path= NULL;
2052 GtkTreeIter tmp_iter;
2054 gboolean retval = TRUE;
2055 GtkTreeRowReference *row_reference = NULL;
2057 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), FALSE);
2058 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
2060 if (!priv->row_reference)
2063 /* Update the next row reference if it's not valid. This could
2064 happen if for example the header which it was pointing to,
2065 was deleted. The best place to do it is in the row-deleted
2066 handler but the tinymail model do not work like the glib
2067 tree models and reports the deletion when the row is still
2069 if (!gtk_tree_row_reference_valid (priv->next_row_reference)) {
2070 if (priv->next_row_reference) {
2071 gtk_tree_row_reference_free (priv->next_row_reference);
2073 if (gtk_tree_row_reference_valid (priv->row_reference)) {
2074 priv->next_row_reference = gtk_tree_row_reference_copy (priv->row_reference);
2075 select_next_valid_row (priv->header_model, &(priv->next_row_reference), FALSE, priv->is_outbox);
2077 priv->next_row_reference = NULL;
2080 if (priv->next_row_reference)
2081 path = gtk_tree_row_reference_get_path (priv->next_row_reference);
2085 row_reference = gtk_tree_row_reference_copy (priv->next_row_reference);
2087 gtk_tree_model_get_iter (priv->header_model,
2090 gtk_tree_path_free (path);
2092 gtk_tree_model_get (priv->header_model, &tmp_iter,
2093 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
2096 /* Read the message & show it */
2097 if (!message_reader (window, priv, header, NULL, NULL, row_reference)) {
2100 gtk_tree_row_reference_free (row_reference);
2103 g_object_unref (header);
2109 modest_msg_view_window_select_previous_message (ModestMsgViewWindow *window)
2111 ModestMsgViewWindowPrivate *priv = NULL;
2113 gboolean finished = FALSE;
2114 gboolean retval = FALSE;
2116 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), FALSE);
2117 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
2119 if (priv->row_reference && !gtk_tree_row_reference_valid (priv->row_reference)) {
2120 gtk_tree_row_reference_free (priv->row_reference);
2121 priv->row_reference = NULL;
2124 /* Return inmediatly if there is no header model */
2125 if (!priv->header_model || !priv->row_reference)
2128 path = gtk_tree_row_reference_get_path (priv->row_reference);
2129 while (!finished && gtk_tree_path_prev (path)) {
2133 gtk_tree_model_get_iter (priv->header_model, &iter, path);
2134 gtk_tree_model_get (priv->header_model, &iter,
2135 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
2139 if (msg_is_visible (header, priv->is_outbox)) {
2140 GtkTreeRowReference *row_reference;
2141 row_reference = gtk_tree_row_reference_new (priv->header_model, path);
2142 /* Read the message & show it */
2143 retval = message_reader (window, priv, header, NULL, NULL, row_reference);
2144 gtk_tree_row_reference_free (row_reference);
2148 g_object_unref (header);
2152 gtk_tree_path_free (path);
2157 view_msg_cb (ModestMailOperation *mail_op,
2164 ModestMsgViewWindow *self = NULL;
2165 ModestMsgViewWindowPrivate *priv = NULL;
2166 GtkTreeRowReference *row_reference = NULL;
2168 /* Unregister the header (it was registered before creating the mail operation) */
2169 modest_window_mgr_unregister_header (modest_runtime_get_window_mgr (), header);
2171 row_reference = (GtkTreeRowReference *) user_data;
2174 gtk_tree_row_reference_free (row_reference);
2175 self = (ModestMsgViewWindow *) modest_mail_operation_get_source (mail_op);
2177 /* Restore window title */
2178 update_window_title (self);
2179 modest_ui_actions_on_close_window (NULL, MODEST_WINDOW (self));
2180 g_object_unref (self);
2185 /* If there was any error */
2186 if (!modest_ui_actions_msg_retrieval_check (mail_op, header, msg)) {
2188 gtk_tree_row_reference_free (row_reference);
2189 self = (ModestMsgViewWindow *) modest_mail_operation_get_source (mail_op);
2191 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
2192 /* First we check if the parent is a folder window */
2193 if (priv->msg_uid && !modest_window_mgr_get_folder_window (MODEST_WINDOW_MGR (modest_runtime_get_window_mgr ()))) {
2195 TnyAccount *account = NULL;
2196 GtkWidget *header_window = NULL;
2198 is_merge = g_str_has_prefix (priv->msg_uid, "merge:");
2200 /* Get the account */
2202 account = tny_account_store_find_account (TNY_ACCOUNT_STORE (modest_runtime_get_account_store ()),
2205 if (is_merge || account) {
2206 TnyFolder *folder = NULL;
2208 /* Try to get the message, if it's already downloaded
2209 we don't need to connect */
2211 folder = modest_tny_folder_store_find_folder_from_uri (TNY_FOLDER_STORE (account),
2214 ModestTnyAccountStore *account_store;
2215 ModestTnyLocalFoldersAccount *local_folders_account;
2217 account_store = modest_runtime_get_account_store ();
2218 local_folders_account = MODEST_TNY_LOCAL_FOLDERS_ACCOUNT (
2219 modest_tny_account_store_get_local_folders_account (account_store));
2220 folder = modest_tny_local_folders_account_get_merged_outbox (local_folders_account);
2221 g_object_unref (local_folders_account);
2223 if (account) g_object_unref (account);
2226 header_window = (GtkWidget *)
2227 modest_header_window_new (
2229 modest_window_get_active_account (MODEST_WINDOW (self)),
2230 modest_window_get_active_mailbox (MODEST_WINDOW (self)));
2231 if (!modest_window_mgr_register_window (modest_runtime_get_window_mgr (),
2232 MODEST_WINDOW (header_window),
2234 gtk_widget_destroy (GTK_WIDGET (header_window));
2236 gtk_widget_show_all (GTK_WIDGET (header_window));
2238 g_object_unref (folder);
2244 /* Restore window title */
2245 update_window_title (self);
2246 modest_ui_actions_on_close_window (NULL, MODEST_WINDOW (self));
2247 g_object_unref (self);
2252 if (msg && TNY_IS_CAMEL_BS_MSG (msg)) {
2254 body = modest_tny_msg_find_body_part (msg, TRUE);
2256 if (body && !tny_camel_bs_mime_part_is_fetched (TNY_CAMEL_BS_MIME_PART (body))) {
2257 /* We have body structure but not the body mime part. What we do
2258 * is restarting load of message */
2259 self = (ModestMsgViewWindow *) modest_mail_operation_get_source (mail_op);
2260 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
2262 tny_header_unset_flag (TNY_HEADER (header), TNY_HEADER_FLAG_CACHED);
2264 modest_msg_view_window_reload (self);
2267 gtk_tree_row_reference_free (row_reference);
2268 g_object_unref (body);
2273 g_object_unref (body);
2276 /* Get the window */
2277 self = (ModestMsgViewWindow *) modest_mail_operation_get_source (mail_op);
2278 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self));
2279 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
2281 /* Update the row reference */
2282 if (priv->row_reference != NULL) {
2283 gtk_tree_row_reference_free (priv->row_reference);
2284 priv->row_reference = (row_reference && gtk_tree_row_reference_valid (row_reference))?gtk_tree_row_reference_copy (row_reference):NULL;
2285 if (priv->next_row_reference != NULL) {
2286 gtk_tree_row_reference_free (priv->next_row_reference);
2288 if (priv->row_reference) {
2289 priv->next_row_reference = gtk_tree_row_reference_copy (priv->row_reference);
2290 select_next_valid_row (priv->header_model, &(priv->next_row_reference), TRUE, priv->is_outbox);
2292 priv->next_row_reference = NULL;
2296 /* Mark header as read */
2297 if (!(tny_header_get_flags (header) & TNY_HEADER_FLAG_SEEN))
2298 tny_header_set_flag (header, TNY_HEADER_FLAG_SEEN);
2300 /* Set new message */
2301 if (priv->msg_view != NULL && TNY_IS_MSG_VIEW (priv->msg_view)) {
2302 tny_msg_view_set_msg (TNY_MSG_VIEW (priv->msg_view), msg);
2303 modest_msg_view_window_update_priority (self);
2304 update_window_title (MODEST_MSG_VIEW_WINDOW (self));
2305 update_branding (MODEST_MSG_VIEW_WINDOW (self));
2306 modest_msg_view_grab_focus (MODEST_MSG_VIEW (priv->msg_view));
2309 /* Set the new message uid of the window */
2310 if (priv->msg_uid) {
2311 g_free (priv->msg_uid);
2312 priv->msg_uid = modest_tny_folder_get_header_unique_id (header);
2315 /* Notify the observers */
2316 g_signal_emit (G_OBJECT (self), signals[MSG_CHANGED_SIGNAL],
2317 0, priv->header_model, priv->row_reference);
2319 /* Sync the flags if the message is not opened from a header
2320 model, i.e, if it's opened from a notification */
2321 if (!priv->header_model)
2325 g_object_unref (self);
2327 gtk_tree_row_reference_free (row_reference);
2331 modest_msg_view_window_get_folder_type (ModestMsgViewWindow *window)
2333 ModestMsgViewWindowPrivate *priv;
2335 TnyFolderType folder_type;
2337 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
2339 folder_type = TNY_FOLDER_TYPE_UNKNOWN;
2341 msg = tny_msg_view_get_msg (TNY_MSG_VIEW (priv->msg_view));
2345 folder = tny_msg_get_folder (msg);
2347 folder_type = modest_tny_folder_guess_folder_type (folder);
2348 g_object_unref (folder);
2350 g_object_unref (msg);
2358 modest_msg_view_window_update_priority (ModestMsgViewWindow *window)
2360 ModestMsgViewWindowPrivate *priv;
2361 TnyHeader *header = NULL;
2362 TnyHeaderFlags flags = 0;
2364 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
2366 if (priv->header_model && priv->row_reference && gtk_tree_row_reference_valid (priv->row_reference)) {
2368 GtkTreePath *path = NULL;
2370 path = gtk_tree_row_reference_get_path (priv->row_reference);
2371 g_return_if_fail (path != NULL);
2372 gtk_tree_model_get_iter (priv->header_model,
2374 gtk_tree_row_reference_get_path (priv->row_reference));
2376 gtk_tree_model_get (priv->header_model, &iter, TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
2378 gtk_tree_path_free (path);
2381 msg = tny_msg_view_get_msg (TNY_MSG_VIEW (priv->msg_view));
2383 header = tny_msg_get_header (msg);
2384 g_object_unref (msg);
2389 flags = tny_header_get_flags (header);
2390 g_object_unref(G_OBJECT(header));
2393 modest_msg_view_set_priority (MODEST_MSG_VIEW(priv->msg_view), flags);
2398 toolbar_resize (ModestMsgViewWindow *self)
2400 ModestMsgViewWindowPrivate *priv = NULL;
2401 ModestWindowPrivate *parent_priv = NULL;
2403 gint static_button_size;
2404 ModestWindowMgr *mgr;
2406 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self));
2407 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
2408 parent_priv = MODEST_WINDOW_GET_PRIVATE(self);
2410 mgr = modest_runtime_get_window_mgr ();
2411 static_button_size = modest_window_mgr_get_fullscreen_mode (mgr)?120:120;
2413 if (parent_priv->toolbar) {
2414 /* Set expandable and homogeneous tool buttons */
2415 widget = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/ToolbarMessageReply");
2416 gtk_tool_item_set_expand (GTK_TOOL_ITEM (widget), TRUE);
2417 gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (widget), TRUE);
2418 widget = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/ToolbarMessageReplyAll");
2419 gtk_tool_item_set_expand (GTK_TOOL_ITEM (widget), TRUE);
2420 gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (widget), TRUE);
2421 widget = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/ToolbarMessageForward");
2422 gtk_tool_item_set_expand (GTK_TOOL_ITEM (widget), TRUE);
2423 gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (widget), TRUE);
2424 widget = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/ToolbarDeleteMessage");
2425 gtk_tool_item_set_expand (GTK_TOOL_ITEM (widget), TRUE);
2426 gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (widget), TRUE);
2427 widget = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/ToolbarDownloadExternalImages");
2428 gtk_tool_item_set_expand (GTK_TOOL_ITEM (widget), TRUE);
2429 gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (widget), TRUE);
2430 gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (priv->next_toolitem), TRUE);
2431 gtk_tool_item_set_expand (GTK_TOOL_ITEM (priv->next_toolitem), TRUE);
2432 gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (priv->prev_toolitem), TRUE);
2433 gtk_tool_item_set_expand (GTK_TOOL_ITEM (priv->prev_toolitem), TRUE);
2438 modest_msg_view_window_show_toolbar (ModestWindow *self,
2439 gboolean show_toolbar)
2441 ModestMsgViewWindowPrivate *priv = NULL;
2442 ModestWindowPrivate *parent_priv;
2444 parent_priv = MODEST_WINDOW_GET_PRIVATE(self);
2445 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(self);
2447 /* Set optimized view status */
2448 priv->optimized_view = !show_toolbar;
2450 if (!parent_priv->toolbar) {
2451 parent_priv->toolbar = gtk_ui_manager_get_widget (parent_priv->ui_manager,
2454 #ifdef MODEST_TOOLKIT_HILDON2
2455 gtk_toolbar_set_icon_size (GTK_TOOLBAR (parent_priv->toolbar), HILDON_ICON_SIZE_FINGER);
2457 gtk_toolbar_set_icon_size (GTK_TOOLBAR (parent_priv->toolbar), GTK_ICON_SIZE_LARGE_TOOLBAR);
2459 gtk_widget_set_no_show_all (parent_priv->toolbar, TRUE);
2461 priv->next_toolitem = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/ToolbarMessageNext");
2462 priv->prev_toolitem = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/ToolbarMessageBack");
2463 toolbar_resize (MODEST_MSG_VIEW_WINDOW (self));
2465 modest_window_add_toolbar (MODEST_WINDOW (self),
2466 GTK_TOOLBAR (parent_priv->toolbar));
2471 /* Quick hack: this prevents toolbar icons "dance" when progress bar show status is changed */
2472 /* TODO: resize mode migth be GTK_RESIZE_QUEUE, in order to avoid unneccesary shows */
2473 gtk_container_set_resize_mode (GTK_CONTAINER(parent_priv->toolbar), GTK_RESIZE_IMMEDIATE);
2475 gtk_widget_show (GTK_WIDGET (parent_priv->toolbar));
2476 if (modest_msg_view_window_transfer_mode_enabled (MODEST_MSG_VIEW_WINDOW (self)))
2477 set_progress_hint (MODEST_MSG_VIEW_WINDOW (self), TRUE);
2479 set_progress_hint (MODEST_MSG_VIEW_WINDOW (self), FALSE);
2482 gtk_widget_set_no_show_all (parent_priv->toolbar, TRUE);
2483 gtk_widget_hide (GTK_WIDGET (parent_priv->toolbar));
2488 modest_msg_view_window_clipboard_owner_change (GtkClipboard *clipboard,
2490 ModestMsgViewWindow *window)
2492 if (!GTK_WIDGET_VISIBLE (window))
2495 modest_window_check_dimming_rules_group (MODEST_WINDOW (window), MODEST_DIMMING_RULES_CLIPBOARD);
2499 modest_msg_view_window_transfer_mode_enabled (ModestMsgViewWindow *self)
2501 ModestMsgViewWindowPrivate *priv;
2503 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self), FALSE);
2504 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(self);
2506 return priv->progress_hint;
2510 observers_empty (ModestMsgViewWindow *self)
2513 ModestMsgViewWindowPrivate *priv;
2514 gboolean is_empty = TRUE;
2515 guint pending_ops = 0;
2517 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(self);
2518 tmp = priv->progress_widgets;
2520 /* Check all observers */
2521 while (tmp && is_empty) {
2522 pending_ops = modest_progress_object_num_pending_operations (MODEST_PROGRESS_OBJECT(tmp->data));
2523 is_empty = pending_ops == 0;
2525 tmp = g_slist_next(tmp);
2532 on_account_removed (TnyAccountStore *account_store,
2533 TnyAccount *account,
2536 /* Do nothing if it's a transport account, because we only
2537 show the messages of a store account */
2538 if (tny_account_get_account_type(account) == TNY_ACCOUNT_TYPE_STORE) {
2539 const gchar *parent_acc = NULL;
2540 const gchar *our_acc = NULL;
2542 our_acc = modest_window_get_active_account (MODEST_WINDOW (user_data));
2543 parent_acc = modest_tny_account_get_parent_modest_account_name_for_server_account (account);
2545 /* Close this window if I'm showing a message of the removed account */
2546 if (our_acc && parent_acc && strcmp (parent_acc, our_acc) == 0)
2547 modest_ui_actions_on_close_window (NULL, MODEST_WINDOW (user_data));
2552 on_mail_operation_started (ModestMailOperation *mail_op,
2555 ModestMsgViewWindow *self;
2556 ModestMailOperationTypeOperation op_type;
2558 ModestMsgViewWindowPrivate *priv;
2559 GObject *source = NULL;
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;
2565 source = modest_mail_operation_get_source(mail_op);
2566 if (G_OBJECT (self) == source) {
2567 if (op_type == MODEST_MAIL_OPERATION_TYPE_RECEIVE ||
2568 op_type == MODEST_MAIL_OPERATION_TYPE_OPEN ||
2569 op_type == MODEST_MAIL_OPERATION_TYPE_DELETE) {
2570 set_progress_hint (self, TRUE);
2572 modest_progress_object_add_operation (
2573 MODEST_PROGRESS_OBJECT (tmp->data),
2575 tmp = g_slist_next (tmp);
2579 g_object_unref (source);
2581 /* Update dimming rules */
2582 check_dimming_rules_after_change (self);
2586 on_mail_operation_finished (ModestMailOperation *mail_op,
2589 ModestMsgViewWindow *self;
2590 ModestMailOperationTypeOperation op_type;
2592 ModestMsgViewWindowPrivate *priv;
2594 self = MODEST_MSG_VIEW_WINDOW (user_data);
2595 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
2596 op_type = modest_mail_operation_get_type_operation (mail_op);
2597 tmp = priv->progress_widgets;
2599 if (op_type == MODEST_MAIL_OPERATION_TYPE_RECEIVE ||
2600 op_type == MODEST_MAIL_OPERATION_TYPE_OPEN ||
2601 op_type == MODEST_MAIL_OPERATION_TYPE_DELETE) {
2603 modest_progress_object_remove_operation (MODEST_PROGRESS_OBJECT (tmp->data),
2605 tmp = g_slist_next (tmp);
2608 /* If no more operations are being observed, NORMAL mode is enabled again */
2609 if (observers_empty (self)) {
2610 set_progress_hint (self, FALSE);
2614 /* Update dimming rules. We have to do this right here
2615 and not in view_msg_cb because at that point the
2616 transfer mode is still enabled so the dimming rule
2617 won't let the user delete the message that has been
2618 readed for example */
2619 check_dimming_rules_after_change (self);
2623 on_queue_changed (ModestMailOperationQueue *queue,
2624 ModestMailOperation *mail_op,
2625 ModestMailOperationQueueNotification type,
2626 ModestMsgViewWindow *self)
2628 ModestMsgViewWindowPrivate *priv;
2630 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
2632 /* If this operations was created by another window, do nothing */
2633 if (!modest_mail_operation_is_mine (mail_op, G_OBJECT(self)))
2636 if (type == MODEST_MAIL_OPERATION_QUEUE_OPERATION_ADDED) {
2637 priv->sighandlers = modest_signal_mgr_connect (priv->sighandlers,
2639 "operation-started",
2640 G_CALLBACK (on_mail_operation_started),
2642 priv->sighandlers = modest_signal_mgr_connect (priv->sighandlers,
2644 "operation-finished",
2645 G_CALLBACK (on_mail_operation_finished),
2647 } else if (type == MODEST_MAIL_OPERATION_QUEUE_OPERATION_REMOVED) {
2648 priv->sighandlers = modest_signal_mgr_disconnect (priv->sighandlers,
2650 "operation-started");
2651 priv->sighandlers = modest_signal_mgr_disconnect (priv->sighandlers,
2653 "operation-finished");
2658 modest_msg_view_window_get_attachments (ModestMsgViewWindow *win)
2660 ModestMsgViewWindowPrivate *priv;
2661 TnyList *selected_attachments = NULL;
2663 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (win), NULL);
2664 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (win);
2666 /* In Hildon 2.2 as there's no selection we assume we have all attachments selected */
2667 selected_attachments = modest_msg_view_get_attachments (MODEST_MSG_VIEW (priv->msg_view));
2669 return selected_attachments;
2673 ModestMsgViewWindow *self;
2675 gchar *attachment_uid;
2676 } DecodeAsyncHelper;
2679 on_decode_to_stream_async_handler (TnyMimePart *mime_part,
2685 DecodeAsyncHelper *helper = (DecodeAsyncHelper *) user_data;
2686 const gchar *content_type;
2688 if (cancelled || err) {
2691 if ((err->domain == TNY_ERROR_DOMAIN) &&
2692 (err->code == TNY_IO_ERROR_WRITE) &&
2693 (errno == ENOSPC)) {
2694 msg = g_strdup_printf (_KR("cerm_device_memory_full"), "");
2696 msg = g_strdup (_("mail_ib_file_operation_failed"));
2698 modest_platform_information_banner (NULL, NULL, msg);
2704 /* It could happen that the window was closed. So we
2705 assume it is a cancelation */
2706 if (!GTK_WIDGET_VISIBLE (helper->self))
2709 /* Remove the progress hint */
2710 set_progress_hint (helper->self, FALSE);
2712 content_type = tny_mime_part_get_content_type (mime_part);
2713 if (g_str_has_prefix (content_type, "message/rfc822")) {
2714 ModestWindowMgr *mgr;
2715 ModestWindow *msg_win = NULL;
2718 const gchar *mailbox;
2719 TnyStream *file_stream;
2722 fd = g_open (helper->file_path, O_RDONLY, 0644);
2724 file_stream = tny_fs_stream_new (fd);
2726 mgr = modest_runtime_get_window_mgr ();
2728 account = g_strdup (modest_window_get_active_account (MODEST_WINDOW (helper->self)));
2729 mailbox = modest_window_get_active_mailbox (MODEST_WINDOW (helper->self));
2732 account = modest_account_mgr_get_default_account (modest_runtime_get_account_mgr ());
2734 msg = tny_camel_msg_new ();
2735 tny_camel_msg_parse (msg, file_stream);
2736 msg_win = modest_msg_view_window_new_for_attachment (TNY_MSG (msg), account, mailbox, helper->attachment_uid);
2737 modest_window_set_zoom (MODEST_WINDOW (msg_win),
2738 modest_window_get_zoom (MODEST_WINDOW (helper->self)));
2739 if (modest_window_mgr_register_window (mgr, msg_win, MODEST_WINDOW (helper->self)))
2740 gtk_widget_show_all (GTK_WIDGET (msg_win));
2742 gtk_widget_destroy (GTK_WIDGET (msg_win));
2743 g_object_unref (msg);
2744 g_object_unref (file_stream);
2746 modest_platform_information_banner (NULL, NULL, _("mail_ib_file_operation_failed"));
2751 /* make the file read-only */
2752 g_chmod(helper->file_path, 0444);
2754 /* Activate the file */
2755 modest_platform_activate_file (helper->file_path, tny_mime_part_get_content_type (mime_part));
2760 g_object_unref (helper->self);
2761 g_free (helper->file_path);
2762 g_free (helper->attachment_uid);
2763 g_slice_free (DecodeAsyncHelper, helper);
2767 view_attachment_connect_handler (gboolean canceled,
2769 GtkWindow *parent_window,
2770 TnyAccount *account,
2774 if (canceled || err) {
2775 g_object_unref (part);
2779 modest_msg_view_window_view_attachment (MODEST_MSG_VIEW_WINDOW (parent_window),
2781 g_object_unref (part);
2785 modest_msg_view_window_view_attachment (ModestMsgViewWindow *window,
2786 TnyMimePart *mime_part)
2788 ModestMsgViewWindowPrivate *priv;
2789 const gchar *msg_uid;
2790 gchar *attachment_uid = NULL;
2791 gint attachment_index = 0;
2792 TnyList *attachments;
2794 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window));
2795 g_return_if_fail (TNY_IS_MIME_PART (mime_part) || (mime_part == NULL));
2796 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
2798 msg_uid = modest_msg_view_window_get_message_uid (MODEST_MSG_VIEW_WINDOW (window));
2799 attachments = modest_msg_view_get_attachments (MODEST_MSG_VIEW (priv->msg_view));
2800 attachment_index = modest_list_index (attachments, (GObject *) mime_part);
2801 g_object_unref (attachments);
2803 if (msg_uid && attachment_index >= 0) {
2804 attachment_uid = g_strdup_printf ("%s/%d", msg_uid, attachment_index);
2807 if (mime_part == NULL) {
2808 gboolean error = FALSE;
2809 TnyList *selected_attachments = modest_msg_view_get_selected_attachments (MODEST_MSG_VIEW (priv->msg_view));
2810 if (selected_attachments == NULL || tny_list_get_length (selected_attachments) == 0) {
2812 } else if (tny_list_get_length (selected_attachments) > 1) {
2813 modest_platform_system_banner (NULL, NULL, _("mcen_ib_unable_to_display_more"));
2817 iter = tny_list_create_iterator (selected_attachments);
2818 mime_part = (TnyMimePart *) tny_iterator_get_current (iter);
2819 g_object_unref (iter);
2821 if (selected_attachments)
2822 g_object_unref (selected_attachments);
2827 g_object_ref (mime_part);
2830 if (tny_mime_part_is_purged (mime_part))
2833 if (TNY_IS_CAMEL_BS_MIME_PART (mime_part) &&
2834 !tny_camel_bs_mime_part_is_fetched (TNY_CAMEL_BS_MIME_PART (mime_part))) {
2836 TnyAccount *account;
2838 is_merge = g_str_has_prefix (priv->msg_uid, "merge:");
2840 /* Get the account */
2842 account = tny_account_store_find_account (TNY_ACCOUNT_STORE (modest_runtime_get_account_store ()),
2845 if (!tny_device_is_online (modest_runtime_get_device())) {
2846 modest_platform_connect_and_perform (GTK_WINDOW (window),
2848 TNY_ACCOUNT (account),
2849 (ModestConnectedPerformer) view_attachment_connect_handler,
2850 g_object_ref (mime_part));
2855 if (!modest_tny_mime_part_is_msg (mime_part) && tny_mime_part_get_filename (mime_part)) {
2856 gchar *filepath = NULL;
2857 const gchar *att_filename = tny_mime_part_get_filename (mime_part);
2858 gboolean show_error_banner = FALSE;
2859 TnyFsStream *temp_stream = NULL;
2860 temp_stream = modest_utils_create_temp_stream (att_filename, attachment_uid,
2863 if (temp_stream != NULL) {
2864 ModestAccountMgr *mgr;
2865 DecodeAsyncHelper *helper;
2866 gboolean decode_in_provider;
2867 ModestProtocol *protocol;
2868 const gchar *account;
2870 /* Activate progress hint */
2871 set_progress_hint (window, TRUE);
2873 helper = g_slice_new0 (DecodeAsyncHelper);
2874 helper->self = g_object_ref (window);
2875 helper->file_path = g_strdup (filepath);
2876 helper->attachment_uid = g_strdup (attachment_uid);
2878 decode_in_provider = FALSE;
2879 mgr = modest_runtime_get_account_mgr ();
2880 account = modest_window_get_active_account (MODEST_WINDOW (window));
2881 if (modest_account_mgr_account_is_multimailbox (mgr, account, &protocol)) {
2882 if (MODEST_IS_ACCOUNT_PROTOCOL (protocol)) {
2884 uri = g_strconcat ("file://", filepath, NULL);
2885 decode_in_provider =
2886 modest_account_protocol_decode_part_to_stream_async (
2887 MODEST_ACCOUNT_PROTOCOL (protocol),
2890 TNY_STREAM (temp_stream),
2891 on_decode_to_stream_async_handler,
2898 if (!decode_in_provider)
2899 tny_mime_part_decode_to_stream_async (mime_part, TNY_STREAM (temp_stream),
2900 on_decode_to_stream_async_handler,
2903 g_object_unref (temp_stream);
2904 /* NOTE: files in the temporary area will be automatically
2905 * cleaned after some time if they are no longer in use */
2908 const gchar *content_type;
2909 /* the file may already exist but it isn't writable,
2910 * let's try to open it anyway */
2911 content_type = tny_mime_part_get_content_type (mime_part);
2912 modest_platform_activate_file (filepath, content_type);
2914 g_warning ("%s: modest_utils_create_temp_stream failed", __FUNCTION__);
2915 show_error_banner = TRUE;
2920 if (show_error_banner)
2921 modest_platform_information_banner (NULL, NULL, _("mail_ib_file_operation_failed"));
2922 } else if (!modest_tny_mime_part_is_msg (mime_part)) {
2923 ModestWindowMgr *mgr;
2924 ModestWindow *msg_win = NULL;
2925 TnyMsg *current_msg;
2929 current_msg = modest_msg_view_window_get_message (MODEST_MSG_VIEW_WINDOW (window));
2930 mgr = modest_runtime_get_window_mgr ();
2931 header = tny_msg_get_header (TNY_MSG (current_msg));
2932 found = modest_window_mgr_find_registered_message_uid (mgr,
2937 g_debug ("window for this body is already being created");
2940 /* it's not found, so create a new window for it */
2941 modest_window_mgr_register_header (mgr, header, attachment_uid); /* register the uid before building the window */
2942 gchar *account = g_strdup (modest_window_get_active_account (MODEST_WINDOW (window)));
2943 const gchar *mailbox = modest_window_get_active_mailbox (MODEST_WINDOW (window));
2945 account = modest_account_mgr_get_default_account (modest_runtime_get_account_mgr ());
2947 msg_win = modest_msg_view_window_new_with_other_body (TNY_MSG (current_msg), TNY_MIME_PART (mime_part),
2948 account, mailbox, attachment_uid);
2950 modest_window_set_zoom (MODEST_WINDOW (msg_win),
2951 modest_window_get_zoom (MODEST_WINDOW (window)));
2952 if (modest_window_mgr_register_window (mgr, msg_win, MODEST_WINDOW (window)))
2953 gtk_widget_show_all (GTK_WIDGET (msg_win));
2955 gtk_widget_destroy (GTK_WIDGET (msg_win));
2957 g_object_unref (current_msg);
2959 /* message attachment */
2960 TnyHeader *header = NULL;
2961 ModestWindowMgr *mgr;
2962 ModestWindow *msg_win = NULL;
2965 header = tny_msg_get_header (TNY_MSG (mime_part));
2966 mgr = modest_runtime_get_window_mgr ();
2967 found = modest_window_mgr_find_registered_header (mgr, header, &msg_win);
2970 /* if it's found, but there is no msg_win, it's probably in the process of being created;
2971 * thus, we don't do anything */
2972 g_debug ("window for is already being created");
2974 /* it's not found, so create a new window for it */
2975 modest_window_mgr_register_header (mgr, header, attachment_uid); /* register the uid before building the window */
2976 gchar *account = g_strdup (modest_window_get_active_account (MODEST_WINDOW (window)));
2977 const gchar *mailbox = modest_window_get_active_mailbox (MODEST_WINDOW (window));
2979 account = modest_account_mgr_get_default_account (modest_runtime_get_account_mgr ());
2980 msg_win = modest_msg_view_window_new_for_attachment (TNY_MSG (mime_part), account,
2981 mailbox, attachment_uid);
2982 modest_window_set_zoom (MODEST_WINDOW (msg_win),
2983 modest_window_get_zoom (MODEST_WINDOW (window)));
2984 if (modest_window_mgr_register_window (mgr, msg_win, MODEST_WINDOW (window)))
2985 gtk_widget_show_all (GTK_WIDGET (msg_win));
2987 gtk_widget_destroy (GTK_WIDGET (msg_win));
2993 g_free (attachment_uid);
2995 g_object_unref (mime_part);
3007 GnomeVFSResult result;
3009 ModestMsgViewWindow *window;
3012 static void save_mime_part_info_free (SaveMimePartInfo *info, gboolean with_struct);
3013 static gboolean idle_save_mime_part_show_result (SaveMimePartInfo *info);
3014 static gpointer save_mime_part_to_file (SaveMimePartInfo *info);
3015 static void save_mime_parts_to_file_with_checks (GtkWindow *parent, SaveMimePartInfo *info);
3018 save_mime_part_info_free (SaveMimePartInfo *info, gboolean with_struct)
3022 for (node = info->pairs; node != NULL; node = g_list_next (node)) {
3023 SaveMimePartPair *pair = (SaveMimePartPair *) node->data;
3024 g_free (pair->filename);
3025 g_object_unref (pair->part);
3026 g_slice_free (SaveMimePartPair, pair);
3028 g_list_free (info->pairs);
3031 g_object_unref (info->window);
3032 info->window = NULL;
3034 g_slice_free (SaveMimePartInfo, info);
3039 idle_save_mime_part_show_result (SaveMimePartInfo *info)
3041 /* This is a GDK lock because we are an idle callback and
3042 * modest_platform_system_banner is or does Gtk+ code */
3044 gdk_threads_enter (); /* CHECKED */
3045 if (info->result == GNOME_VFS_ERROR_CANCELLED) {
3047 } else if (info->result == GNOME_VFS_OK) {
3048 modest_platform_system_banner (NULL, NULL, _CS_SAVED);
3049 } else if (info->result == GNOME_VFS_ERROR_NO_SPACE) {
3052 /* Check if the uri belongs to the external mmc */
3053 if (g_str_has_prefix (info->uri, g_getenv (MODEST_MMC1_VOLUMEPATH_ENV)))
3054 msg = g_strdup_printf (_KR("cerm_device_memory_full"), "");
3056 msg = g_strdup (_KR("cerm_memory_card_full"));
3057 modest_platform_information_banner (NULL, NULL, msg);
3060 modest_platform_system_banner (NULL, NULL, _("mail_ib_file_operation_failed"));
3062 save_mime_part_info_free (info, FALSE);
3063 gdk_threads_leave (); /* CHECKED */
3069 save_mime_part_to_file_connect_handler (gboolean canceled,
3071 GtkWindow *parent_window,
3072 TnyAccount *account,
3073 SaveMimePartInfo *info)
3075 if (canceled || err) {
3076 g_idle_add ((GSourceFunc) idle_save_mime_part_show_result, info);
3078 g_thread_create ((GThreadFunc)save_mime_part_to_file, info, FALSE, NULL);
3083 save_mime_part_to_file_connect_idle (SaveMimePartInfo *info)
3086 TnyAccount *account;
3087 ModestMsgViewWindowPrivate *priv;
3089 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (info->window);
3091 is_merge = g_str_has_prefix (priv->msg_uid, "merge:");
3094 /* Get the account */
3096 account = tny_account_store_find_account (TNY_ACCOUNT_STORE (modest_runtime_get_account_store ()),
3099 modest_platform_connect_and_perform (GTK_WINDOW (info->window),
3101 TNY_ACCOUNT (account),
3102 (ModestConnectedPerformer) save_mime_part_to_file_connect_handler,
3108 save_mime_part_to_file (SaveMimePartInfo *info)
3110 GnomeVFSHandle *handle;
3112 SaveMimePartPair *pair = (SaveMimePartPair *) info->pairs->data;
3114 if (TNY_IS_CAMEL_BS_MIME_PART (pair->part) &&
3115 !tny_camel_bs_mime_part_is_fetched (TNY_CAMEL_BS_MIME_PART (pair->part))) {
3116 g_idle_add ((GSourceFunc) save_mime_part_to_file_connect_idle, info);
3120 info->result = gnome_vfs_create (&handle, pair->filename, GNOME_VFS_OPEN_WRITE, FALSE, 0644);
3121 if (info->result == GNOME_VFS_OK) {
3122 GError *error = NULL;
3123 gboolean decode_in_provider;
3125 ModestAccountMgr *mgr;
3126 const gchar *account;
3127 ModestProtocol *protocol = NULL;
3129 stream = tny_vfs_stream_new (handle);
3131 decode_in_provider = FALSE;
3132 mgr = modest_runtime_get_account_mgr ();
3133 account = modest_window_get_active_account (MODEST_WINDOW (info->window));
3134 if (modest_account_mgr_account_is_multimailbox (mgr, account, &protocol)) {
3135 if (MODEST_IS_ACCOUNT_PROTOCOL (protocol)) {
3136 decode_in_provider =
3137 modest_account_protocol_decode_part_to_stream (
3138 MODEST_ACCOUNT_PROTOCOL (protocol),
3146 if (!decode_in_provider)
3147 written = tny_mime_part_decode_to_stream (pair->part, stream, &error);
3150 g_warning ("modest: could not save attachment %s: %d (%s)\n", pair->filename, error?error->code:-1, error?error->message:"Unknown error");
3152 if ((error->domain == TNY_ERROR_DOMAIN) &&
3153 (error->code == TNY_IO_ERROR_WRITE) &&
3154 (errno == ENOSPC)) {
3155 info->result = GNOME_VFS_ERROR_NO_SPACE;
3157 info->result = GNOME_VFS_ERROR_IO;
3160 g_object_unref (G_OBJECT (stream));
3162 g_warning ("Could not create save attachment %s: %s\n",
3163 pair->filename, gnome_vfs_result_to_string (info->result));
3166 /* Go on saving remaining files */
3167 info->pairs = g_list_remove_link (info->pairs, info->pairs);
3168 if (info->pairs != NULL) {
3169 save_mime_part_to_file (info);
3171 g_idle_add ((GSourceFunc) idle_save_mime_part_show_result, info);
3178 save_mime_parts_to_file_with_checks (GtkWindow *parent,
3179 SaveMimePartInfo *info)
3181 gboolean is_ok = TRUE;
3182 gint replaced_files = 0;
3183 const GList *files = info->pairs;
3184 const GList *iter, *to_replace = NULL;
3186 for (iter = files; (iter != NULL) && (replaced_files < 2); iter = g_list_next(iter)) {
3187 SaveMimePartPair *pair = iter->data;
3188 gchar *unescaped = g_uri_unescape_string (pair->filename, NULL);
3190 if (modest_utils_file_exists (unescaped)) {
3192 if (replaced_files == 1)
3197 if (replaced_files) {
3200 if (replaced_files == 1) {
3201 SaveMimePartPair *pair = to_replace->data;
3202 const gchar *basename = strrchr (pair->filename, G_DIR_SEPARATOR) + 1;
3203 gchar *escaped_basename, *message;
3205 escaped_basename = g_uri_unescape_string (basename, NULL);
3206 message = g_strdup_printf ("%s\n%s",
3208 (escaped_basename) ? escaped_basename : "");
3209 response = modest_platform_run_confirmation_dialog (parent, message);
3211 g_free (escaped_basename);
3213 response = modest_platform_run_confirmation_dialog (parent,
3214 _FM_REPLACE_MULTIPLE);
3216 if (response != GTK_RESPONSE_OK)
3221 save_mime_part_info_free (info, TRUE);
3223 g_thread_create ((GThreadFunc)save_mime_part_to_file, info, FALSE, NULL);
3228 typedef struct _SaveAttachmentsInfo {
3229 TnyList *attachments_list;
3230 ModestMsgViewWindow *window;
3231 } SaveAttachmentsInfo;
3234 save_attachments_response (GtkDialog *dialog,
3238 TnyList *mime_parts;
3240 GList *files_to_save = NULL;
3241 gchar *current_folder;
3242 SaveAttachmentsInfo *sa_info = (SaveAttachmentsInfo *) user_data;
3244 mime_parts = TNY_LIST (sa_info->attachments_list);
3246 if (arg1 != GTK_RESPONSE_OK)
3249 chooser_uri = gtk_file_chooser_get_uri (GTK_FILE_CHOOSER (dialog));
3250 current_folder = gtk_file_chooser_get_current_folder_uri (GTK_FILE_CHOOSER (dialog));
3251 if (current_folder && *current_folder != '\0') {
3253 modest_conf_set_string (modest_runtime_get_conf (), MODEST_CONF_LATEST_SAVE_ATTACHMENT_PATH,
3254 current_folder,&err);
3256 g_debug ("Error storing latest used folder: %s", err->message);
3260 g_free (current_folder);
3262 if (!modest_utils_folder_writable (chooser_uri)) {
3263 const gchar *err_msg;
3265 #ifdef MODEST_PLATFORM_MAEMO
3266 if (modest_maemo_utils_in_usb_mode ()) {
3267 err_msg = dgettext ("hildon-status-bar-usb", "usbh_ib_mmc_usb_connected");
3269 err_msg = _FM_READ_ONLY_LOCATION;
3272 err_msg = _FM_READ_ONLY_LOCATION;
3274 modest_platform_system_banner (NULL, NULL, err_msg);
3278 iter = tny_list_create_iterator (mime_parts);
3279 while (!tny_iterator_is_done (iter)) {
3280 TnyMimePart *mime_part = (TnyMimePart *) tny_iterator_get_current (iter);
3282 if ((modest_tny_mime_part_is_attachment_for_modest (mime_part)) &&
3283 !tny_mime_part_is_purged (mime_part) &&
3284 (tny_mime_part_get_filename (mime_part) != NULL)) {
3285 SaveMimePartPair *pair;
3287 pair = g_slice_new0 (SaveMimePartPair);
3289 if (tny_list_get_length (mime_parts) > 1) {
3291 gnome_vfs_escape_slashes (tny_mime_part_get_filename (mime_part));
3292 pair->filename = g_build_filename (chooser_uri, escaped, NULL);
3295 pair->filename = g_strdup (chooser_uri);
3297 pair->part = mime_part;
3298 files_to_save = g_list_prepend (files_to_save, pair);
3300 tny_iterator_next (iter);
3302 g_object_unref (iter);
3305 if (files_to_save != NULL) {
3306 SaveMimePartInfo *info = g_slice_new0 (SaveMimePartInfo);
3307 info->pairs = files_to_save;
3308 info->result = TRUE;
3309 info->uri = g_strdup (chooser_uri);
3310 info->window = g_object_ref (sa_info->window);
3311 save_mime_parts_to_file_with_checks ((GtkWindow *) dialog, info);
3313 g_free (chooser_uri);
3316 /* Free and close the dialog */
3317 g_object_unref (mime_parts);
3318 g_object_unref (sa_info->window);
3319 g_slice_free (SaveAttachmentsInfo, sa_info);
3320 gtk_widget_destroy (GTK_WIDGET (dialog));
3324 modest_msg_view_window_save_attachments (ModestMsgViewWindow *window,
3325 TnyList *mime_parts)
3327 ModestMsgViewWindowPrivate *priv;
3328 GtkWidget *save_dialog = NULL;
3329 gchar *conf_folder = NULL;
3330 gchar *filename = NULL;
3331 gchar *save_multiple_str = NULL;
3332 const gchar *root_folder = "file:///";
3334 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window));
3335 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
3337 if (mime_parts == NULL) {
3338 /* In Hildon 2.2 save and delete operate over all the attachments as there's no
3339 * selection available */
3340 mime_parts = modest_msg_view_get_attachments (MODEST_MSG_VIEW (priv->msg_view));
3341 if (mime_parts && !modest_toolkit_utils_select_attachments (GTK_WINDOW (gtk_widget_get_toplevel (window)), mime_parts, FALSE)) {
3342 g_object_unref (mime_parts);
3345 if (mime_parts == NULL || tny_list_get_length (mime_parts) == 0) {
3347 g_object_unref (mime_parts);
3353 g_object_ref (mime_parts);
3356 /* prepare dialog */
3357 if (tny_list_get_length (mime_parts) == 1) {
3359 /* only one attachment selected */
3360 iter = tny_list_create_iterator (mime_parts);
3361 TnyMimePart *mime_part = (TnyMimePart *) tny_iterator_get_current (iter);
3362 g_object_unref (iter);
3363 if (!modest_tny_mime_part_is_msg (mime_part) &&
3364 modest_tny_mime_part_is_attachment_for_modest (mime_part) &&
3365 !tny_mime_part_is_purged (mime_part)) {
3366 filename = g_strdup (tny_mime_part_get_filename (mime_part));
3368 /* TODO: show any error? */
3369 g_warning ("%s: Tried to save a non-file attachment", __FUNCTION__);
3370 g_object_unref (mime_parts);
3373 g_object_unref (mime_part);
3375 gint num = tny_list_get_length (mime_parts);
3376 save_multiple_str = g_strdup_printf (dngettext("hildon-fm",
3377 "sfil_va_number_of_objects_attachment",
3378 "sfil_va_number_of_objects_attachments",
3382 /* Creation of hildon file chooser dialog for saving */
3383 save_dialog = modest_toolkit_factory_create_file_chooser_dialog (modest_runtime_get_toolkit_factory (),
3385 (GtkWindow *) window,
3386 GTK_FILE_CHOOSER_ACTION_SAVE);
3388 /* Get last used folder */
3389 conf_folder = modest_conf_get_string (modest_runtime_get_conf (),
3390 MODEST_CONF_LATEST_SAVE_ATTACHMENT_PATH, NULL);
3392 /* File chooser stops working if we select "file:///" as current folder */
3393 if (conf_folder && g_ascii_strcasecmp (root_folder, conf_folder) != 0) {
3394 g_free (conf_folder);
3398 if (conf_folder && conf_folder[0] != '\0') {
3399 gtk_file_chooser_set_current_folder_uri (GTK_FILE_CHOOSER (save_dialog), conf_folder);
3402 /* Set the default folder to documents folder */
3403 docs_folder = (gchar *) g_strdup(g_get_user_special_dir (G_USER_DIRECTORY_DOCUMENTS));
3406 docs_folder = g_build_filename (g_getenv (MYDOCS_ENV), DOCS_FOLDER, NULL);
3408 gtk_file_chooser_set_current_folder (GTK_FILE_CHOOSER (save_dialog), docs_folder);
3409 g_free (docs_folder);
3411 g_free (conf_folder);
3415 gtk_file_chooser_set_current_name (GTK_FILE_CHOOSER (save_dialog),
3420 /* if multiple, set multiple string */
3421 if (save_multiple_str) {
3422 g_object_set (G_OBJECT (save_dialog), "save-multiple", save_multiple_str, NULL);
3423 gtk_window_set_title (GTK_WINDOW (save_dialog), _FM_SAVE_OBJECT_FILES);
3424 g_free (save_multiple_str);
3427 /* We must run this asynchronously, because the hildon dialog
3428 performs a gtk_dialog_run by itself which leads to gdk
3430 SaveAttachmentsInfo *sa_info;
3431 sa_info = g_slice_new (SaveAttachmentsInfo);
3432 sa_info->attachments_list = mime_parts;
3433 sa_info->window = g_object_ref (window);
3434 g_signal_connect (save_dialog, "response",
3435 G_CALLBACK (save_attachments_response), sa_info);
3437 gtk_widget_show_all (save_dialog);
3441 show_remove_attachment_information (gpointer userdata)
3443 ModestMsgViewWindow *window = (ModestMsgViewWindow *) userdata;
3444 ModestMsgViewWindowPrivate *priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
3446 /* We're outside the main lock */
3447 gdk_threads_enter ();
3449 if (priv->remove_attachment_banner != NULL) {
3450 gtk_widget_destroy (priv->remove_attachment_banner);
3451 g_object_unref (priv->remove_attachment_banner);
3454 priv->remove_attachment_banner = g_object_ref (
3455 modest_platform_animation_banner (NULL, NULL, _("mcen_me_inbox_remove_attachments")));
3457 gdk_threads_leave ();
3463 modest_msg_view_window_remove_attachments (ModestMsgViewWindow *window, gboolean get_all)
3465 ModestMsgViewWindowPrivate *priv;
3466 TnyList *mime_parts = NULL, *tmp;
3467 gchar *confirmation_message;
3473 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window));
3474 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
3476 /* In hildon 2.2 we ignore the get_all flag as we always get all attachments. This is
3477 * because we don't have selection
3479 mime_parts = modest_msg_view_get_attachments (MODEST_MSG_VIEW (priv->msg_view));
3481 /* Remove already purged messages from mime parts list. We use
3482 a copy of the list to remove items in the original one */
3483 tmp = tny_list_copy (mime_parts);
3484 iter = tny_list_create_iterator (tmp);
3485 while (!tny_iterator_is_done (iter)) {
3486 TnyMimePart *part = TNY_MIME_PART (tny_iterator_get_current (iter));
3487 if (tny_mime_part_is_purged (part))
3488 tny_list_remove (mime_parts, (GObject *) part);
3490 g_object_unref (part);
3491 tny_iterator_next (iter);
3493 g_object_unref (tmp);
3494 g_object_unref (iter);
3496 if (!modest_toolkit_utils_select_attachments (GTK_WINDOW (window), mime_parts, TRUE) ||
3497 tny_list_get_length (mime_parts) == 0) {
3498 g_object_unref (mime_parts);
3502 n_attachments = tny_list_get_length (mime_parts);
3503 if (n_attachments == 1) {
3507 iter = tny_list_create_iterator (mime_parts);
3508 part = (TnyMimePart *) tny_iterator_get_current (iter);
3509 g_object_unref (iter);
3510 if (modest_tny_mime_part_is_msg (part)) {
3512 header = tny_msg_get_header (TNY_MSG (part));
3513 filename = tny_header_dup_subject (header);
3514 g_object_unref (header);
3515 if (filename == NULL)
3516 filename = g_strdup (_("mail_va_no_subject"));
3518 filename = g_strdup (tny_mime_part_get_filename (TNY_MIME_PART (part)));
3520 confirmation_message = g_strdup_printf (_("mcen_nc_purge_file_text"), filename);
3522 g_object_unref (part);
3524 confirmation_message = g_strdup_printf (ngettext("mcen_nc_purge_file_text",
3525 "mcen_nc_purge_files_text",
3526 n_attachments), n_attachments);
3528 response = modest_platform_run_confirmation_dialog (GTK_WINDOW (window),
3529 confirmation_message);
3530 g_free (confirmation_message);
3532 if (response != GTK_RESPONSE_OK) {
3533 g_object_unref (mime_parts);
3537 priv->purge_timeout = g_timeout_add (2000, show_remove_attachment_information, window);
3539 iter = tny_list_create_iterator (mime_parts);
3540 while (!tny_iterator_is_done (iter)) {
3543 part = (TnyMimePart *) tny_iterator_get_current (iter);
3544 tny_mime_part_set_purged (TNY_MIME_PART (part));
3545 g_object_unref (part);
3546 tny_iterator_next (iter);
3548 g_object_unref (iter);
3550 msg = tny_msg_view_get_msg (TNY_MSG_VIEW (priv->msg_view));
3551 tny_msg_view_clear (TNY_MSG_VIEW (priv->msg_view));
3552 tny_msg_rewrite_cache (msg);
3553 tny_msg_view_set_msg (TNY_MSG_VIEW (priv->msg_view), msg);
3554 g_object_unref (msg);
3555 update_branding (MODEST_MSG_VIEW_WINDOW (window));
3557 g_object_unref (mime_parts);
3559 if (priv->purge_timeout > 0) {
3560 g_source_remove (priv->purge_timeout);
3561 priv->purge_timeout = 0;
3564 if (priv->remove_attachment_banner) {
3565 gtk_widget_destroy (priv->remove_attachment_banner);
3566 g_object_unref (priv->remove_attachment_banner);
3567 priv->remove_attachment_banner = NULL;
3573 update_window_title (ModestMsgViewWindow *window)
3575 ModestMsgViewWindowPrivate *priv;
3577 TnyHeader *header = NULL;
3578 gchar *subject = NULL;
3580 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
3582 /* Note that if the window is closed while we're retrieving
3583 the message, this widget could de deleted */
3584 if (!priv->msg_view)
3587 msg = tny_msg_view_get_msg (TNY_MSG_VIEW (priv->msg_view));
3589 if (priv->other_body) {
3592 description = modest_tny_mime_part_get_header_value (priv->other_body, "Content-Description");
3594 g_strstrip (description);
3595 subject = description;
3597 } else if (msg != NULL) {
3598 header = tny_msg_get_header (msg);
3599 subject = tny_header_dup_subject (header);
3600 g_object_unref (header);
3601 g_object_unref (msg);
3604 if ((subject == NULL)||(subject[0] == '\0')) {
3606 subject = g_strdup (_("mail_va_no_subject"));
3609 modest_window_set_title (MODEST_WINDOW (window), subject);
3614 on_move_focus (GtkWidget *widget,
3615 GtkDirectionType direction,
3618 g_signal_stop_emission_by_name (G_OBJECT (widget), "move-focus");
3622 fetch_image_open_stream (TnyStreamCache *self, gint64 *expected_size, gchar *uri)
3624 GnomeVFSResult result;
3625 GnomeVFSHandle *handle = NULL;
3626 GnomeVFSFileInfo *info = NULL;
3629 result = gnome_vfs_open (&handle, uri, GNOME_VFS_OPEN_READ);
3630 if (result != GNOME_VFS_OK) {
3635 info = gnome_vfs_file_info_new ();
3636 result = gnome_vfs_get_file_info_from_handle (handle, info, GNOME_VFS_FILE_INFO_DEFAULT);
3637 if (result != GNOME_VFS_OK || ! (info->valid_fields & GNOME_VFS_FILE_INFO_FIELDS_SIZE)) {
3638 /* We put a "safe" default size for going to cache */
3639 *expected_size = (300*1024);
3641 *expected_size = info->size;
3643 gnome_vfs_file_info_unref (info);
3645 stream = tny_vfs_stream_new (handle);
3654 TnyStream *output_stream;
3655 GtkWidget *msg_view;
3660 on_fetch_image_idle_refresh_view (gpointer userdata)
3663 FetchImageData *fidata = (FetchImageData *) userdata;
3665 gdk_threads_enter ();
3666 if (GTK_WIDGET_DRAWABLE (fidata->msg_view)) {
3667 ModestMsgViewWindowPrivate *priv;
3669 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (fidata->window);
3670 priv->fetching_images--;
3671 gtk_widget_queue_draw (fidata->msg_view);
3672 update_progress_hint (MODEST_MSG_VIEW_WINDOW (fidata->window));
3674 gdk_threads_leave ();
3676 g_object_unref (fidata->msg_view);
3677 g_object_unref (fidata->window);
3678 g_slice_free (FetchImageData, fidata);
3683 on_fetch_image_thread (gpointer userdata)
3685 FetchImageData *fidata = (FetchImageData *) userdata;
3686 TnyStreamCache *cache;
3687 TnyStream *cache_stream;
3689 cache = modest_runtime_get_images_cache ();
3691 tny_stream_cache_get_stream (cache,
3693 (TnyStreamCacheOpenStreamFetcher) fetch_image_open_stream,
3694 (gpointer) fidata->uri);
3695 g_free (fidata->cache_id);
3696 g_free (fidata->uri);
3698 if (cache_stream != NULL) {
3701 while (G_LIKELY (!tny_stream_is_eos (cache_stream))) {
3704 nb_read = tny_stream_read (cache_stream, buffer, sizeof (buffer));
3705 if (G_UNLIKELY (nb_read < 0)) {
3707 } else if (G_LIKELY (nb_read > 0)) {
3708 gssize nb_written = 0;
3710 while (G_UNLIKELY (nb_written < nb_read)) {
3713 len = tny_stream_write (fidata->output_stream, buffer + nb_written,
3714 nb_read - nb_written);
3715 if (G_UNLIKELY (len < 0))
3721 tny_stream_close (cache_stream);
3722 g_object_unref (cache_stream);
3725 tny_stream_close (fidata->output_stream);
3726 g_object_unref (fidata->output_stream);
3728 g_idle_add (on_fetch_image_idle_refresh_view, fidata);
3734 on_fetch_image (ModestMsgView *msgview,
3737 ModestMsgViewWindow *window)
3739 const gchar *current_account;
3740 ModestMsgViewWindowPrivate *priv;
3741 FetchImageData *fidata;
3743 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
3745 current_account = modest_window_get_active_account (MODEST_WINDOW (window));
3747 fidata = g_slice_new0 (FetchImageData);
3748 fidata->msg_view = g_object_ref (msgview);
3749 fidata->window = g_object_ref (window);
3750 fidata->uri = g_strdup (uri);
3751 fidata->cache_id = modest_images_cache_get_id (current_account, uri);
3752 fidata->output_stream = g_object_ref (stream);
3754 priv->fetching_images++;
3755 if (g_thread_create (on_fetch_image_thread, fidata, FALSE, NULL) == NULL) {
3756 g_object_unref (fidata->output_stream);
3757 g_free (fidata->cache_id);
3758 g_free (fidata->uri);
3759 g_object_unref (fidata->msg_view);
3760 g_slice_free (FetchImageData, fidata);
3761 tny_stream_close (stream);
3762 priv->fetching_images--;
3763 update_progress_hint (window);
3766 update_progress_hint (window);
3772 setup_menu (ModestMsgViewWindow *self)
3774 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW(self));
3776 /* Settings menu buttons */
3777 modest_window_add_to_menu (MODEST_WINDOW (self), _("mcen_me_viewer_find"), NULL,
3778 MODEST_WINDOW_MENU_CALLBACK (modest_msg_view_window_show_isearch_toolbar),
3779 MODEST_DIMMING_CALLBACK (modest_ui_dimming_rules_on_find_in_msg));
3781 modest_window_add_to_menu (MODEST_WINDOW (self),
3782 dngettext(GETTEXT_PACKAGE,
3783 "mcen_me_move_message",
3784 "mcen_me_move_messages",
3787 MODEST_WINDOW_MENU_CALLBACK (modest_ui_actions_on_move_to),
3788 MODEST_DIMMING_CALLBACK (modest_ui_dimming_rules_on_move_to));
3790 modest_window_add_to_menu (MODEST_WINDOW (self), _("mcen_me_inbox_mark_as_read"), NULL,
3791 MODEST_WINDOW_MENU_CALLBACK (modest_ui_actions_on_mark_as_read),
3792 MODEST_DIMMING_CALLBACK (modest_ui_dimming_rules_on_mark_as_read_msg_in_view));
3794 modest_window_add_to_menu (MODEST_WINDOW (self), _("mcen_me_inbox_mark_as_unread"), NULL,
3795 MODEST_WINDOW_MENU_CALLBACK (modest_ui_actions_on_mark_as_unread),
3796 MODEST_DIMMING_CALLBACK (modest_ui_dimming_rules_on_mark_as_unread_msg_in_view));
3798 modest_window_add_to_menu (MODEST_WINDOW (self), _("mcen_me_viewer_save_attachments"), NULL,
3799 MODEST_WINDOW_MENU_CALLBACK (modest_ui_actions_save_attachments),
3800 MODEST_DIMMING_CALLBACK (modest_ui_dimming_rules_on_save_attachments));
3801 modest_window_add_to_menu (MODEST_WINDOW (self), _("mcen_me_inbox_remove_attachments"), NULL,
3802 MODEST_WINDOW_MENU_CALLBACK (modest_ui_actions_remove_attachments),
3803 MODEST_DIMMING_CALLBACK (modest_ui_dimming_rules_on_remove_attachments));
3805 modest_window_add_to_menu (MODEST_WINDOW (self), _("mcen_me_new_message"), "<Control>n",
3806 MODEST_WINDOW_MENU_CALLBACK (modest_ui_actions_on_new_msg),
3807 MODEST_DIMMING_CALLBACK (modest_ui_dimming_rules_on_new_msg));
3808 modest_window_add_to_menu (MODEST_WINDOW (self), _("mcen_me_viewer_addtocontacts"), NULL,
3809 MODEST_WINDOW_MENU_CALLBACK (modest_ui_actions_add_to_contacts),
3810 MODEST_DIMMING_CALLBACK (modest_ui_dimming_rules_on_add_to_contacts));
3812 modest_window_add_to_menu (MODEST_WINDOW (self), _("mcen_ti_message_properties"), NULL,
3813 MODEST_WINDOW_MENU_CALLBACK (modest_ui_actions_on_details),
3814 MODEST_DIMMING_CALLBACK (modest_ui_dimming_rules_on_details));
3818 modest_msg_view_window_add_to_contacts (ModestMsgViewWindow *self)
3820 ModestMsgViewWindowPrivate *priv;
3821 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
3822 GSList *recipients = NULL;
3825 msg = tny_msg_view_get_msg (TNY_MSG_VIEW (priv->msg_view));
3829 header = modest_msg_view_window_get_header (self);
3832 recipients = modest_tny_msg_header_get_all_recipients_list (header);
3833 g_object_unref (header);
3835 recipients = modest_tny_msg_get_all_recipients_list (msg);
3836 g_object_unref (msg);
3840 /* Offer the user to add recipients to the address book */
3841 modest_address_book_add_address_list_with_selector (recipients, (GtkWindow *) self);
3842 g_slist_foreach (recipients, (GFunc) g_free, NULL); g_slist_free (recipients);
3847 _modest_msg_view_window_map_event (GtkWidget *widget,
3851 ModestMsgViewWindow *self = (ModestMsgViewWindow *) userdata;
3853 update_progress_hint (self);
3859 modest_msg_view_window_fetch_images (ModestMsgViewWindow *self)
3861 ModestMsgViewWindowPrivate *priv;
3862 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
3864 modest_msg_view_request_fetch_images (MODEST_MSG_VIEW (priv->msg_view));
3868 modest_msg_view_window_has_blocked_external_images (ModestMsgViewWindow *self)
3870 ModestMsgViewWindowPrivate *priv;
3871 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
3873 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self), FALSE);
3875 return modest_msg_view_has_blocked_external_images (MODEST_MSG_VIEW (priv->msg_view));
3879 modest_msg_view_window_reload (ModestMsgViewWindow *self)
3881 ModestMsgViewWindowPrivate *priv;
3882 const gchar *msg_uid;
3883 TnyHeader *header = NULL;
3884 TnyFolder *folder = NULL;
3886 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self));
3888 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
3890 header = modest_msg_view_window_get_header (MODEST_MSG_VIEW_WINDOW (self));
3894 folder = tny_header_get_folder (header);
3895 g_object_unref (header);
3900 msg_uid = modest_msg_view_window_get_message_uid (self);
3902 GtkTreeRowReference *row_reference;
3904 if (priv->row_reference && gtk_tree_row_reference_valid (priv->row_reference)) {
3905 row_reference = priv->row_reference;
3907 row_reference = NULL;
3909 if (!message_reader (self, priv, NULL, msg_uid, folder, row_reference))
3910 g_warning ("Shouldn't happen, trying to reload a message failed");
3913 g_object_unref (folder);
3917 update_branding (ModestMsgViewWindow *self)
3919 const gchar *account;
3920 const gchar *mailbox;
3921 ModestAccountMgr *mgr;
3922 ModestProtocol *protocol = NULL;
3923 gchar *service_name = NULL;
3924 const GdkPixbuf *service_icon = NULL;
3925 ModestMsgViewWindowPrivate *priv;
3927 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
3929 account = modest_window_get_active_account (MODEST_WINDOW (self));
3930 mailbox = modest_window_get_active_mailbox (MODEST_WINDOW (self));
3932 mgr = modest_runtime_get_account_mgr ();
3934 if (modest_account_mgr_account_is_multimailbox (mgr, account, &protocol)) {
3935 if (MODEST_IS_ACCOUNT_PROTOCOL (protocol)) {
3936 service_name = modest_account_protocol_get_service_name (MODEST_ACCOUNT_PROTOCOL (protocol),
3938 service_icon = modest_account_protocol_get_service_icon (MODEST_ACCOUNT_PROTOCOL (protocol),
3939 account, mailbox, MODEST_ICON_SIZE_SMALL);
3943 modest_msg_view_set_branding (MODEST_MSG_VIEW (priv->msg_view), service_name, service_icon);
3944 g_free (service_name);
3948 sync_flags (ModestMsgViewWindow *self)
3950 TnyHeader *header = NULL;
3952 header = modest_msg_view_window_get_header (self);
3954 TnyMsg *msg = modest_msg_view_window_get_message (self);
3956 header = tny_msg_get_header (msg);
3957 g_object_unref (msg);
3962 TnyFolder *folder = tny_header_get_folder (header);
3965 ModestMailOperation *mail_op;
3967 /* Sync folder, we need this to save the seen flag */
3968 mail_op = modest_mail_operation_new (NULL);
3969 modest_mail_operation_queue_add (modest_runtime_get_mail_operation_queue (),
3971 modest_mail_operation_sync_folder (mail_op, folder, FALSE, NULL, NULL);
3972 g_object_unref (mail_op);
3973 g_object_unref (folder);
3975 g_object_unref (header);