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;
129 gulong fetch_image_redraw_handler;
132 GtkWidget *remove_attachment_banner;
135 TnyMimePart *other_body;
140 static void modest_msg_view_window_class_init (ModestMsgViewWindowClass *klass);
141 static void modest_msg_view_window_init (ModestMsgViewWindow *obj);
142 static void modest_header_view_observer_init (ModestHeaderViewObserverIface *iface_class);
143 static void modest_msg_view_window_finalize (GObject *obj);
144 static void modest_msg_view_window_show_isearch_toolbar (GtkWidget *obj, gpointer data);
145 static void modest_msg_view_window_isearch_toolbar_close (GtkWidget *widget,
146 ModestMsgViewWindow *obj);
147 static void modest_msg_view_window_isearch_toolbar_search (GtkWidget *widget,
148 ModestMsgViewWindow *obj);
149 static void modest_msg_view_window_toggle_isearch_toolbar (GtkWidget *obj,
151 static void modest_msg_view_window_disconnect_signals (ModestWindow *self);
153 static gdouble modest_msg_view_window_get_zoom (ModestWindow *window);
154 static void modest_msg_view_window_set_zoom (ModestWindow *window,
156 static gboolean modest_msg_view_window_zoom_minus (ModestWindow *window);
157 static gboolean modest_msg_view_window_zoom_plus (ModestWindow *window);
158 static gboolean modest_msg_view_window_key_event (GtkWidget *window,
161 static void modest_msg_view_window_update_priority (ModestMsgViewWindow *window);
163 static void modest_msg_view_window_show_toolbar (ModestWindow *window,
164 gboolean show_toolbar);
166 static void modest_msg_view_window_clipboard_owner_change (GtkClipboard *clipboard,
168 ModestMsgViewWindow *window);
170 static void modest_msg_view_window_on_row_changed (GtkTreeModel *header_model,
173 ModestMsgViewWindow *window);
175 static void modest_msg_view_window_on_row_deleted (GtkTreeModel *header_model,
177 ModestMsgViewWindow *window);
179 static void modest_msg_view_window_on_row_inserted (GtkTreeModel *header_model,
180 GtkTreePath *tree_path,
181 GtkTreeIter *tree_iter,
182 ModestMsgViewWindow *window);
184 static void modest_msg_view_window_on_row_reordered (GtkTreeModel *header_model,
188 ModestMsgViewWindow *window);
190 static void modest_msg_view_window_update_model_replaced (ModestHeaderViewObserver *window,
192 const gchar *tny_folder_id);
194 static void on_queue_changed (ModestMailOperationQueue *queue,
195 ModestMailOperation *mail_op,
196 ModestMailOperationQueueNotification type,
197 ModestMsgViewWindow *self);
199 static void on_account_removed (TnyAccountStore *account_store,
203 static void on_move_focus (GtkWidget *widget,
204 GtkDirectionType direction,
207 static void view_msg_cb (ModestMailOperation *mail_op,
214 static void set_progress_hint (ModestMsgViewWindow *self,
217 static void update_window_title (ModestMsgViewWindow *window);
219 static void init_window (ModestMsgViewWindow *obj);
221 static gboolean msg_is_visible (TnyHeader *header, gboolean check_outbox);
223 static void check_dimming_rules_after_change (ModestMsgViewWindow *window);
225 static gboolean on_fetch_image (ModestMsgView *msgview,
228 ModestMsgViewWindow *window);
230 static gboolean modest_msg_view_window_scroll_child (ModestMsgViewWindow *self,
231 GtkScrollType scroll_type,
234 static gboolean message_reader (ModestMsgViewWindow *window,
235 ModestMsgViewWindowPrivate *priv,
237 const gchar *msg_uid,
239 GtkTreeRowReference *row_reference);
241 static void setup_menu (ModestMsgViewWindow *self);
242 static gboolean _modest_msg_view_window_map_event (GtkWidget *widget,
245 static void update_branding (ModestMsgViewWindow *self);
246 static void sync_flags (ModestMsgViewWindow *self);
248 /* list my signals */
255 static const GtkActionEntry msg_view_toolbar_action_entries [] = {
258 { "ToolbarMessageReply", MODEST_STOCK_REPLY, N_("mcen_me_inbox_reply"), "<CTRL>R", NULL, G_CALLBACK (modest_ui_actions_on_reply) },
259 { "ToolbarMessageReplyAll", MODEST_STOCK_REPLY_ALL, N_("mcen_me_inbox_replytoall"), NULL, NULL, G_CALLBACK (modest_ui_actions_on_reply_all) },
260 { "ToolbarMessageForward", MODEST_STOCK_FORWARD, N_("mcen_me_inbox_forward"), NULL, NULL, G_CALLBACK (modest_ui_actions_on_forward) },
261 { "ToolbarDeleteMessage", MODEST_STOCK_DELETE, N_("qgn_toolb_gene_deletebutton"), NULL, NULL, G_CALLBACK (modest_ui_actions_on_delete_message_or_folder) },
262 { "ToolbarMessageBack", MODEST_TOOLBAR_ICON_PREV, N_("qgn_toolb_gene_back"), NULL, NULL, G_CALLBACK (modest_ui_actions_on_prev) },
263 { "ToolbarMessageNext", MODEST_TOOLBAR_ICON_NEXT, N_("qgn_toolb_gene_forward"), NULL, NULL, G_CALLBACK (modest_ui_actions_on_next) },
264 { "ToolbarDownloadExternalImages", MODEST_TOOLBAR_ICON_DOWNLOAD_IMAGES, N_("mail_bd_external_images"), NULL, NULL, G_CALLBACK (modest_ui_actions_on_fetch_images) },
267 static const GtkToggleActionEntry msg_view_toggle_action_entries [] = {
268 { "FindInMessage", MODEST_TOOLBAR_ICON_FIND, N_("qgn_toolb_gene_find"), "<CTRL>F", NULL, G_CALLBACK (modest_msg_view_window_toggle_isearch_toolbar), FALSE },
271 #define MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE((o), \
272 MODEST_TYPE_MSG_VIEW_WINDOW, \
273 ModestMsgViewWindowPrivate))
275 static GtkWindowClass *parent_class = NULL;
277 /* uncomment the following if you have defined any signals */
278 static guint signals[LAST_SIGNAL] = {0};
281 modest_msg_view_window_get_type (void)
283 static GType my_type = 0;
285 static const GTypeInfo my_info = {
286 sizeof(ModestMsgViewWindowClass),
287 NULL, /* base init */
288 NULL, /* base finalize */
289 (GClassInitFunc) modest_msg_view_window_class_init,
290 NULL, /* class finalize */
291 NULL, /* class data */
292 sizeof(ModestMsgViewWindow),
294 (GInstanceInitFunc) modest_msg_view_window_init,
297 #ifndef MODEST_TOOLKIT_HILDON2
298 my_type = g_type_register_static (MODEST_TYPE_SHELL_WINDOW,
299 "ModestMsgViewWindow",
302 my_type = g_type_register_static (MODEST_TYPE_HILDON2_WINDOW,
303 "ModestMsgViewWindow",
307 static const GInterfaceInfo modest_header_view_observer_info =
309 (GInterfaceInitFunc) modest_header_view_observer_init,
310 NULL, /* interface_finalize */
311 NULL /* interface_data */
314 g_type_add_interface_static (my_type,
315 MODEST_TYPE_HEADER_VIEW_OBSERVER,
316 &modest_header_view_observer_info);
322 save_state (ModestWindow *self)
324 modest_widget_memory_save (modest_runtime_get_conf (),
326 MODEST_CONF_MSG_VIEW_WINDOW_KEY);
330 modest_msg_view_window_scroll_child (ModestMsgViewWindow *self,
331 GtkScrollType scroll_type,
335 ModestMsgViewWindowPrivate *priv;
338 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(self);
340 switch (scroll_type) {
341 case GTK_SCROLL_STEP_UP:
344 case GTK_SCROLL_STEP_DOWN:
347 case GTK_SCROLL_PAGE_UP:
350 case GTK_SCROLL_PAGE_DOWN:
353 case GTK_SCROLL_START:
364 modest_scrollable_scroll ((ModestScrollable *) priv->main_scroll, 0, step);
366 return (gboolean) step;
370 add_scroll_binding (GtkBindingSet *binding_set,
372 GtkScrollType scroll)
374 guint keypad_keyval = keyval - GDK_Left + GDK_KP_Left;
376 gtk_binding_entry_add_signal (binding_set, keyval, 0,
378 GTK_TYPE_SCROLL_TYPE, scroll,
379 G_TYPE_BOOLEAN, FALSE);
380 gtk_binding_entry_add_signal (binding_set, keypad_keyval, 0,
382 GTK_TYPE_SCROLL_TYPE, scroll,
383 G_TYPE_BOOLEAN, FALSE);
387 modest_msg_view_window_class_init (ModestMsgViewWindowClass *klass)
389 GObjectClass *gobject_class;
390 ModestWindowClass *modest_window_class;
391 GtkBindingSet *binding_set;
393 gobject_class = (GObjectClass*) klass;
394 modest_window_class = (ModestWindowClass *) klass;
396 parent_class = g_type_class_peek_parent (klass);
397 gobject_class->finalize = modest_msg_view_window_finalize;
399 modest_window_class->set_zoom_func = modest_msg_view_window_set_zoom;
400 modest_window_class->get_zoom_func = modest_msg_view_window_get_zoom;
401 modest_window_class->zoom_plus_func = modest_msg_view_window_zoom_plus;
402 modest_window_class->zoom_minus_func = modest_msg_view_window_zoom_minus;
403 modest_window_class->show_toolbar_func = modest_msg_view_window_show_toolbar;
404 modest_window_class->disconnect_signals_func = modest_msg_view_window_disconnect_signals;
406 modest_window_class->save_state_func = save_state;
408 klass->scroll_child = modest_msg_view_window_scroll_child;
410 signals[MSG_CHANGED_SIGNAL] =
411 g_signal_new ("msg-changed",
412 G_TYPE_FROM_CLASS (gobject_class),
414 G_STRUCT_OFFSET (ModestMsgViewWindowClass, msg_changed),
416 modest_marshal_VOID__POINTER_POINTER,
417 G_TYPE_NONE, 2, G_TYPE_POINTER, G_TYPE_POINTER);
419 signals[SCROLL_CHILD_SIGNAL] =
420 g_signal_new ("scroll-child",
421 G_TYPE_FROM_CLASS (gobject_class),
422 G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
423 G_STRUCT_OFFSET (ModestMsgViewWindowClass, scroll_child),
425 modest_marshal_BOOLEAN__ENUM_BOOLEAN,
426 G_TYPE_BOOLEAN, 2, GTK_TYPE_SCROLL_TYPE, G_TYPE_BOOLEAN);
428 binding_set = gtk_binding_set_by_class (klass);
429 add_scroll_binding (binding_set, GDK_Up, GTK_SCROLL_STEP_UP);
430 add_scroll_binding (binding_set, GDK_Down, GTK_SCROLL_STEP_DOWN);
431 add_scroll_binding (binding_set, GDK_Page_Up, GTK_SCROLL_PAGE_UP);
432 add_scroll_binding (binding_set, GDK_Page_Down, GTK_SCROLL_PAGE_DOWN);
433 add_scroll_binding (binding_set, GDK_Home, GTK_SCROLL_START);
434 add_scroll_binding (binding_set, GDK_End, GTK_SCROLL_END);
436 g_type_class_add_private (gobject_class, sizeof(ModestMsgViewWindowPrivate));
440 static void modest_header_view_observer_init(
441 ModestHeaderViewObserverIface *iface_class)
443 iface_class->update_func = modest_msg_view_window_update_model_replaced;
447 modest_msg_view_window_init (ModestMsgViewWindow *obj)
449 ModestMsgViewWindowPrivate *priv;
450 ModestWindowPrivate *parent_priv = NULL;
451 GtkActionGroup *action_group = NULL;
452 GError *error = NULL;
454 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(obj);
455 parent_priv = MODEST_WINDOW_GET_PRIVATE(obj);
456 parent_priv->ui_manager = gtk_ui_manager_new();
458 action_group = gtk_action_group_new ("ModestMsgViewWindowActions");
459 gtk_action_group_set_translation_domain (action_group, GETTEXT_PACKAGE);
461 /* Add common actions */
462 gtk_action_group_add_actions (action_group,
463 msg_view_toolbar_action_entries,
464 G_N_ELEMENTS (msg_view_toolbar_action_entries),
466 gtk_action_group_add_toggle_actions (action_group,
467 msg_view_toggle_action_entries,
468 G_N_ELEMENTS (msg_view_toggle_action_entries),
471 gtk_ui_manager_insert_action_group (parent_priv->ui_manager, action_group, 0);
472 g_object_unref (action_group);
474 /* Load the UI definition */
475 gtk_ui_manager_add_ui_from_file (parent_priv->ui_manager, MODEST_UIDIR "modest-msg-view-window-ui.xml",
478 g_printerr ("modest: could not merge modest-msg-view-window-ui.xml: %s\n", error->message);
479 g_error_free (error);
483 priv->is_search_result = FALSE;
484 priv->is_outbox = FALSE;
486 priv->msg_view = NULL;
487 priv->header_model = NULL;
488 priv->header_folder_id = NULL;
489 priv->clipboard_change_handler = 0;
490 priv->queue_change_handler = 0;
491 priv->account_removed_handler = 0;
492 priv->row_changed_handler = 0;
493 priv->row_deleted_handler = 0;
494 priv->row_inserted_handler = 0;
495 priv->rows_reordered_handler = 0;
496 priv->fetch_image_redraw_handler = 0;
497 priv->progress_hint = FALSE;
498 priv->fetching_images = 0;
500 priv->optimized_view = FALSE;
501 priv->purge_timeout = 0;
502 priv->remove_attachment_banner = NULL;
503 priv->msg_uid = NULL;
504 priv->other_body = NULL;
506 priv->sighandlers = NULL;
509 init_window (MODEST_MSG_VIEW_WINDOW(obj));
514 update_progress_hint (ModestMsgViewWindow *self)
516 ModestMsgViewWindowPrivate *priv;
517 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(self);
519 if (GTK_WIDGET_VISIBLE (self)) {
520 modest_window_show_progress (MODEST_WINDOW (self),
521 (priv->progress_hint || (priv->fetching_images > 0))?1:0);
526 set_progress_hint (ModestMsgViewWindow *self,
529 ModestWindowPrivate *parent_priv;
530 ModestMsgViewWindowPrivate *priv;
532 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self));
534 parent_priv = MODEST_WINDOW_GET_PRIVATE(self);
535 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(self);
537 /* Sets current progress hint */
538 priv->progress_hint = enabled;
540 update_progress_hint (self);
546 init_window (ModestMsgViewWindow *obj)
548 GtkWidget *main_vbox;
549 ModestMsgViewWindowPrivate *priv;
551 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(obj);
553 priv->msg_view = GTK_WIDGET (tny_platform_factory_new_msg_view (modest_tny_platform_factory_get_instance ()));
554 modest_msg_view_set_shadow_type (MODEST_MSG_VIEW (priv->msg_view), GTK_SHADOW_NONE);
555 main_vbox = gtk_vbox_new (FALSE, 6);
557 priv->main_scroll = modest_toolkit_factory_create_scrollable (modest_runtime_get_toolkit_factory ());
558 modest_scrollable_set_horizontal_policy (MODEST_SCROLLABLE (priv->main_scroll), GTK_POLICY_AUTOMATIC);
559 g_object_set (G_OBJECT (priv->main_scroll),
560 "movement-mode", MODEST_MOVEMENT_MODE_BOTH,
561 "horizontal-max-overshoot", 0,
563 gtk_container_add (GTK_CONTAINER (priv->main_scroll), priv->msg_view);
564 gtk_box_pack_start (GTK_BOX(main_vbox), priv->main_scroll, TRUE, TRUE, 0);
565 gtk_container_add (GTK_CONTAINER(obj), main_vbox);
567 /* NULL-ize fields if the window is destroyed */
568 g_signal_connect (priv->msg_view, "destroy", G_CALLBACK (gtk_widget_destroyed), &(priv->msg_view));
570 gtk_widget_show_all (GTK_WIDGET(main_vbox));
574 modest_msg_view_window_disconnect_signals (ModestWindow *self)
576 ModestMsgViewWindowPrivate *priv;
577 GtkWidget *header_view = NULL;
578 GtkWindow *parent_window = NULL;
580 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
582 if (gtk_clipboard_get (GDK_SELECTION_PRIMARY) &&
583 g_signal_handler_is_connected (gtk_clipboard_get (GDK_SELECTION_PRIMARY),
584 priv->clipboard_change_handler))
585 g_signal_handler_disconnect (gtk_clipboard_get (GDK_SELECTION_PRIMARY),
586 priv->clipboard_change_handler);
588 if (g_signal_handler_is_connected (G_OBJECT (modest_runtime_get_mail_operation_queue ()),
589 priv->queue_change_handler))
590 g_signal_handler_disconnect (G_OBJECT (modest_runtime_get_mail_operation_queue ()),
591 priv->queue_change_handler);
593 if (g_signal_handler_is_connected (G_OBJECT (modest_runtime_get_account_store ()),
594 priv->account_removed_handler))
595 g_signal_handler_disconnect (G_OBJECT (modest_runtime_get_account_store ()),
596 priv->account_removed_handler);
598 if (priv->header_model) {
599 if (g_signal_handler_is_connected(G_OBJECT (priv->header_model),
600 priv->row_changed_handler))
601 g_signal_handler_disconnect(G_OBJECT (priv->header_model),
602 priv->row_changed_handler);
604 if (g_signal_handler_is_connected(G_OBJECT (priv->header_model),
605 priv->row_deleted_handler))
606 g_signal_handler_disconnect(G_OBJECT (priv->header_model),
607 priv->row_deleted_handler);
609 if (g_signal_handler_is_connected(G_OBJECT (priv->header_model),
610 priv->row_inserted_handler))
611 g_signal_handler_disconnect(G_OBJECT (priv->header_model),
612 priv->row_inserted_handler);
614 if (g_signal_handler_is_connected(G_OBJECT (priv->header_model),
615 priv->rows_reordered_handler))
616 g_signal_handler_disconnect(G_OBJECT (priv->header_model),
617 priv->rows_reordered_handler);
620 modest_signal_mgr_disconnect_all_and_destroy (priv->sighandlers);
621 priv->sighandlers = NULL;
623 parent_window = gtk_window_get_transient_for (GTK_WINDOW (self));
624 if (parent_window && MODEST_IS_HEADER_WINDOW (parent_window)) {
625 header_view = GTK_WIDGET (modest_header_window_get_header_view (MODEST_HEADER_WINDOW (parent_window)));
627 modest_header_view_remove_observer(MODEST_HEADER_VIEW (header_view),
628 MODEST_HEADER_VIEW_OBSERVER(self));
634 modest_msg_view_window_finalize (GObject *obj)
636 ModestMsgViewWindowPrivate *priv;
638 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (obj);
640 /* Sanity check: shouldn't be needed, the window mgr should
641 call this function before */
642 modest_msg_view_window_disconnect_signals (MODEST_WINDOW (obj));
644 if (priv->fetch_image_redraw_handler > 0) {
645 g_source_remove (priv->fetch_image_redraw_handler);
646 priv->fetch_image_redraw_handler = 0;
649 if (priv->other_body != NULL) {
650 g_object_unref (priv->other_body);
651 priv->other_body = NULL;
654 if (priv->header_model != NULL) {
655 g_object_unref (priv->header_model);
656 priv->header_model = NULL;
659 if (priv->remove_attachment_banner) {
660 gtk_widget_destroy (priv->remove_attachment_banner);
661 g_object_unref (priv->remove_attachment_banner);
662 priv->remove_attachment_banner = NULL;
665 if (priv->purge_timeout > 0) {
666 g_source_remove (priv->purge_timeout);
667 priv->purge_timeout = 0;
670 if (priv->row_reference) {
671 gtk_tree_row_reference_free (priv->row_reference);
672 priv->row_reference = NULL;
675 if (priv->next_row_reference) {
676 gtk_tree_row_reference_free (priv->next_row_reference);
677 priv->next_row_reference = NULL;
681 g_free (priv->msg_uid);
682 priv->msg_uid = NULL;
685 G_OBJECT_CLASS(parent_class)->finalize (obj);
689 select_next_valid_row (GtkTreeModel *model,
690 GtkTreeRowReference **row_reference,
694 GtkTreeIter tmp_iter;
696 GtkTreePath *next = NULL;
697 gboolean retval = FALSE, finished;
699 g_return_val_if_fail (gtk_tree_row_reference_valid (*row_reference), FALSE);
701 path = gtk_tree_row_reference_get_path (*row_reference);
702 gtk_tree_model_get_iter (model, &tmp_iter, path);
703 gtk_tree_row_reference_free (*row_reference);
704 *row_reference = NULL;
708 TnyHeader *header = NULL;
710 if (gtk_tree_model_iter_next (model, &tmp_iter)) {
711 gtk_tree_model_get (model, &tmp_iter,
712 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
716 if (msg_is_visible (header, is_outbox)) {
717 next = gtk_tree_model_get_path (model, &tmp_iter);
718 *row_reference = gtk_tree_row_reference_new (model, next);
719 gtk_tree_path_free (next);
723 g_object_unref (header);
726 } else if (cycle && gtk_tree_model_get_iter_first (model, &tmp_iter)) {
727 next = gtk_tree_model_get_path (model, &tmp_iter);
729 /* Ensure that we are not selecting the same */
730 if (gtk_tree_path_compare (path, next) != 0) {
731 gtk_tree_model_get (model, &tmp_iter,
732 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
735 if (msg_is_visible (header, is_outbox)) {
736 *row_reference = gtk_tree_row_reference_new (model, next);
740 g_object_unref (header);
744 /* If we ended up in the same message
745 then there is no valid next
749 gtk_tree_path_free (next);
751 /* If there are no more messages and we don't
752 want to start again in the first one then
753 there is no valid next message */
759 gtk_tree_path_free (path);
764 /* TODO: This should be in _init(), with the parameters as properties. */
766 modest_msg_view_window_construct (ModestMsgViewWindow *self,
767 const gchar *modest_account_name,
768 const gchar *mailbox,
769 const gchar *msg_uid)
772 ModestMsgViewWindowPrivate *priv = NULL;
773 ModestWindowPrivate *parent_priv = NULL;
774 ModestDimmingRulesGroup *toolbar_rules_group = NULL;
775 ModestDimmingRulesGroup *clipboard_rules_group = NULL;
777 obj = G_OBJECT (self);
778 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(obj);
779 parent_priv = MODEST_WINDOW_GET_PRIVATE(obj);
781 priv->msg_uid = g_strdup (msg_uid);
784 parent_priv->menubar = NULL;
786 toolbar_rules_group = modest_dimming_rules_group_new (MODEST_DIMMING_RULES_TOOLBAR, TRUE);
787 clipboard_rules_group = modest_dimming_rules_group_new (MODEST_DIMMING_RULES_CLIPBOARD, FALSE);
790 /* Add common dimming rules */
791 modest_dimming_rules_group_add_rules (toolbar_rules_group,
792 modest_msg_view_toolbar_dimming_entries,
793 G_N_ELEMENTS (modest_msg_view_toolbar_dimming_entries),
794 MODEST_WINDOW (self));
795 modest_dimming_rules_group_add_rules (clipboard_rules_group,
796 modest_msg_view_clipboard_dimming_entries,
797 G_N_ELEMENTS (modest_msg_view_clipboard_dimming_entries),
798 MODEST_WINDOW (self));
800 /* Insert dimming rules group for this window */
801 modest_ui_dimming_manager_insert_rules_group (parent_priv->ui_dimming_manager, toolbar_rules_group);
802 modest_ui_dimming_manager_insert_rules_group (parent_priv->ui_dimming_manager, clipboard_rules_group);
803 g_object_unref (toolbar_rules_group);
804 g_object_unref (clipboard_rules_group);
806 /* g_signal_connect (G_OBJECT(obj), "delete-event", G_CALLBACK(on_delete_event), obj); */
808 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);
809 g_signal_connect (G_OBJECT(priv->msg_view), "activate_link",
810 G_CALLBACK (modest_ui_actions_on_msg_link_clicked), obj);
811 g_signal_connect (G_OBJECT(priv->msg_view), "link_hover",
812 G_CALLBACK (modest_ui_actions_on_msg_link_hover), obj);
813 g_signal_connect (G_OBJECT(priv->msg_view), "attachment_clicked",
814 G_CALLBACK (modest_ui_actions_on_msg_attachment_clicked), obj);
815 g_signal_connect (G_OBJECT(priv->msg_view), "recpt_activated",
816 G_CALLBACK (modest_ui_actions_on_msg_recpt_activated), obj);
817 g_signal_connect (G_OBJECT(priv->msg_view), "show_details",
818 G_CALLBACK (modest_ui_actions_on_details), obj);
819 g_signal_connect (G_OBJECT(priv->msg_view), "link_contextual",
820 G_CALLBACK (modest_ui_actions_on_msg_link_contextual), obj);
821 g_signal_connect (G_OBJECT(priv->msg_view), "limit_error",
822 G_CALLBACK (modest_ui_actions_on_limit_error), obj);
823 g_signal_connect (G_OBJECT (priv->msg_view), "fetch_image",
824 G_CALLBACK (on_fetch_image), obj);
826 g_signal_connect (G_OBJECT (obj), "key-release-event",
827 G_CALLBACK (modest_msg_view_window_key_event),
830 g_signal_connect (G_OBJECT (obj), "key-press-event",
831 G_CALLBACK (modest_msg_view_window_key_event),
834 g_signal_connect (G_OBJECT (obj), "move-focus",
835 G_CALLBACK (on_move_focus), obj);
837 g_signal_connect (G_OBJECT (obj), "map-event",
838 G_CALLBACK (_modest_msg_view_window_map_event),
841 /* Mail Operation Queue */
842 priv->queue_change_handler = g_signal_connect (G_OBJECT (modest_runtime_get_mail_operation_queue ()),
844 G_CALLBACK (on_queue_changed),
847 /* Account manager */
848 priv->account_removed_handler = g_signal_connect (G_OBJECT (modest_runtime_get_account_store ()),
850 G_CALLBACK(on_account_removed),
853 modest_window_set_active_account (MODEST_WINDOW(obj), modest_account_name);
854 modest_window_set_active_mailbox (MODEST_WINDOW(obj), mailbox);
856 /* First add out toolbar ... */
857 modest_msg_view_window_show_toolbar (MODEST_WINDOW (obj), TRUE);
859 priv->isearch_toolbar = modest_toolkit_factory_create_isearch_toolbar (modest_runtime_get_toolkit_factory (),
861 modest_window_add_toolbar (MODEST_WINDOW (obj), GTK_TOOLBAR (priv->isearch_toolbar));
862 gtk_widget_set_no_show_all (priv->isearch_toolbar, TRUE);
863 g_signal_connect (G_OBJECT (priv->isearch_toolbar), "isearch-close",
864 G_CALLBACK (modest_msg_view_window_isearch_toolbar_close), obj);
865 g_signal_connect (G_OBJECT (priv->isearch_toolbar), "isearch-search",
866 G_CALLBACK (modest_msg_view_window_isearch_toolbar_search), obj);
867 priv->last_search = NULL;
869 /* Init the clipboard actions dim status */
870 modest_msg_view_grab_focus(MODEST_MSG_VIEW (priv->msg_view));
872 update_window_title (MODEST_MSG_VIEW_WINDOW (obj));
877 /* FIXME: parameter checks */
879 modest_msg_view_window_new_with_header_model (TnyMsg *msg,
880 const gchar *modest_account_name,
881 const gchar *mailbox,
882 const gchar *msg_uid,
884 GtkTreeRowReference *row_reference)
886 ModestMsgViewWindow *window = NULL;
887 ModestMsgViewWindowPrivate *priv = NULL;
888 TnyFolder *header_folder = NULL;
889 ModestHeaderView *header_view = NULL;
890 ModestWindowMgr *mgr = NULL;
893 modest_tny_mime_part_to_string (TNY_MIME_PART (msg), 0);
896 mgr = modest_runtime_get_window_mgr ();
897 window = MODEST_MSG_VIEW_WINDOW (modest_window_mgr_get_msg_view_window (mgr));
898 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), NULL);
900 modest_msg_view_window_construct (window, modest_account_name, mailbox, msg_uid);
902 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
904 /* Remember the message list's TreeModel so we can detect changes
905 * and change the list selection when necessary: */
906 header_folder = modest_header_view_get_folder (header_view);
908 priv->is_outbox = (modest_tny_folder_guess_folder_type (header_folder) ==
909 TNY_FOLDER_TYPE_OUTBOX);
910 priv->header_folder_id = tny_folder_get_id (header_folder);
911 g_object_unref(header_folder);
914 /* Setup row references and connect signals */
915 priv->header_model = g_object_ref (model);
917 if (row_reference && gtk_tree_row_reference_valid (row_reference)) {
918 priv->row_reference = gtk_tree_row_reference_copy (row_reference);
919 priv->next_row_reference = gtk_tree_row_reference_copy (row_reference);
920 select_next_valid_row (model, &(priv->next_row_reference), TRUE, priv->is_outbox);
922 priv->row_reference = NULL;
923 priv->next_row_reference = NULL;
926 /* Connect signals */
927 priv->row_changed_handler =
928 g_signal_connect (GTK_TREE_MODEL(model), "row-changed",
929 G_CALLBACK(modest_msg_view_window_on_row_changed),
931 priv->row_deleted_handler =
932 g_signal_connect (GTK_TREE_MODEL(model), "row-deleted",
933 G_CALLBACK(modest_msg_view_window_on_row_deleted),
935 priv->row_inserted_handler =
936 g_signal_connect (GTK_TREE_MODEL(model), "row-inserted",
937 G_CALLBACK(modest_msg_view_window_on_row_inserted),
939 priv->rows_reordered_handler =
940 g_signal_connect(GTK_TREE_MODEL(model), "rows-reordered",
941 G_CALLBACK(modest_msg_view_window_on_row_reordered),
944 if (header_view != NULL){
945 modest_header_view_add_observer(header_view,
946 MODEST_HEADER_VIEW_OBSERVER(window));
949 tny_msg_view_set_msg (TNY_MSG_VIEW (priv->msg_view), msg);
950 update_window_title (MODEST_MSG_VIEW_WINDOW (window));
951 update_branding (MODEST_MSG_VIEW_WINDOW (window));
953 /* gtk_widget_show_all (GTK_WIDGET (window)); */
954 modest_msg_view_window_update_priority (window);
955 /* Check dimming rules */
956 modest_ui_actions_check_toolbar_dimming_rules (MODEST_WINDOW (window));
957 modest_ui_actions_check_menu_dimming_rules (MODEST_WINDOW (window));
958 modest_window_check_dimming_rules_group (MODEST_WINDOW (window), MODEST_DIMMING_RULES_CLIPBOARD);
960 return MODEST_WINDOW(window);
964 modest_msg_view_window_new_from_uid (const gchar *modest_account_name,
965 const gchar *mailbox,
966 const gchar *msg_uid)
968 ModestMsgViewWindow *window = NULL;
969 ModestMsgViewWindowPrivate *priv = NULL;
970 ModestWindowMgr *mgr = NULL;
972 TnyAccount *account = NULL;
974 mgr = modest_runtime_get_window_mgr ();
975 window = MODEST_MSG_VIEW_WINDOW (modest_window_mgr_get_msg_view_window (mgr));
976 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), NULL);
978 modest_msg_view_window_construct (window, modest_account_name, mailbox, msg_uid);
980 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
982 is_merge = g_str_has_prefix (msg_uid, "merge:");
984 /* Get the account */
986 account = tny_account_store_find_account (TNY_ACCOUNT_STORE (modest_runtime_get_account_store ()),
989 if (is_merge || account) {
990 TnyFolder *folder = NULL;
992 /* Try to get the message, if it's already downloaded
993 we don't need to connect */
995 folder = modest_tny_folder_store_find_folder_from_uri (TNY_FOLDER_STORE (account), msg_uid);
997 ModestTnyAccountStore *account_store;
998 ModestTnyLocalFoldersAccount *local_folders_account;
1000 account_store = modest_runtime_get_account_store ();
1001 local_folders_account = MODEST_TNY_LOCAL_FOLDERS_ACCOUNT (
1002 modest_tny_account_store_get_local_folders_account (account_store));
1003 folder = modest_tny_local_folders_account_get_merged_outbox (local_folders_account);
1004 g_object_unref (local_folders_account);
1008 gboolean device_online;
1010 device = modest_runtime_get_device();
1011 device_online = tny_device_is_online (device);
1012 if (device_online) {
1013 message_reader (window, priv, NULL, msg_uid, folder, NULL);
1015 TnyMsg *msg = tny_folder_find_msg (folder, msg_uid, NULL);
1017 tny_msg_view_set_msg (TNY_MSG_VIEW (priv->msg_view), msg);
1018 update_window_title (MODEST_MSG_VIEW_WINDOW (window));
1019 update_branding (MODEST_MSG_VIEW_WINDOW (window));
1020 g_object_unref (msg);
1021 /* Sync flags to server */
1022 sync_flags (MODEST_MSG_VIEW_WINDOW (window));
1024 message_reader (window, priv, NULL, msg_uid, folder, NULL);
1027 g_object_unref (folder);
1032 /* Check dimming rules */
1033 modest_ui_actions_check_toolbar_dimming_rules (MODEST_WINDOW (window));
1034 modest_ui_actions_check_menu_dimming_rules (MODEST_WINDOW (window));
1035 modest_window_check_dimming_rules_group (MODEST_WINDOW (window), MODEST_DIMMING_RULES_CLIPBOARD);
1037 return MODEST_WINDOW(window);
1041 modest_msg_view_window_new_from_header_view (ModestHeaderView *header_view,
1042 const gchar *modest_account_name,
1043 const gchar *mailbox,
1044 const gchar *msg_uid,
1045 GtkTreeRowReference *row_reference)
1047 ModestMsgViewWindow *window = NULL;
1048 ModestMsgViewWindowPrivate *priv = NULL;
1049 TnyFolder *header_folder = NULL;
1050 ModestWindowMgr *mgr = NULL;
1054 mgr = modest_runtime_get_window_mgr ();
1055 window = MODEST_MSG_VIEW_WINDOW (modest_window_mgr_get_msg_view_window (mgr));
1056 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), NULL);
1058 modest_msg_view_window_construct (window, modest_account_name, mailbox, msg_uid);
1060 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1062 /* Remember the message list's TreeModel so we can detect changes
1063 * and change the list selection when necessary: */
1065 if (header_view != NULL){
1066 header_folder = modest_header_view_get_folder(header_view);
1067 /* This could happen if the header folder was
1068 unseleted before opening this msg window (for
1069 example if the user selects an account in the
1070 folder view of the main window */
1071 if (header_folder) {
1072 priv->is_outbox = (modest_tny_folder_guess_folder_type (header_folder) ==
1073 TNY_FOLDER_TYPE_OUTBOX);
1074 priv->header_folder_id = tny_folder_get_id(header_folder);
1075 g_object_unref(header_folder);
1079 /* Setup row references and connect signals */
1080 priv->header_model = gtk_tree_view_get_model (GTK_TREE_VIEW (header_view));
1081 g_object_ref (priv->header_model);
1083 if (row_reference && gtk_tree_row_reference_valid (row_reference)) {
1084 priv->row_reference = gtk_tree_row_reference_copy (row_reference);
1085 priv->next_row_reference = gtk_tree_row_reference_copy (row_reference);
1086 select_next_valid_row (priv->header_model, &(priv->next_row_reference), TRUE, priv->is_outbox);
1088 priv->row_reference = NULL;
1089 priv->next_row_reference = NULL;
1092 /* Connect signals */
1093 priv->row_changed_handler =
1094 g_signal_connect (GTK_TREE_MODEL(priv->header_model), "row-changed",
1095 G_CALLBACK(modest_msg_view_window_on_row_changed),
1097 priv->row_deleted_handler =
1098 g_signal_connect (GTK_TREE_MODEL(priv->header_model), "row-deleted",
1099 G_CALLBACK(modest_msg_view_window_on_row_deleted),
1101 priv->row_inserted_handler =
1102 g_signal_connect (GTK_TREE_MODEL(priv->header_model), "row-inserted",
1103 G_CALLBACK(modest_msg_view_window_on_row_inserted),
1105 priv->rows_reordered_handler =
1106 g_signal_connect(GTK_TREE_MODEL(priv->header_model), "rows-reordered",
1107 G_CALLBACK(modest_msg_view_window_on_row_reordered),
1110 if (header_view != NULL){
1111 modest_header_view_add_observer(header_view,
1112 MODEST_HEADER_VIEW_OBSERVER(window));
1115 tny_msg_view_set_msg (TNY_MSG_VIEW (priv->msg_view), NULL);
1116 update_branding (MODEST_MSG_VIEW_WINDOW (window));
1118 if (priv->row_reference) {
1119 path = gtk_tree_row_reference_get_path (priv->row_reference);
1120 if (gtk_tree_model_get_iter (priv->header_model, &iter, path)) {
1122 gtk_tree_model_get (priv->header_model, &iter,
1123 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
1125 message_reader (window, priv, header, NULL, NULL, priv->row_reference);
1126 g_object_unref (header);
1128 gtk_tree_path_free (path);
1130 /* Check dimming rules */
1131 modest_ui_actions_check_toolbar_dimming_rules (MODEST_WINDOW (window));
1132 modest_ui_actions_check_menu_dimming_rules (MODEST_WINDOW (window));
1133 modest_window_check_dimming_rules_group (MODEST_WINDOW (window), MODEST_DIMMING_RULES_CLIPBOARD);
1135 return MODEST_WINDOW(window);
1139 modest_msg_view_window_new_for_search_result (TnyMsg *msg,
1140 const gchar *modest_account_name,
1141 const gchar *mailbox,
1142 const gchar *msg_uid)
1144 ModestMsgViewWindow *window = NULL;
1145 ModestMsgViewWindowPrivate *priv = NULL;
1146 ModestWindowMgr *mgr = NULL;
1148 mgr = modest_runtime_get_window_mgr ();
1149 window = MODEST_MSG_VIEW_WINDOW (modest_window_mgr_get_msg_view_window (mgr));
1150 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), NULL);
1151 modest_msg_view_window_construct (window, modest_account_name, mailbox, msg_uid);
1153 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1155 /* Remember that this is a search result,
1156 * so we can disable some UI appropriately: */
1157 priv->is_search_result = TRUE;
1159 tny_msg_view_set_msg (TNY_MSG_VIEW (priv->msg_view), msg);
1160 update_branding (MODEST_MSG_VIEW_WINDOW (window));
1162 update_window_title (window);
1163 /* gtk_widget_show_all (GTK_WIDGET (window));*/
1164 modest_msg_view_window_update_priority (window);
1166 /* Check dimming rules */
1167 modest_ui_actions_check_toolbar_dimming_rules (MODEST_WINDOW (window));
1168 modest_ui_actions_check_menu_dimming_rules (MODEST_WINDOW (window));
1169 modest_window_check_dimming_rules_group (MODEST_WINDOW (window), MODEST_DIMMING_RULES_CLIPBOARD);
1171 return MODEST_WINDOW(window);
1175 modest_msg_view_window_is_other_body (ModestMsgViewWindow *self)
1177 ModestMsgViewWindowPrivate *priv = NULL;
1179 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self), FALSE);
1180 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
1182 return (priv->other_body != NULL);
1186 modest_msg_view_window_new_with_other_body (TnyMsg *msg,
1187 TnyMimePart *other_body,
1188 const gchar *modest_account_name,
1189 const gchar *mailbox,
1190 const gchar *msg_uid)
1192 GObject *obj = NULL;
1193 ModestMsgViewWindowPrivate *priv;
1194 ModestWindowMgr *mgr = NULL;
1196 g_return_val_if_fail (msg, NULL);
1197 mgr = modest_runtime_get_window_mgr ();
1198 obj = G_OBJECT (modest_window_mgr_get_msg_view_window (mgr));
1199 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (obj);
1200 modest_msg_view_window_construct (MODEST_MSG_VIEW_WINDOW (obj),
1201 modest_account_name, mailbox, msg_uid);
1204 priv->other_body = g_object_ref (other_body);
1205 modest_msg_view_set_msg_with_other_body (MODEST_MSG_VIEW (priv->msg_view), msg, other_body);
1207 tny_msg_view_set_msg (TNY_MSG_VIEW (priv->msg_view), msg);
1209 update_window_title (MODEST_MSG_VIEW_WINDOW (obj));
1210 update_branding (MODEST_MSG_VIEW_WINDOW (obj));
1212 /* gtk_widget_show_all (GTK_WIDGET (obj)); */
1214 /* Check dimming rules */
1215 modest_ui_actions_check_toolbar_dimming_rules (MODEST_WINDOW (obj));
1216 modest_ui_actions_check_menu_dimming_rules (MODEST_WINDOW (obj));
1217 modest_window_check_dimming_rules_group (MODEST_WINDOW (obj), MODEST_DIMMING_RULES_CLIPBOARD);
1219 return MODEST_WINDOW(obj);
1223 modest_msg_view_window_new_for_attachment (TnyMsg *msg,
1224 const gchar *modest_account_name,
1225 const gchar *mailbox,
1226 const gchar *msg_uid)
1228 return modest_msg_view_window_new_with_other_body (msg, NULL, modest_account_name, mailbox, msg_uid);
1232 modest_msg_view_window_on_row_changed (GtkTreeModel *header_model,
1235 ModestMsgViewWindow *window)
1237 check_dimming_rules_after_change (window);
1241 modest_msg_view_window_on_row_deleted(GtkTreeModel *header_model,
1243 ModestMsgViewWindow *window)
1245 check_dimming_rules_after_change (window);
1247 /* The window could have dissapeared */
1250 check_dimming_rules_after_change (ModestMsgViewWindow *window)
1252 modest_ui_actions_check_menu_dimming_rules (MODEST_WINDOW (window));
1253 modest_ui_actions_check_toolbar_dimming_rules (MODEST_WINDOW (window));
1257 /* On insertions we check if the folder still has the message we are
1258 * showing or do not. If do not, we do nothing. Which means we are still
1259 * not attached to any header folder and thus next/prev buttons are
1260 * still dimmed. Once the message that is shown by msg-view is found, the
1261 * new model of header-view will be attached and the references will be set.
1262 * On each further insertions dimming rules will be checked. However
1263 * this requires extra CPU time at least works.
1264 * (An message might be deleted from TnyFolder and thus will not be
1265 * inserted into the model again for example if it is removed by the
1266 * imap server and the header view is refreshed.)
1269 modest_msg_view_window_on_row_inserted (GtkTreeModel *model,
1270 GtkTreePath *tree_path,
1271 GtkTreeIter *tree_iter,
1272 ModestMsgViewWindow *window)
1274 ModestMsgViewWindowPrivate *priv = NULL;
1275 TnyHeader *header = NULL;
1277 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window));
1278 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1280 g_assert (model == priv->header_model);
1282 /* Check if the newly inserted message is the same we are actually
1283 * showing. IF not, we should remain detached from the header model
1284 * and thus prev and next toolbar buttons should remain dimmed. */
1285 gtk_tree_model_get (model, tree_iter,
1286 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
1289 if (TNY_IS_HEADER (header)) {
1292 uid = modest_tny_folder_get_header_unique_id (header);
1293 if (!g_str_equal(priv->msg_uid, uid)) {
1294 check_dimming_rules_after_change (window);
1296 g_object_unref (G_OBJECT(header));
1300 g_object_unref(G_OBJECT(header));
1303 if (priv->row_reference) {
1304 gtk_tree_row_reference_free (priv->row_reference);
1307 /* Setup row_reference for the actual msg. */
1308 priv->row_reference = gtk_tree_row_reference_new (priv->header_model, tree_path);
1309 if (priv->row_reference == NULL) {
1310 g_warning("%s: No reference for msg header item.", __FUNCTION__);
1314 /* Now set up next_row_reference. */
1315 if (priv->next_row_reference) {
1316 gtk_tree_row_reference_free (priv->next_row_reference);
1319 priv->next_row_reference = gtk_tree_row_reference_copy (priv->row_reference);
1320 select_next_valid_row (priv->header_model,
1321 &(priv->next_row_reference), FALSE, priv->is_outbox);
1323 /* Connect the remaining callbacks to become able to detect
1324 * changes in header-view. */
1325 priv->row_changed_handler =
1326 g_signal_connect (priv->header_model, "row-changed",
1327 G_CALLBACK (modest_msg_view_window_on_row_changed),
1329 priv->row_deleted_handler =
1330 g_signal_connect (priv->header_model, "row-deleted",
1331 G_CALLBACK (modest_msg_view_window_on_row_deleted),
1333 priv->rows_reordered_handler =
1334 g_signal_connect (priv->header_model, "rows-reordered",
1335 G_CALLBACK (modest_msg_view_window_on_row_reordered),
1338 check_dimming_rules_after_change (window);
1342 modest_msg_view_window_on_row_reordered (GtkTreeModel *header_model,
1346 ModestMsgViewWindow *window)
1348 ModestMsgViewWindowPrivate *priv = NULL;
1349 gboolean already_changed = FALSE;
1351 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(window);
1353 /* If the current row was reordered select the proper next
1354 valid row. The same if the next row reference changes */
1355 if (!priv->row_reference ||
1356 !gtk_tree_row_reference_valid (priv->row_reference))
1359 if (priv->next_row_reference &&
1360 gtk_tree_row_reference_valid (priv->next_row_reference)) {
1361 GtkTreePath *cur, *next;
1362 /* Check that the order is still the correct one */
1363 cur = gtk_tree_row_reference_get_path (priv->row_reference);
1364 next = gtk_tree_row_reference_get_path (priv->next_row_reference);
1365 gtk_tree_path_next (cur);
1366 if (gtk_tree_path_compare (cur, next) != 0) {
1367 gtk_tree_row_reference_free (priv->next_row_reference);
1368 priv->next_row_reference = gtk_tree_row_reference_copy (priv->row_reference);
1369 select_next_valid_row (header_model, &(priv->next_row_reference), FALSE, priv->is_outbox);
1370 already_changed = TRUE;
1372 gtk_tree_path_free (cur);
1373 gtk_tree_path_free (next);
1375 if (priv->next_row_reference)
1376 gtk_tree_row_reference_free (priv->next_row_reference);
1377 /* Update next row reference */
1378 priv->next_row_reference = gtk_tree_row_reference_copy (priv->row_reference);
1379 select_next_valid_row (header_model, &(priv->next_row_reference), FALSE, priv->is_outbox);
1380 already_changed = TRUE;
1383 check_dimming_rules_after_change (window);
1386 /* The modest_msg_view_window_update_model_replaced implements update
1387 * function for ModestHeaderViewObserver. Checks whether the TnyFolder
1388 * actually belongs to the header-view is the same as the TnyFolder of
1389 * the message of msg-view or not. If they are different, there is
1390 * nothing to do. If they are the same, then the model has replaced and
1391 * the reference in msg-view shall be replaced from the old model to
1392 * the new model. In this case the view will be detached from it's
1393 * header folder. From this point the next/prev buttons are dimmed.
1396 modest_msg_view_window_update_model_replaced (ModestHeaderViewObserver *observer,
1397 GtkTreeModel *model,
1398 const gchar *tny_folder_id)
1400 ModestMsgViewWindowPrivate *priv = NULL;
1401 ModestMsgViewWindow *window = NULL;
1403 g_assert(MODEST_IS_HEADER_VIEW_OBSERVER(observer));
1404 g_assert(MODEST_IS_MSG_VIEW_WINDOW(observer));
1406 window = MODEST_MSG_VIEW_WINDOW(observer);
1407 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(window);
1409 /* If there is an other folder in the header-view then we do
1410 * not care about it's model (msg list). Else if the
1411 * header-view shows the folder the msg shown by us is in, we
1412 * shall replace our model reference and make some check. */
1413 if(model == NULL || tny_folder_id == NULL ||
1414 (priv->header_folder_id && !g_str_equal(tny_folder_id, priv->header_folder_id)))
1417 /* Model is changed(replaced), so we should forget the old
1418 * one. Because there might be other references and there
1419 * might be some change on the model even if we unreferenced
1420 * it, we need to disconnect our signals here. */
1421 if (priv->header_model) {
1422 if (g_signal_handler_is_connected(G_OBJECT (priv->header_model),
1423 priv->row_changed_handler))
1424 g_signal_handler_disconnect(G_OBJECT (priv->header_model),
1425 priv->row_changed_handler);
1426 if (g_signal_handler_is_connected(G_OBJECT (priv->header_model),
1427 priv->row_deleted_handler))
1428 g_signal_handler_disconnect(G_OBJECT (priv->header_model),
1429 priv->row_deleted_handler);
1430 if (g_signal_handler_is_connected(G_OBJECT (priv->header_model),
1431 priv->row_inserted_handler))
1432 g_signal_handler_disconnect(G_OBJECT (priv->header_model),
1433 priv->row_inserted_handler);
1434 if (g_signal_handler_is_connected(G_OBJECT (priv->header_model),
1435 priv->rows_reordered_handler))
1436 g_signal_handler_disconnect(G_OBJECT (priv->header_model),
1437 priv->rows_reordered_handler);
1440 if (priv->row_reference)
1441 gtk_tree_row_reference_free (priv->row_reference);
1442 if (priv->next_row_reference)
1443 gtk_tree_row_reference_free (priv->next_row_reference);
1444 g_object_unref(priv->header_model);
1447 priv->row_changed_handler = 0;
1448 priv->row_deleted_handler = 0;
1449 priv->row_inserted_handler = 0;
1450 priv->rows_reordered_handler = 0;
1451 priv->next_row_reference = NULL;
1452 priv->row_reference = NULL;
1453 priv->header_model = NULL;
1456 priv->header_model = g_object_ref (model);
1458 /* Also we must connect to the new model for row insertions.
1459 * Only for insertions now. We will need other ones only after
1460 * the msg is show by msg-view is added to the new model. */
1461 priv->row_inserted_handler =
1462 g_signal_connect (priv->header_model, "row-inserted",
1463 G_CALLBACK(modest_msg_view_window_on_row_inserted),
1466 modest_ui_actions_check_menu_dimming_rules(MODEST_WINDOW(window));
1467 modest_ui_actions_check_toolbar_dimming_rules(MODEST_WINDOW(window));
1471 modest_msg_view_window_toolbar_on_transfer_mode (ModestMsgViewWindow *self)
1473 ModestMsgViewWindowPrivate *priv= NULL;
1475 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self), FALSE);
1476 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
1478 return priv->progress_hint;
1482 modest_msg_view_window_get_header (ModestMsgViewWindow *self)
1484 ModestMsgViewWindowPrivate *priv= NULL;
1486 TnyHeader *header = NULL;
1487 GtkTreePath *path = NULL;
1490 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self), NULL);
1491 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
1493 /* If the message was not obtained from a treemodel,
1494 * for instance if it was opened directly by the search UI:
1496 if (priv->header_model == NULL ||
1497 priv->row_reference == NULL ||
1498 !gtk_tree_row_reference_valid (priv->row_reference)) {
1499 msg = modest_msg_view_window_get_message (self);
1501 header = tny_msg_get_header (msg);
1502 g_object_unref (msg);
1507 /* Get iter of the currently selected message in the header view: */
1508 path = gtk_tree_row_reference_get_path (priv->row_reference);
1509 g_return_val_if_fail (path != NULL, NULL);
1510 gtk_tree_model_get_iter (priv->header_model,
1514 /* Get current message header */
1515 gtk_tree_model_get (priv->header_model, &iter,
1516 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
1519 gtk_tree_path_free (path);
1524 modest_msg_view_window_get_message (ModestMsgViewWindow *self)
1526 ModestMsgViewWindowPrivate *priv;
1528 g_return_val_if_fail (self, NULL);
1530 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(self);
1532 return tny_msg_view_get_msg (TNY_MSG_VIEW (priv->msg_view));
1536 modest_msg_view_window_get_message_uid (ModestMsgViewWindow *self)
1538 ModestMsgViewWindowPrivate *priv;
1540 g_return_val_if_fail (self, NULL);
1542 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
1544 return (const gchar*) priv->msg_uid;
1547 /* Used for the Ctrl+F accelerator */
1549 modest_msg_view_window_toggle_isearch_toolbar (GtkWidget *obj,
1552 ModestMsgViewWindow *window = MODEST_MSG_VIEW_WINDOW (data);
1553 ModestMsgViewWindowPrivate *priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1555 if (GTK_WIDGET_VISIBLE (priv->isearch_toolbar)) {
1556 modest_msg_view_window_isearch_toolbar_close (obj, data);
1558 modest_msg_view_window_show_isearch_toolbar (obj, data);
1562 /* Handler for menu option */
1564 modest_msg_view_window_show_isearch_toolbar (GtkWidget *obj,
1567 ModestMsgViewWindow *window = MODEST_MSG_VIEW_WINDOW (data);
1568 ModestMsgViewWindowPrivate *priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1570 gtk_widget_show (priv->isearch_toolbar);
1571 modest_isearch_toolbar_highlight_entry (MODEST_ISEARCH_TOOLBAR (priv->isearch_toolbar), TRUE);
1574 /* Handler for click on the "X" close button in isearch toolbar */
1576 modest_msg_view_window_isearch_toolbar_close (GtkWidget *widget,
1577 ModestMsgViewWindow *obj)
1579 ModestMsgViewWindowPrivate *priv;
1581 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (obj);
1584 gtk_widget_hide (priv->isearch_toolbar);
1585 modest_msg_view_grab_focus (MODEST_MSG_VIEW (priv->msg_view));
1589 modest_msg_view_window_isearch_toolbar_search (GtkWidget *widget,
1590 ModestMsgViewWindow *obj)
1592 const gchar *current_search;
1593 ModestMsgViewWindowPrivate *priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (obj);
1595 if (modest_mime_part_view_is_empty (MODEST_MIME_PART_VIEW (priv->msg_view))) {
1596 modest_platform_system_banner (NULL, NULL, _("mail_ib_nothing_to_find"));
1600 current_search = modest_isearch_toolbar_get_search (MODEST_ISEARCH_TOOLBAR (widget));
1602 if ((current_search == NULL) || (strcmp (current_search, "") == 0)) {
1603 modest_platform_system_banner (NULL, NULL, _CS_FIND_REP_ENTER_TEXT);
1607 if ((priv->last_search == NULL) || (strcmp (priv->last_search, current_search) != 0)) {
1609 g_free (priv->last_search);
1610 priv->last_search = g_strdup (current_search);
1611 result = modest_isearch_view_search (MODEST_ISEARCH_VIEW (priv->msg_view),
1614 modest_platform_system_banner (NULL, NULL,
1615 _HL_IB_FIND_NO_MATCHES);
1616 g_free (priv->last_search);
1617 priv->last_search = NULL;
1619 modest_isearch_toolbar_highlight_entry (MODEST_ISEARCH_TOOLBAR (priv->isearch_toolbar), TRUE);
1622 if (!modest_isearch_view_search_next (MODEST_ISEARCH_VIEW (priv->msg_view))) {
1623 modest_platform_system_banner (NULL, NULL,
1624 _HL_IB_FIND_COMPLETE);
1625 g_free (priv->last_search);
1626 priv->last_search = NULL;
1628 modest_isearch_toolbar_highlight_entry (MODEST_ISEARCH_TOOLBAR (priv->isearch_toolbar), TRUE);
1635 modest_msg_view_window_set_zoom (ModestWindow *window,
1638 ModestMsgViewWindowPrivate *priv;
1639 ModestWindowPrivate *parent_priv;
1641 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window));
1643 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1644 parent_priv = MODEST_WINDOW_GET_PRIVATE (window);
1645 modest_zoomable_set_zoom (MODEST_ZOOMABLE (priv->msg_view), zoom);
1650 modest_msg_view_window_get_zoom (ModestWindow *window)
1652 ModestMsgViewWindowPrivate *priv;
1654 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), 1.0);
1656 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1657 return modest_zoomable_get_zoom (MODEST_ZOOMABLE (priv->msg_view));
1661 modest_msg_view_window_zoom_plus (ModestWindow *window)
1664 ModestMsgViewWindowPrivate *priv;
1668 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), 1.0);
1669 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1671 zoom_level = modest_zoomable_get_zoom (MODEST_ZOOMABLE (priv->msg_view));
1673 if (zoom_level >= 2.0) {
1674 modest_platform_system_banner (NULL, NULL,
1675 _CS_MAX_ZOOM_LEVEL_REACHED);
1677 } else if (zoom_level >= 1.5) {
1679 } else if (zoom_level >= 1.2) {
1681 } else if (zoom_level >= 1.0) {
1683 } else if (zoom_level >= 0.8) {
1685 } else if (zoom_level >= 0.5) {
1691 /* set zoom level */
1692 int_zoom = (gint) rint (zoom_level*100.0+0.1);
1693 banner_text = g_strdup_printf (_HL_IB_ZOOM, int_zoom);
1694 modest_platform_information_banner (GTK_WIDGET (window), NULL, banner_text);
1695 g_free (banner_text);
1696 modest_zoomable_set_zoom (MODEST_ZOOMABLE (priv->msg_view), zoom_level);
1702 modest_msg_view_window_zoom_minus (ModestWindow *window)
1705 ModestMsgViewWindowPrivate *priv;
1709 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), 1.0);
1710 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1712 zoom_level = modest_zoomable_get_zoom (MODEST_ZOOMABLE (priv->msg_view));
1714 if (zoom_level <= 0.5) {
1715 modest_platform_system_banner (NULL, NULL,
1716 _CS_MIN_ZOOM_LEVEL_REACHED);
1718 } else if (zoom_level <= 0.8) {
1720 } else if (zoom_level <= 1.0) {
1722 } else if (zoom_level <= 1.2) {
1724 } else if (zoom_level <= 1.5) {
1726 } else if (zoom_level <= 2.0) {
1732 /* set zoom level */
1733 int_zoom = (gint) rint (zoom_level*100.0+0.1);
1734 banner_text = g_strdup_printf (_HL_IB_ZOOM, int_zoom);
1735 modest_platform_information_banner (GTK_WIDGET (window), NULL, banner_text);
1736 g_free (banner_text);
1737 modest_zoomable_set_zoom (MODEST_ZOOMABLE (priv->msg_view), zoom_level);
1743 modest_msg_view_window_key_event (GtkWidget *window,
1749 focus = gtk_container_get_focus_child ((GtkContainer *) window);
1751 /* for the isearch toolbar case */
1752 if (focus && GTK_IS_ENTRY (focus)) {
1753 if (event->keyval == GDK_BackSpace) {
1755 copy = gdk_event_copy ((GdkEvent *) event);
1756 gtk_widget_event (focus, copy);
1757 gdk_event_free (copy);
1767 modest_msg_view_window_last_message_selected (ModestMsgViewWindow *window)
1770 ModestMsgViewWindowPrivate *priv;
1771 GtkTreeIter tmp_iter;
1772 gboolean is_last_selected;
1774 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), TRUE);
1775 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1777 /*if no model (so no rows at all), then virtually we are the last*/
1778 if (!priv->header_model || !priv->row_reference)
1781 if (!gtk_tree_row_reference_valid (priv->row_reference))
1784 path = gtk_tree_row_reference_get_path (priv->row_reference);
1788 is_last_selected = TRUE;
1789 while (is_last_selected) {
1791 gtk_tree_path_next (path);
1792 if (!gtk_tree_model_get_iter (priv->header_model, &tmp_iter, path))
1794 gtk_tree_model_get (priv->header_model, &tmp_iter,
1795 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
1798 if (msg_is_visible (header, priv->is_outbox))
1799 is_last_selected = FALSE;
1800 g_object_unref(G_OBJECT(header));
1803 gtk_tree_path_free (path);
1804 return is_last_selected;
1808 modest_msg_view_window_has_headers_model (ModestMsgViewWindow *window)
1810 ModestMsgViewWindowPrivate *priv;
1812 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), TRUE);
1813 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1815 return priv->header_model != NULL;
1819 modest_msg_view_window_is_search_result (ModestMsgViewWindow *window)
1821 ModestMsgViewWindowPrivate *priv;
1823 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), TRUE);
1824 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1826 return priv->is_search_result;
1830 msg_is_visible (TnyHeader *header, gboolean check_outbox)
1832 if ((tny_header_get_flags(header) & TNY_HEADER_FLAG_DELETED))
1834 if (!check_outbox) {
1837 ModestTnySendQueueStatus status;
1838 status = modest_tny_all_send_queues_get_msg_status (header);
1839 return ((status != MODEST_TNY_SEND_QUEUE_FAILED) &&
1840 (status != MODEST_TNY_SEND_QUEUE_SENDING));
1845 modest_msg_view_window_first_message_selected (ModestMsgViewWindow *window)
1848 ModestMsgViewWindowPrivate *priv;
1849 gboolean is_first_selected;
1850 GtkTreeIter tmp_iter;
1852 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), TRUE);
1853 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1855 /*if no model (so no rows at all), then virtually we are the first*/
1856 if (!priv->header_model || !priv->row_reference)
1859 if (!gtk_tree_row_reference_valid (priv->row_reference))
1862 path = gtk_tree_row_reference_get_path (priv->row_reference);
1866 is_first_selected = TRUE;
1867 while (is_first_selected) {
1869 if(!gtk_tree_path_prev (path))
1871 /* Here the 'if' is needless for logic, but let make sure
1872 * iter is valid for gtk_tree_model_get. */
1873 if (!gtk_tree_model_get_iter (priv->header_model, &tmp_iter, path))
1875 gtk_tree_model_get (priv->header_model, &tmp_iter,
1876 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
1879 if (msg_is_visible (header, priv->is_outbox))
1880 is_first_selected = FALSE;
1881 g_object_unref(G_OBJECT(header));
1884 gtk_tree_path_free (path);
1885 return is_first_selected;
1892 GtkTreeRowReference *row_reference;
1896 message_reader_performer (gboolean canceled,
1898 ModestWindow *parent_window,
1899 TnyAccount *account,
1902 ModestMailOperation *mail_op = NULL;
1903 MsgReaderInfo *info;
1905 info = (MsgReaderInfo *) user_data;
1906 if (canceled || err) {
1907 update_window_title (MODEST_MSG_VIEW_WINDOW (parent_window));
1908 modest_ui_actions_on_close_window (NULL, MODEST_WINDOW (parent_window));
1912 /* Register the header - it'll be unregistered in the callback */
1914 modest_window_mgr_register_header (modest_runtime_get_window_mgr (), info->header, NULL);
1916 /* New mail operation */
1917 mail_op = modest_mail_operation_new_with_error_handling (G_OBJECT(parent_window),
1918 modest_ui_actions_disk_operations_error_handler,
1921 modest_mail_operation_queue_add (modest_runtime_get_mail_operation_queue (), mail_op);
1923 modest_mail_operation_get_msg (mail_op, info->header, TRUE, view_msg_cb, info->row_reference);
1925 modest_mail_operation_find_msg (mail_op, info->folder, info->msg_uid, TRUE, view_msg_cb, NULL);
1926 g_object_unref (mail_op);
1928 /* Update dimming rules */
1929 modest_ui_actions_check_toolbar_dimming_rules (MODEST_WINDOW (parent_window));
1930 modest_ui_actions_check_menu_dimming_rules (MODEST_WINDOW (parent_window));
1933 /* Frees. The row_reference will be freed by the view_msg_cb callback */
1934 g_free (info->msg_uid);
1936 g_object_unref (info->folder);
1938 g_object_unref (info->header);
1939 g_slice_free (MsgReaderInfo, info);
1944 * Reads the message whose summary item is @header. It takes care of
1945 * several things, among others:
1947 * If the message was not previously downloaded then ask the user
1948 * before downloading. If there is no connection launch the connection
1949 * dialog. Update toolbar dimming rules.
1951 * Returns: TRUE if the mail operation was started, otherwise if the
1952 * user do not want to download the message, or if the user do not
1953 * want to connect, then the operation is not issued
1956 message_reader (ModestMsgViewWindow *window,
1957 ModestMsgViewWindowPrivate *priv,
1959 const gchar *msg_uid,
1961 GtkTreeRowReference *row_reference)
1963 ModestWindowMgr *mgr;
1964 TnyAccount *account = NULL;
1965 MsgReaderInfo *info;
1967 /* We set the header from model while we're loading */
1968 tny_header_view_set_header (TNY_HEADER_VIEW (priv->msg_view), header);
1969 modest_window_set_title (MODEST_WINDOW (window), _CS_UPDATING);
1975 g_object_ref (folder);
1977 mgr = modest_runtime_get_window_mgr ();
1978 /* Msg download completed */
1979 if (!header || !(tny_header_get_flags (header) & TNY_HEADER_FLAG_CACHED)) {
1981 /* Ask the user if he wants to download the message if
1983 if (!tny_device_is_online (modest_runtime_get_device())) {
1984 GtkResponseType response;
1985 GtkWindow *toplevel;
1987 toplevel = (GtkWindow *) gtk_widget_get_toplevel ((GtkWidget *) window);
1988 response = modest_platform_run_confirmation_dialog (toplevel, _("mcen_nc_get_msg"));
1989 if (response == GTK_RESPONSE_CANCEL) {
1990 update_window_title (window);
1995 folder = tny_header_get_folder (header);
1997 info = g_slice_new (MsgReaderInfo);
1998 info->msg_uid = g_strdup (msg_uid);
2000 info->header = g_object_ref (header);
2002 info->header = NULL;
2004 info->folder = g_object_ref (folder);
2006 info->folder = NULL;
2007 if (row_reference) {
2008 info->row_reference = gtk_tree_row_reference_copy (row_reference);
2010 info->row_reference = NULL;
2013 /* Offer the connection dialog if necessary */
2014 modest_platform_connect_if_remote_and_perform ((ModestWindow *) window,
2016 TNY_FOLDER_STORE (folder),
2017 message_reader_performer,
2020 g_object_unref (folder);
2026 folder = tny_header_get_folder (header);
2029 account = tny_folder_get_account (folder);
2031 info = g_slice_new (MsgReaderInfo);
2032 info->msg_uid = g_strdup (msg_uid);
2034 info->folder = g_object_ref (folder);
2036 info->folder = NULL;
2038 info->header = g_object_ref (header);
2040 info->header = NULL;
2042 info->row_reference = gtk_tree_row_reference_copy (row_reference);
2044 info->row_reference = NULL;
2046 message_reader_performer (FALSE, NULL, (ModestWindow *) window, account, info);
2048 g_object_unref (account);
2050 g_object_unref (folder);
2056 modest_msg_view_window_select_next_message (ModestMsgViewWindow *window)
2058 ModestMsgViewWindowPrivate *priv;
2059 GtkTreePath *path= NULL;
2060 GtkTreeIter tmp_iter;
2062 gboolean retval = TRUE;
2063 GtkTreeRowReference *row_reference = NULL;
2065 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), FALSE);
2066 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
2068 if (!priv->row_reference)
2071 /* Update the next row reference if it's not valid. This could
2072 happen if for example the header which it was pointing to,
2073 was deleted. The best place to do it is in the row-deleted
2074 handler but the tinymail model do not work like the glib
2075 tree models and reports the deletion when the row is still
2077 if (!gtk_tree_row_reference_valid (priv->next_row_reference)) {
2078 if (priv->next_row_reference) {
2079 gtk_tree_row_reference_free (priv->next_row_reference);
2081 if (gtk_tree_row_reference_valid (priv->row_reference)) {
2082 priv->next_row_reference = gtk_tree_row_reference_copy (priv->row_reference);
2083 select_next_valid_row (priv->header_model, &(priv->next_row_reference), FALSE, priv->is_outbox);
2085 priv->next_row_reference = NULL;
2088 if (priv->next_row_reference)
2089 path = gtk_tree_row_reference_get_path (priv->next_row_reference);
2093 row_reference = gtk_tree_row_reference_copy (priv->next_row_reference);
2095 gtk_tree_model_get_iter (priv->header_model,
2098 gtk_tree_path_free (path);
2100 gtk_tree_model_get (priv->header_model, &tmp_iter,
2101 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
2104 /* Read the message & show it */
2105 if (!message_reader (window, priv, header, NULL, NULL, row_reference)) {
2108 gtk_tree_row_reference_free (row_reference);
2111 g_object_unref (header);
2117 modest_msg_view_window_select_previous_message (ModestMsgViewWindow *window)
2119 ModestMsgViewWindowPrivate *priv = NULL;
2121 gboolean finished = FALSE;
2122 gboolean retval = FALSE;
2124 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), FALSE);
2125 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
2127 if (priv->row_reference && !gtk_tree_row_reference_valid (priv->row_reference)) {
2128 gtk_tree_row_reference_free (priv->row_reference);
2129 priv->row_reference = NULL;
2132 /* Return inmediatly if there is no header model */
2133 if (!priv->header_model || !priv->row_reference)
2136 path = gtk_tree_row_reference_get_path (priv->row_reference);
2137 while (!finished && gtk_tree_path_prev (path)) {
2141 gtk_tree_model_get_iter (priv->header_model, &iter, path);
2142 gtk_tree_model_get (priv->header_model, &iter,
2143 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
2147 if (msg_is_visible (header, priv->is_outbox)) {
2148 GtkTreeRowReference *row_reference;
2149 row_reference = gtk_tree_row_reference_new (priv->header_model, path);
2150 /* Read the message & show it */
2151 retval = message_reader (window, priv, header, NULL, NULL, row_reference);
2152 gtk_tree_row_reference_free (row_reference);
2156 g_object_unref (header);
2160 gtk_tree_path_free (path);
2165 view_msg_cb (ModestMailOperation *mail_op,
2172 ModestMsgViewWindow *self = NULL;
2173 ModestMsgViewWindowPrivate *priv = NULL;
2174 GtkTreeRowReference *row_reference = NULL;
2176 /* Unregister the header (it was registered before creating the mail operation) */
2177 modest_window_mgr_unregister_header (modest_runtime_get_window_mgr (), header);
2179 row_reference = (GtkTreeRowReference *) user_data;
2182 gtk_tree_row_reference_free (row_reference);
2183 self = (ModestMsgViewWindow *) modest_mail_operation_get_source (mail_op);
2185 /* Restore window title */
2186 update_window_title (self);
2187 modest_ui_actions_on_close_window (NULL, MODEST_WINDOW (self));
2188 g_object_unref (self);
2193 /* If there was any error */
2194 if (!modest_ui_actions_msg_retrieval_check (mail_op, header, msg)) {
2196 gtk_tree_row_reference_free (row_reference);
2197 self = (ModestMsgViewWindow *) modest_mail_operation_get_source (mail_op);
2199 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
2200 /* First we check if the parent is a folder window */
2201 if (priv->msg_uid && !modest_window_mgr_get_folder_window (MODEST_WINDOW_MGR (modest_runtime_get_window_mgr ()))) {
2203 TnyAccount *account = NULL;
2204 GtkWidget *header_window = NULL;
2206 is_merge = g_str_has_prefix (priv->msg_uid, "merge:");
2208 /* Get the account */
2210 account = tny_account_store_find_account (TNY_ACCOUNT_STORE (modest_runtime_get_account_store ()),
2213 if (is_merge || account) {
2214 TnyFolder *folder = NULL;
2216 /* Try to get the message, if it's already downloaded
2217 we don't need to connect */
2219 folder = modest_tny_folder_store_find_folder_from_uri (TNY_FOLDER_STORE (account),
2222 ModestTnyAccountStore *account_store;
2223 ModestTnyLocalFoldersAccount *local_folders_account;
2225 account_store = modest_runtime_get_account_store ();
2226 local_folders_account = MODEST_TNY_LOCAL_FOLDERS_ACCOUNT (
2227 modest_tny_account_store_get_local_folders_account (account_store));
2228 folder = modest_tny_local_folders_account_get_merged_outbox (local_folders_account);
2229 g_object_unref (local_folders_account);
2231 if (account) g_object_unref (account);
2234 header_window = (GtkWidget *)
2235 modest_header_window_new (
2237 modest_window_get_active_account (MODEST_WINDOW (self)),
2238 modest_window_get_active_mailbox (MODEST_WINDOW (self)));
2239 if (!modest_window_mgr_register_window (modest_runtime_get_window_mgr (),
2240 MODEST_WINDOW (header_window),
2242 gtk_widget_destroy (GTK_WIDGET (header_window));
2244 gtk_widget_show_all (GTK_WIDGET (header_window));
2246 g_object_unref (folder);
2252 /* Restore window title */
2253 update_window_title (self);
2254 modest_ui_actions_on_close_window (NULL, MODEST_WINDOW (self));
2255 g_object_unref (self);
2260 /* Get the window */
2261 self = (ModestMsgViewWindow *) modest_mail_operation_get_source (mail_op);
2262 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self));
2263 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
2265 /* Update the row reference */
2266 if (priv->row_reference != NULL) {
2267 gtk_tree_row_reference_free (priv->row_reference);
2268 priv->row_reference = (row_reference && gtk_tree_row_reference_valid (row_reference))?gtk_tree_row_reference_copy (row_reference):NULL;
2269 if (priv->next_row_reference != NULL) {
2270 gtk_tree_row_reference_free (priv->next_row_reference);
2272 if (priv->row_reference) {
2273 priv->next_row_reference = gtk_tree_row_reference_copy (priv->row_reference);
2274 select_next_valid_row (priv->header_model, &(priv->next_row_reference), TRUE, priv->is_outbox);
2276 priv->next_row_reference = NULL;
2280 /* Mark header as read */
2281 if (!(tny_header_get_flags (header) & TNY_HEADER_FLAG_SEEN))
2282 tny_header_set_flag (header, TNY_HEADER_FLAG_SEEN);
2284 /* Set new message */
2285 if (priv->msg_view != NULL && TNY_IS_MSG_VIEW (priv->msg_view)) {
2286 tny_msg_view_set_msg (TNY_MSG_VIEW (priv->msg_view), msg);
2287 modest_msg_view_window_update_priority (self);
2288 update_window_title (MODEST_MSG_VIEW_WINDOW (self));
2289 update_branding (MODEST_MSG_VIEW_WINDOW (self));
2290 modest_msg_view_grab_focus (MODEST_MSG_VIEW (priv->msg_view));
2293 /* Set the new message uid of the window */
2294 if (priv->msg_uid) {
2295 g_free (priv->msg_uid);
2296 priv->msg_uid = modest_tny_folder_get_header_unique_id (header);
2299 /* Notify the observers */
2300 g_signal_emit (G_OBJECT (self), signals[MSG_CHANGED_SIGNAL],
2301 0, priv->header_model, priv->row_reference);
2303 /* Sync the flags if the message is not opened from a header
2304 model, i.e, if it's opened from a notification */
2305 if (!priv->header_model)
2309 g_object_unref (self);
2311 gtk_tree_row_reference_free (row_reference);
2315 modest_msg_view_window_get_folder_type (ModestMsgViewWindow *window)
2317 ModestMsgViewWindowPrivate *priv;
2319 TnyFolderType folder_type;
2321 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
2323 folder_type = TNY_FOLDER_TYPE_UNKNOWN;
2325 msg = tny_msg_view_get_msg (TNY_MSG_VIEW (priv->msg_view));
2329 folder = tny_msg_get_folder (msg);
2331 folder_type = modest_tny_folder_guess_folder_type (folder);
2332 g_object_unref (folder);
2334 g_object_unref (msg);
2342 modest_msg_view_window_update_priority (ModestMsgViewWindow *window)
2344 ModestMsgViewWindowPrivate *priv;
2345 TnyHeader *header = NULL;
2346 TnyHeaderFlags flags = 0;
2348 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
2350 if (priv->header_model && priv->row_reference && gtk_tree_row_reference_valid (priv->row_reference)) {
2352 GtkTreePath *path = NULL;
2354 path = gtk_tree_row_reference_get_path (priv->row_reference);
2355 g_return_if_fail (path != NULL);
2356 gtk_tree_model_get_iter (priv->header_model,
2358 gtk_tree_row_reference_get_path (priv->row_reference));
2360 gtk_tree_model_get (priv->header_model, &iter, TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
2362 gtk_tree_path_free (path);
2365 msg = tny_msg_view_get_msg (TNY_MSG_VIEW (priv->msg_view));
2367 header = tny_msg_get_header (msg);
2368 g_object_unref (msg);
2373 flags = tny_header_get_flags (header);
2374 g_object_unref(G_OBJECT(header));
2377 modest_msg_view_set_priority (MODEST_MSG_VIEW(priv->msg_view), flags);
2382 toolbar_resize (ModestMsgViewWindow *self)
2384 ModestMsgViewWindowPrivate *priv = NULL;
2385 ModestWindowPrivate *parent_priv = NULL;
2387 gint static_button_size;
2388 ModestWindowMgr *mgr;
2390 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self));
2391 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
2392 parent_priv = MODEST_WINDOW_GET_PRIVATE(self);
2394 mgr = modest_runtime_get_window_mgr ();
2395 static_button_size = modest_window_mgr_get_fullscreen_mode (mgr)?120:120;
2397 if (parent_priv->toolbar) {
2398 /* Set expandable and homogeneous tool buttons */
2399 widget = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/ToolbarMessageReply");
2400 gtk_tool_item_set_expand (GTK_TOOL_ITEM (widget), TRUE);
2401 gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (widget), TRUE);
2402 widget = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/ToolbarMessageReplyAll");
2403 gtk_tool_item_set_expand (GTK_TOOL_ITEM (widget), TRUE);
2404 gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (widget), TRUE);
2405 widget = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/ToolbarMessageForward");
2406 gtk_tool_item_set_expand (GTK_TOOL_ITEM (widget), TRUE);
2407 gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (widget), TRUE);
2408 widget = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/ToolbarDeleteMessage");
2409 gtk_tool_item_set_expand (GTK_TOOL_ITEM (widget), TRUE);
2410 gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (widget), TRUE);
2411 widget = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/ToolbarDownloadExternalImages");
2412 gtk_tool_item_set_expand (GTK_TOOL_ITEM (widget), TRUE);
2413 gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (widget), TRUE);
2414 gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (priv->next_toolitem), TRUE);
2415 gtk_tool_item_set_expand (GTK_TOOL_ITEM (priv->next_toolitem), TRUE);
2416 gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (priv->prev_toolitem), TRUE);
2417 gtk_tool_item_set_expand (GTK_TOOL_ITEM (priv->prev_toolitem), TRUE);
2422 modest_msg_view_window_show_toolbar (ModestWindow *self,
2423 gboolean show_toolbar)
2425 ModestMsgViewWindowPrivate *priv = NULL;
2426 ModestWindowPrivate *parent_priv;
2428 parent_priv = MODEST_WINDOW_GET_PRIVATE(self);
2429 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(self);
2431 /* Set optimized view status */
2432 priv->optimized_view = !show_toolbar;
2434 if (!parent_priv->toolbar) {
2435 parent_priv->toolbar = gtk_ui_manager_get_widget (parent_priv->ui_manager,
2438 #ifdef MODEST_TOOLKIT_HILDON2
2439 gtk_toolbar_set_icon_size (GTK_TOOLBAR (parent_priv->toolbar), HILDON_ICON_SIZE_FINGER);
2441 gtk_toolbar_set_icon_size (GTK_TOOLBAR (parent_priv->toolbar), GTK_ICON_SIZE_LARGE_TOOLBAR);
2443 gtk_widget_set_no_show_all (parent_priv->toolbar, TRUE);
2445 priv->next_toolitem = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/ToolbarMessageNext");
2446 priv->prev_toolitem = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/ToolbarMessageBack");
2447 toolbar_resize (MODEST_MSG_VIEW_WINDOW (self));
2449 modest_window_add_toolbar (MODEST_WINDOW (self),
2450 GTK_TOOLBAR (parent_priv->toolbar));
2455 /* Quick hack: this prevents toolbar icons "dance" when progress bar show status is changed */
2456 /* TODO: resize mode migth be GTK_RESIZE_QUEUE, in order to avoid unneccesary shows */
2457 gtk_container_set_resize_mode (GTK_CONTAINER(parent_priv->toolbar), GTK_RESIZE_IMMEDIATE);
2459 gtk_widget_show (GTK_WIDGET (parent_priv->toolbar));
2460 if (modest_msg_view_window_transfer_mode_enabled (MODEST_MSG_VIEW_WINDOW (self)))
2461 set_progress_hint (MODEST_MSG_VIEW_WINDOW (self), TRUE);
2463 set_progress_hint (MODEST_MSG_VIEW_WINDOW (self), FALSE);
2466 gtk_widget_set_no_show_all (parent_priv->toolbar, TRUE);
2467 gtk_widget_hide (GTK_WIDGET (parent_priv->toolbar));
2472 modest_msg_view_window_clipboard_owner_change (GtkClipboard *clipboard,
2474 ModestMsgViewWindow *window)
2476 if (!GTK_WIDGET_VISIBLE (window))
2479 modest_window_check_dimming_rules_group (MODEST_WINDOW (window), MODEST_DIMMING_RULES_CLIPBOARD);
2483 modest_msg_view_window_transfer_mode_enabled (ModestMsgViewWindow *self)
2485 ModestMsgViewWindowPrivate *priv;
2487 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self), FALSE);
2488 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(self);
2490 return priv->progress_hint;
2494 observers_empty (ModestMsgViewWindow *self)
2497 ModestMsgViewWindowPrivate *priv;
2498 gboolean is_empty = TRUE;
2499 guint pending_ops = 0;
2501 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(self);
2502 tmp = priv->progress_widgets;
2504 /* Check all observers */
2505 while (tmp && is_empty) {
2506 pending_ops = modest_progress_object_num_pending_operations (MODEST_PROGRESS_OBJECT(tmp->data));
2507 is_empty = pending_ops == 0;
2509 tmp = g_slist_next(tmp);
2516 on_account_removed (TnyAccountStore *account_store,
2517 TnyAccount *account,
2520 /* Do nothing if it's a transport account, because we only
2521 show the messages of a store account */
2522 if (tny_account_get_account_type(account) == TNY_ACCOUNT_TYPE_STORE) {
2523 const gchar *parent_acc = NULL;
2524 const gchar *our_acc = NULL;
2526 our_acc = modest_window_get_active_account (MODEST_WINDOW (user_data));
2527 parent_acc = modest_tny_account_get_parent_modest_account_name_for_server_account (account);
2529 /* Close this window if I'm showing a message of the removed account */
2530 if (our_acc && parent_acc && strcmp (parent_acc, our_acc) == 0)
2531 modest_ui_actions_on_close_window (NULL, MODEST_WINDOW (user_data));
2536 on_mail_operation_started (ModestMailOperation *mail_op,
2539 ModestMsgViewWindow *self;
2540 ModestMailOperationTypeOperation op_type;
2542 ModestMsgViewWindowPrivate *priv;
2543 GObject *source = NULL;
2545 self = MODEST_MSG_VIEW_WINDOW (user_data);
2546 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
2547 op_type = modest_mail_operation_get_type_operation (mail_op);
2548 tmp = priv->progress_widgets;
2549 source = modest_mail_operation_get_source(mail_op);
2550 if (G_OBJECT (self) == source) {
2551 if (op_type == MODEST_MAIL_OPERATION_TYPE_RECEIVE ||
2552 op_type == MODEST_MAIL_OPERATION_TYPE_OPEN ||
2553 op_type == MODEST_MAIL_OPERATION_TYPE_DELETE) {
2554 set_progress_hint (self, TRUE);
2556 modest_progress_object_add_operation (
2557 MODEST_PROGRESS_OBJECT (tmp->data),
2559 tmp = g_slist_next (tmp);
2563 g_object_unref (source);
2565 /* Update dimming rules */
2566 check_dimming_rules_after_change (self);
2570 on_mail_operation_finished (ModestMailOperation *mail_op,
2573 ModestMsgViewWindow *self;
2574 ModestMailOperationTypeOperation op_type;
2576 ModestMsgViewWindowPrivate *priv;
2578 self = MODEST_MSG_VIEW_WINDOW (user_data);
2579 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
2580 op_type = modest_mail_operation_get_type_operation (mail_op);
2581 tmp = priv->progress_widgets;
2583 if (op_type == MODEST_MAIL_OPERATION_TYPE_RECEIVE ||
2584 op_type == MODEST_MAIL_OPERATION_TYPE_OPEN ||
2585 op_type == MODEST_MAIL_OPERATION_TYPE_DELETE) {
2587 modest_progress_object_remove_operation (MODEST_PROGRESS_OBJECT (tmp->data),
2589 tmp = g_slist_next (tmp);
2592 /* If no more operations are being observed, NORMAL mode is enabled again */
2593 if (observers_empty (self)) {
2594 set_progress_hint (self, FALSE);
2598 /* Update dimming rules. We have to do this right here
2599 and not in view_msg_cb because at that point the
2600 transfer mode is still enabled so the dimming rule
2601 won't let the user delete the message that has been
2602 readed for example */
2603 check_dimming_rules_after_change (self);
2607 on_queue_changed (ModestMailOperationQueue *queue,
2608 ModestMailOperation *mail_op,
2609 ModestMailOperationQueueNotification type,
2610 ModestMsgViewWindow *self)
2612 ModestMsgViewWindowPrivate *priv;
2614 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
2616 /* If this operations was created by another window, do nothing */
2617 if (!modest_mail_operation_is_mine (mail_op, G_OBJECT(self)))
2620 if (type == MODEST_MAIL_OPERATION_QUEUE_OPERATION_ADDED) {
2621 priv->sighandlers = modest_signal_mgr_connect (priv->sighandlers,
2623 "operation-started",
2624 G_CALLBACK (on_mail_operation_started),
2626 priv->sighandlers = modest_signal_mgr_connect (priv->sighandlers,
2628 "operation-finished",
2629 G_CALLBACK (on_mail_operation_finished),
2631 } else if (type == MODEST_MAIL_OPERATION_QUEUE_OPERATION_REMOVED) {
2632 priv->sighandlers = modest_signal_mgr_disconnect (priv->sighandlers,
2634 "operation-started");
2635 priv->sighandlers = modest_signal_mgr_disconnect (priv->sighandlers,
2637 "operation-finished");
2642 modest_msg_view_window_get_attachments (ModestMsgViewWindow *win)
2644 ModestMsgViewWindowPrivate *priv;
2645 TnyList *selected_attachments = NULL;
2647 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (win), NULL);
2648 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (win);
2650 /* In Hildon 2.2 as there's no selection we assume we have all attachments selected */
2651 selected_attachments = modest_msg_view_get_attachments (MODEST_MSG_VIEW (priv->msg_view));
2653 return selected_attachments;
2657 ModestMsgViewWindow *self;
2659 gchar *attachment_uid;
2660 } DecodeAsyncHelper;
2663 on_decode_to_stream_async_handler (TnyMimePart *mime_part,
2669 DecodeAsyncHelper *helper = (DecodeAsyncHelper *) user_data;
2670 const gchar *content_type;
2672 if (cancelled || err) {
2675 if ((err->domain == TNY_ERROR_DOMAIN) &&
2676 (err->code == TNY_IO_ERROR_WRITE) &&
2677 (errno == ENOSPC)) {
2678 msg = g_strdup_printf (_KR("cerm_device_memory_full"), "");
2680 msg = g_strdup (_("mail_ib_file_operation_failed"));
2682 modest_platform_information_banner (NULL, NULL, msg);
2688 /* It could happen that the window was closed. So we
2689 assume it is a cancelation */
2690 if (!GTK_WIDGET_VISIBLE (helper->self))
2693 /* Remove the progress hint */
2694 set_progress_hint (helper->self, FALSE);
2696 content_type = tny_mime_part_get_content_type (mime_part);
2697 if (content_type && g_str_has_prefix (content_type, "message/rfc822")) {
2698 ModestWindowMgr *mgr;
2699 ModestWindow *msg_win = NULL;
2702 const gchar *mailbox;
2703 TnyStream *file_stream;
2706 fd = g_open (helper->file_path, O_RDONLY, 0644);
2708 file_stream = tny_fs_stream_new (fd);
2710 mgr = modest_runtime_get_window_mgr ();
2712 account = g_strdup (modest_window_get_active_account (MODEST_WINDOW (helper->self)));
2713 mailbox = modest_window_get_active_mailbox (MODEST_WINDOW (helper->self));
2716 account = modest_account_mgr_get_default_account (modest_runtime_get_account_mgr ());
2718 msg = tny_camel_msg_new ();
2719 tny_camel_msg_parse ((TnyCamelMsg *) msg, file_stream);
2720 msg_win = modest_msg_view_window_new_for_attachment (TNY_MSG (msg), account, mailbox, helper->attachment_uid);
2721 modest_window_set_zoom (MODEST_WINDOW (msg_win),
2722 modest_window_get_zoom (MODEST_WINDOW (helper->self)));
2723 if (modest_window_mgr_register_window (mgr, msg_win, MODEST_WINDOW (helper->self)))
2724 gtk_widget_show_all (GTK_WIDGET (msg_win));
2726 gtk_widget_destroy (GTK_WIDGET (msg_win));
2727 g_object_unref (msg);
2728 g_object_unref (file_stream);
2730 modest_platform_information_banner (NULL, NULL, _("mail_ib_file_operation_failed"));
2735 /* make the file read-only */
2736 g_chmod(helper->file_path, 0444);
2738 /* Activate the file */
2739 modest_platform_activate_file (helper->file_path, content_type);
2744 g_object_unref (helper->self);
2745 g_free (helper->file_path);
2746 g_free (helper->attachment_uid);
2747 g_slice_free (DecodeAsyncHelper, helper);
2751 view_attachment_connect_handler (gboolean canceled,
2753 GtkWindow *parent_window,
2754 TnyAccount *account,
2758 if (canceled || err) {
2759 g_object_unref (part);
2763 modest_msg_view_window_view_attachment (MODEST_MSG_VIEW_WINDOW (parent_window),
2765 g_object_unref (part);
2769 modest_msg_view_window_view_attachment (ModestMsgViewWindow *window,
2770 TnyMimePart *mime_part)
2772 ModestMsgViewWindowPrivate *priv;
2773 const gchar *msg_uid;
2774 gchar *attachment_uid = NULL;
2775 gint attachment_index = 0;
2776 TnyList *attachments;
2778 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window));
2779 g_return_if_fail (TNY_IS_MIME_PART (mime_part) || (mime_part == NULL));
2780 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
2782 msg_uid = modest_msg_view_window_get_message_uid (MODEST_MSG_VIEW_WINDOW (window));
2783 attachments = modest_msg_view_get_attachments (MODEST_MSG_VIEW (priv->msg_view));
2784 attachment_index = modest_list_index (attachments, (GObject *) mime_part);
2785 g_object_unref (attachments);
2787 if (msg_uid && attachment_index >= 0) {
2788 attachment_uid = g_strdup_printf ("%s/%d", msg_uid, attachment_index);
2791 if (mime_part == NULL) {
2792 gboolean error = FALSE;
2793 TnyList *selected_attachments = modest_msg_view_get_selected_attachments (MODEST_MSG_VIEW (priv->msg_view));
2794 if (selected_attachments == NULL || tny_list_get_length (selected_attachments) == 0) {
2796 } else if (tny_list_get_length (selected_attachments) > 1) {
2797 modest_platform_system_banner (NULL, NULL, _("mcen_ib_unable_to_display_more"));
2801 iter = tny_list_create_iterator (selected_attachments);
2802 mime_part = (TnyMimePart *) tny_iterator_get_current (iter);
2803 g_object_unref (iter);
2805 if (selected_attachments)
2806 g_object_unref (selected_attachments);
2811 g_object_ref (mime_part);
2814 if (tny_mime_part_is_purged (mime_part))
2817 if (TNY_IS_CAMEL_BS_MIME_PART (mime_part) &&
2818 !tny_camel_bs_mime_part_is_fetched (TNY_CAMEL_BS_MIME_PART (mime_part))) {
2820 TnyAccount *account;
2822 is_merge = g_str_has_prefix (priv->msg_uid, "merge:");
2824 /* Get the account */
2826 account = tny_account_store_find_account (TNY_ACCOUNT_STORE (modest_runtime_get_account_store ()),
2829 if (!tny_device_is_online (modest_runtime_get_device())) {
2830 modest_platform_connect_and_perform ((ModestWindow *) window,
2832 TNY_ACCOUNT (account),
2833 (ModestConnectedPerformer) view_attachment_connect_handler,
2834 g_object_ref (mime_part));
2839 if (!modest_tny_mime_part_is_msg (mime_part) && tny_mime_part_get_filename (mime_part)) {
2840 gchar *filepath = NULL;
2841 const gchar *att_filename = tny_mime_part_get_filename (mime_part);
2842 gboolean show_error_banner = FALSE;
2843 TnyFsStream *temp_stream = NULL;
2844 temp_stream = modest_utils_create_temp_stream (att_filename, attachment_uid,
2847 if (temp_stream != NULL) {
2848 ModestAccountMgr *mgr;
2849 DecodeAsyncHelper *helper;
2850 gboolean decode_in_provider;
2851 ModestProtocol *protocol;
2852 const gchar *account;
2854 /* Activate progress hint */
2855 set_progress_hint (window, TRUE);
2857 helper = g_slice_new0 (DecodeAsyncHelper);
2858 helper->self = g_object_ref (window);
2859 helper->file_path = g_strdup (filepath);
2860 helper->attachment_uid = g_strdup (attachment_uid);
2862 decode_in_provider = FALSE;
2863 mgr = modest_runtime_get_account_mgr ();
2864 account = modest_window_get_active_account (MODEST_WINDOW (window));
2865 if (modest_account_mgr_account_is_multimailbox (mgr, account, &protocol)) {
2866 if (MODEST_IS_ACCOUNT_PROTOCOL (protocol)) {
2868 uri = g_strconcat ("file://", filepath, NULL);
2869 decode_in_provider =
2870 modest_account_protocol_decode_part_to_stream_async (
2871 MODEST_ACCOUNT_PROTOCOL (protocol),
2874 TNY_STREAM (temp_stream),
2875 on_decode_to_stream_async_handler,
2882 if (!decode_in_provider)
2883 tny_mime_part_decode_to_stream_async (mime_part, TNY_STREAM (temp_stream),
2884 on_decode_to_stream_async_handler,
2887 g_object_unref (temp_stream);
2888 /* NOTE: files in the temporary area will be automatically
2889 * cleaned after some time if they are no longer in use */
2892 const gchar *content_type;
2893 /* the file may already exist but it isn't writable,
2894 * let's try to open it anyway */
2895 content_type = tny_mime_part_get_content_type (mime_part);
2896 modest_platform_activate_file (filepath, content_type);
2898 g_warning ("%s: modest_utils_create_temp_stream failed", __FUNCTION__);
2899 show_error_banner = TRUE;
2904 if (show_error_banner)
2905 modest_platform_information_banner (NULL, NULL, _("mail_ib_file_operation_failed"));
2906 } else if (!modest_tny_mime_part_is_msg (mime_part)) {
2907 ModestWindowMgr *mgr;
2908 ModestWindow *msg_win = NULL;
2909 TnyMsg *current_msg;
2913 current_msg = modest_msg_view_window_get_message (MODEST_MSG_VIEW_WINDOW (window));
2914 mgr = modest_runtime_get_window_mgr ();
2915 header = tny_msg_get_header (TNY_MSG (current_msg));
2916 found = modest_window_mgr_find_registered_message_uid (mgr,
2921 g_debug ("window for this body is already being created");
2924 /* it's not found, so create a new window for it */
2925 modest_window_mgr_register_header (mgr, header, attachment_uid); /* register the uid before building the window */
2926 gchar *account = g_strdup (modest_window_get_active_account (MODEST_WINDOW (window)));
2927 const gchar *mailbox = modest_window_get_active_mailbox (MODEST_WINDOW (window));
2929 account = modest_account_mgr_get_default_account (modest_runtime_get_account_mgr ());
2931 msg_win = modest_msg_view_window_new_with_other_body (TNY_MSG (current_msg), TNY_MIME_PART (mime_part),
2932 account, mailbox, attachment_uid);
2934 modest_window_set_zoom (MODEST_WINDOW (msg_win),
2935 modest_window_get_zoom (MODEST_WINDOW (window)));
2936 if (modest_window_mgr_register_window (mgr, msg_win, MODEST_WINDOW (window)))
2937 gtk_widget_show_all (GTK_WIDGET (msg_win));
2939 gtk_widget_destroy (GTK_WIDGET (msg_win));
2941 g_object_unref (current_msg);
2943 /* message attachment */
2944 TnyHeader *header = NULL;
2945 ModestWindowMgr *mgr;
2946 ModestWindow *msg_win = NULL;
2949 header = tny_msg_get_header (TNY_MSG (mime_part));
2950 mgr = modest_runtime_get_window_mgr ();
2951 found = modest_window_mgr_find_registered_header (mgr, header, &msg_win);
2954 /* if it's found, but there is no msg_win, it's probably in the process of being created;
2955 * thus, we don't do anything */
2956 g_debug ("window for is already being created");
2958 /* it's not found, so create a new window for it */
2959 modest_window_mgr_register_header (mgr, header, attachment_uid); /* register the uid before building the window */
2960 gchar *account = g_strdup (modest_window_get_active_account (MODEST_WINDOW (window)));
2961 const gchar *mailbox = modest_window_get_active_mailbox (MODEST_WINDOW (window));
2963 account = modest_account_mgr_get_default_account (modest_runtime_get_account_mgr ());
2964 msg_win = modest_msg_view_window_new_for_attachment (TNY_MSG (mime_part), account,
2965 mailbox, attachment_uid);
2966 modest_window_set_zoom (MODEST_WINDOW (msg_win),
2967 modest_window_get_zoom (MODEST_WINDOW (window)));
2968 if (modest_window_mgr_register_window (mgr, msg_win, MODEST_WINDOW (window)))
2969 gtk_widget_show_all (GTK_WIDGET (msg_win));
2971 gtk_widget_destroy (GTK_WIDGET (msg_win));
2977 g_free (attachment_uid);
2979 g_object_unref (mime_part);
2991 GnomeVFSResult result;
2993 ModestMsgViewWindow *window;
2996 static void save_mime_part_info_free (SaveMimePartInfo *info, gboolean with_struct);
2997 static gboolean idle_save_mime_part_show_result (SaveMimePartInfo *info);
2998 static gpointer save_mime_part_to_file (SaveMimePartInfo *info);
2999 static void save_mime_parts_to_file_with_checks (GtkWindow *parent, SaveMimePartInfo *info);
3002 save_mime_part_info_free (SaveMimePartInfo *info, gboolean with_struct)
3006 for (node = info->pairs; node != NULL; node = g_list_next (node)) {
3007 SaveMimePartPair *pair = (SaveMimePartPair *) node->data;
3008 g_free (pair->filename);
3009 g_object_unref (pair->part);
3010 g_slice_free (SaveMimePartPair, pair);
3012 g_list_free (info->pairs);
3015 g_object_unref (info->window);
3016 info->window = NULL;
3018 g_slice_free (SaveMimePartInfo, info);
3023 idle_save_mime_part_show_result (SaveMimePartInfo *info)
3025 /* This is a GDK lock because we are an idle callback and
3026 * modest_platform_system_banner is or does Gtk+ code */
3028 gdk_threads_enter (); /* CHECKED */
3029 if (info->result == GNOME_VFS_ERROR_CANCELLED) {
3031 } else if (info->result == GNOME_VFS_OK) {
3032 modest_platform_system_banner (NULL, NULL, _CS_SAVED);
3033 } else if (info->result == GNOME_VFS_ERROR_NO_SPACE) {
3036 /* Check if the uri belongs to the external mmc */
3037 if (g_str_has_prefix (info->uri, g_getenv (MODEST_MMC1_VOLUMEPATH_ENV)))
3038 msg = g_strdup_printf (_KR("cerm_device_memory_full"), "");
3040 msg = g_strdup (_KR("cerm_memory_card_full"));
3041 modest_platform_information_banner (NULL, NULL, msg);
3044 modest_platform_system_banner (NULL, NULL, _("mail_ib_file_operation_failed"));
3046 save_mime_part_info_free (info, FALSE);
3047 gdk_threads_leave (); /* CHECKED */
3053 save_mime_part_to_file_connect_handler (gboolean canceled,
3055 GtkWindow *parent_window,
3056 TnyAccount *account,
3057 SaveMimePartInfo *info)
3059 if (canceled || err) {
3060 if (canceled && !err) {
3061 info->result = GNOME_VFS_ERROR_CANCELLED;
3063 g_idle_add ((GSourceFunc) idle_save_mime_part_show_result, info);
3065 g_thread_create ((GThreadFunc)save_mime_part_to_file, info, FALSE, NULL);
3070 save_mime_part_to_file_connect_idle (SaveMimePartInfo *info)
3073 TnyAccount *account;
3074 ModestMsgViewWindowPrivate *priv;
3076 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (info->window);
3078 is_merge = g_str_has_prefix (priv->msg_uid, "merge:");
3081 /* Get the account */
3083 account = tny_account_store_find_account (TNY_ACCOUNT_STORE (modest_runtime_get_account_store ()),
3086 modest_platform_connect_and_perform ((ModestWindow *) info->window,
3088 TNY_ACCOUNT (account),
3089 (ModestConnectedPerformer) save_mime_part_to_file_connect_handler,
3093 g_object_unref (account);
3099 save_mime_part_to_file (SaveMimePartInfo *info)
3101 GnomeVFSHandle *handle;
3103 SaveMimePartPair *pair = (SaveMimePartPair *) info->pairs->data;
3105 if (TNY_IS_CAMEL_BS_MIME_PART (pair->part) &&
3106 !tny_camel_bs_mime_part_is_fetched (TNY_CAMEL_BS_MIME_PART (pair->part))) {
3107 gboolean check_online = TRUE;
3108 ModestMsgViewWindowPrivate *priv = NULL;
3110 /* Check if we really need to connect to save the mime part */
3111 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (info->window);
3112 if (g_str_has_prefix (priv->msg_uid, "merge:")) {
3113 check_online = FALSE;
3115 TnyAccountStore *acc_store;
3116 TnyAccount *account = NULL;
3118 acc_store = (TnyAccountStore*) modest_runtime_get_account_store ();
3119 account = tny_account_store_find_account (acc_store, priv->msg_uid);
3122 if (tny_account_get_connection_status (account) ==
3123 TNY_CONNECTION_STATUS_CONNECTED)
3124 check_online = FALSE;
3125 g_object_unref (account);
3127 check_online = !tny_device_is_online (tny_account_store_get_device (acc_store));
3132 g_idle_add ((GSourceFunc) save_mime_part_to_file_connect_idle, info);
3137 info->result = gnome_vfs_create (&handle, pair->filename, GNOME_VFS_OPEN_WRITE, FALSE, 0644);
3138 if (info->result == GNOME_VFS_OK) {
3139 GError *error = NULL;
3140 gboolean decode_in_provider;
3142 ModestAccountMgr *mgr;
3143 const gchar *account;
3144 ModestProtocol *protocol = NULL;
3146 stream = tny_vfs_stream_new (handle);
3148 decode_in_provider = FALSE;
3149 mgr = modest_runtime_get_account_mgr ();
3150 account = modest_window_get_active_account (MODEST_WINDOW (info->window));
3151 if (modest_account_mgr_account_is_multimailbox (mgr, account, &protocol)) {
3152 if (MODEST_IS_ACCOUNT_PROTOCOL (protocol)) {
3153 decode_in_provider =
3154 modest_account_protocol_decode_part_to_stream (
3155 MODEST_ACCOUNT_PROTOCOL (protocol),
3163 if (!decode_in_provider)
3164 written = tny_mime_part_decode_to_stream (pair->part, stream, &error);
3167 g_warning ("modest: could not save attachment %s: %d (%s)\n", pair->filename, error?error->code:-1, error?error->message:"Unknown error");
3169 if ((error->domain == TNY_ERROR_DOMAIN) &&
3170 (error->code == TNY_IO_ERROR_WRITE) &&
3171 (errno == ENOSPC)) {
3172 info->result = GNOME_VFS_ERROR_NO_SPACE;
3174 info->result = GNOME_VFS_ERROR_IO;
3177 g_object_unref (G_OBJECT (stream));
3179 g_warning ("Could not create save attachment %s: %s\n",
3180 pair->filename, gnome_vfs_result_to_string (info->result));
3183 /* Go on saving remaining files */
3184 info->pairs = g_list_remove_link (info->pairs, info->pairs);
3185 if (info->pairs != NULL) {
3186 save_mime_part_to_file (info);
3188 g_idle_add ((GSourceFunc) idle_save_mime_part_show_result, info);
3195 save_mime_parts_to_file_with_checks (GtkWindow *parent,
3196 SaveMimePartInfo *info)
3198 gboolean is_ok = TRUE;
3199 gint replaced_files = 0;
3200 const GList *files = info->pairs;
3201 const GList *iter, *to_replace = NULL;
3203 for (iter = files; (iter != NULL) && (replaced_files < 2); iter = g_list_next(iter)) {
3204 SaveMimePartPair *pair = iter->data;
3205 gchar *unescaped = g_uri_unescape_string (pair->filename, NULL);
3207 if (modest_utils_file_exists (unescaped)) {
3209 if (replaced_files == 1)
3214 if (replaced_files) {
3217 if (replaced_files == 1) {
3218 SaveMimePartPair *pair = to_replace->data;
3219 const gchar *basename = strrchr (pair->filename, G_DIR_SEPARATOR) + 1;
3220 gchar *escaped_basename, *message;
3222 escaped_basename = g_uri_unescape_string (basename, NULL);
3223 message = g_strdup_printf ("%s\n%s",
3225 (escaped_basename) ? escaped_basename : "");
3226 response = modest_platform_run_confirmation_dialog (parent, message);
3228 g_free (escaped_basename);
3230 response = modest_platform_run_confirmation_dialog (parent,
3231 _FM_REPLACE_MULTIPLE);
3233 if (response != GTK_RESPONSE_OK)
3238 save_mime_part_info_free (info, TRUE);
3240 g_thread_create ((GThreadFunc)save_mime_part_to_file, info, FALSE, NULL);
3245 typedef struct _SaveAttachmentsInfo {
3246 TnyList *attachments_list;
3247 ModestMsgViewWindow *window;
3248 } SaveAttachmentsInfo;
3251 save_attachments_response (GtkDialog *dialog,
3255 TnyList *mime_parts;
3257 GList *files_to_save = NULL;
3258 gchar *current_folder;
3259 SaveAttachmentsInfo *sa_info = (SaveAttachmentsInfo *) user_data;
3261 mime_parts = TNY_LIST (sa_info->attachments_list);
3263 if (arg1 != GTK_RESPONSE_OK)
3266 chooser_uri = gtk_file_chooser_get_uri (GTK_FILE_CHOOSER (dialog));
3267 current_folder = gtk_file_chooser_get_current_folder_uri (GTK_FILE_CHOOSER (dialog));
3268 if (current_folder && *current_folder != '\0') {
3270 modest_conf_set_string (modest_runtime_get_conf (), MODEST_CONF_LATEST_SAVE_ATTACHMENT_PATH,
3271 current_folder,&err);
3273 g_debug ("Error storing latest used folder: %s", err->message);
3277 g_free (current_folder);
3279 if (!modest_utils_folder_writable (chooser_uri)) {
3280 const gchar *err_msg;
3282 #ifdef MODEST_PLATFORM_MAEMO
3283 if (modest_maemo_utils_in_usb_mode ()) {
3284 err_msg = dgettext ("hildon-status-bar-usb", "usbh_ib_mmc_usb_connected");
3286 err_msg = _FM_READ_ONLY_LOCATION;
3289 err_msg = _FM_READ_ONLY_LOCATION;
3291 modest_platform_system_banner (NULL, NULL, err_msg);
3295 iter = tny_list_create_iterator (mime_parts);
3296 while (!tny_iterator_is_done (iter)) {
3297 TnyMimePart *mime_part = (TnyMimePart *) tny_iterator_get_current (iter);
3299 if ((modest_tny_mime_part_is_attachment_for_modest (mime_part)) &&
3300 !tny_mime_part_is_purged (mime_part) &&
3301 (tny_mime_part_get_filename (mime_part) != NULL)) {
3302 SaveMimePartPair *pair;
3304 pair = g_slice_new0 (SaveMimePartPair);
3306 if (tny_list_get_length (mime_parts) > 1) {
3308 gnome_vfs_escape_slashes (tny_mime_part_get_filename (mime_part));
3309 pair->filename = g_build_filename (chooser_uri, escaped, NULL);
3312 pair->filename = g_strdup (chooser_uri);
3314 pair->part = mime_part;
3315 files_to_save = g_list_prepend (files_to_save, pair);
3317 tny_iterator_next (iter);
3319 g_object_unref (iter);
3322 if (files_to_save != NULL) {
3323 SaveMimePartInfo *info = g_slice_new0 (SaveMimePartInfo);
3324 info->pairs = files_to_save;
3325 info->result = TRUE;
3326 info->uri = g_strdup (chooser_uri);
3327 info->window = g_object_ref (sa_info->window);
3328 save_mime_parts_to_file_with_checks ((GtkWindow *) dialog, info);
3330 g_free (chooser_uri);
3333 /* Free and close the dialog */
3334 g_object_unref (mime_parts);
3335 g_object_unref (sa_info->window);
3336 g_slice_free (SaveAttachmentsInfo, sa_info);
3337 gtk_widget_destroy (GTK_WIDGET (dialog));
3341 modest_msg_view_window_save_attachments (ModestMsgViewWindow *window,
3342 TnyList *mime_parts)
3344 ModestMsgViewWindowPrivate *priv;
3345 GtkWidget *save_dialog = NULL;
3346 gchar *conf_folder = NULL;
3347 gchar *filename = NULL;
3348 gchar *save_multiple_str = NULL;
3349 const gchar *root_folder = "file:///";
3351 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window));
3352 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
3354 if (mime_parts == NULL) {
3355 /* In Hildon 2.2 save and delete operate over all the attachments as there's no
3356 * selection available */
3357 mime_parts = modest_msg_view_get_attachments (MODEST_MSG_VIEW (priv->msg_view));
3358 if (mime_parts && !modest_toolkit_utils_select_attachments (GTK_WINDOW (gtk_widget_get_toplevel (GTK_WIDGET (window))), mime_parts, FALSE)) {
3359 g_object_unref (mime_parts);
3362 if (mime_parts == NULL || tny_list_get_length (mime_parts) == 0) {
3364 g_object_unref (mime_parts);
3370 g_object_ref (mime_parts);
3373 /* prepare dialog */
3374 if (tny_list_get_length (mime_parts) == 1) {
3376 /* only one attachment selected */
3377 iter = tny_list_create_iterator (mime_parts);
3378 TnyMimePart *mime_part = (TnyMimePart *) tny_iterator_get_current (iter);
3379 g_object_unref (iter);
3380 if (!modest_tny_mime_part_is_msg (mime_part) &&
3381 modest_tny_mime_part_is_attachment_for_modest (mime_part) &&
3382 !tny_mime_part_is_purged (mime_part)) {
3383 filename = g_strdup (tny_mime_part_get_filename (mime_part));
3385 /* TODO: show any error? */
3386 g_warning ("%s: Tried to save a non-file attachment", __FUNCTION__);
3387 g_object_unref (mime_parts);
3390 g_object_unref (mime_part);
3392 gint num = tny_list_get_length (mime_parts);
3393 save_multiple_str = g_strdup_printf (dngettext("hildon-fm",
3394 "sfil_va_number_of_objects_attachment",
3395 "sfil_va_number_of_objects_attachments",
3399 /* Creation of hildon file chooser dialog for saving */
3400 save_dialog = modest_toolkit_factory_create_file_chooser_dialog (modest_runtime_get_toolkit_factory (),
3402 (GtkWindow *) window,
3403 GTK_FILE_CHOOSER_ACTION_SAVE);
3405 /* Get last used folder */
3406 conf_folder = modest_conf_get_string (modest_runtime_get_conf (),
3407 MODEST_CONF_LATEST_SAVE_ATTACHMENT_PATH, NULL);
3409 /* File chooser stops working if we select "file:///" as current folder */
3410 if (conf_folder && g_ascii_strcasecmp (root_folder, conf_folder) != 0) {
3411 g_free (conf_folder);
3415 if (conf_folder && conf_folder[0] != '\0') {
3416 gtk_file_chooser_set_current_folder_uri (GTK_FILE_CHOOSER (save_dialog), conf_folder);
3419 /* Set the default folder to documents folder */
3420 docs_folder = (gchar *) g_strdup(g_get_user_special_dir (G_USER_DIRECTORY_DOCUMENTS));
3423 docs_folder = g_build_filename (g_getenv (MYDOCS_ENV), DOCS_FOLDER, NULL);
3425 gtk_file_chooser_set_current_folder (GTK_FILE_CHOOSER (save_dialog), docs_folder);
3426 g_free (docs_folder);
3428 g_free (conf_folder);
3432 gtk_file_chooser_set_current_name (GTK_FILE_CHOOSER (save_dialog),
3437 /* if multiple, set multiple string */
3438 if (save_multiple_str) {
3439 g_object_set (G_OBJECT (save_dialog), "save-multiple", save_multiple_str, NULL);
3440 gtk_window_set_title (GTK_WINDOW (save_dialog), _FM_SAVE_OBJECT_FILES);
3441 g_free (save_multiple_str);
3444 /* We must run this asynchronously, because the hildon dialog
3445 performs a gtk_dialog_run by itself which leads to gdk
3447 SaveAttachmentsInfo *sa_info;
3448 sa_info = g_slice_new (SaveAttachmentsInfo);
3449 sa_info->attachments_list = mime_parts;
3450 sa_info->window = g_object_ref (window);
3451 g_signal_connect (save_dialog, "response",
3452 G_CALLBACK (save_attachments_response), sa_info);
3454 gtk_widget_show_all (save_dialog);
3458 show_remove_attachment_information (gpointer userdata)
3460 ModestMsgViewWindow *window = (ModestMsgViewWindow *) userdata;
3461 ModestMsgViewWindowPrivate *priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
3463 /* We're outside the main lock */
3464 gdk_threads_enter ();
3466 if (priv->remove_attachment_banner != NULL) {
3467 gtk_widget_destroy (priv->remove_attachment_banner);
3468 g_object_unref (priv->remove_attachment_banner);
3471 priv->remove_attachment_banner = g_object_ref (
3472 modest_platform_animation_banner (NULL, NULL, _("mcen_me_inbox_remove_attachments")));
3474 gdk_threads_leave ();
3480 modest_msg_view_window_remove_attachments (ModestMsgViewWindow *window, gboolean get_all)
3482 ModestMsgViewWindowPrivate *priv;
3483 TnyList *mime_parts = NULL, *tmp;
3484 gchar *confirmation_message;
3490 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window));
3491 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
3493 #ifdef MODEST_TOOLKIT_HILDON2
3494 /* In hildon 2.2 we ignore the get_all flag as we always get all attachments. This is
3495 * because we don't have selection
3497 mime_parts = modest_msg_view_get_attachments (MODEST_MSG_VIEW (priv->msg_view));
3499 /* Remove already purged messages from mime parts list. We use
3500 a copy of the list to remove items in the original one */
3501 tmp = tny_list_copy (mime_parts);
3502 iter = tny_list_create_iterator (tmp);
3503 while (!tny_iterator_is_done (iter)) {
3504 TnyMimePart *part = TNY_MIME_PART (tny_iterator_get_current (iter));
3505 if (tny_mime_part_is_purged (part))
3506 tny_list_remove (mime_parts, (GObject *) part);
3508 g_object_unref (part);
3509 tny_iterator_next (iter);
3511 g_object_unref (tmp);
3512 g_object_unref (iter);
3514 if (!modest_toolkit_utils_select_attachments (GTK_WINDOW (window), mime_parts, TRUE) ||
3515 tny_list_get_length (mime_parts) == 0) {
3516 g_object_unref (mime_parts);
3520 /* In gtk we get only selected attachments for the operation.
3522 mime_parts = modest_msg_view_get_selected_attachments (MODEST_MSG_VIEW (priv->msg_view));
3524 /* Remove already purged messages from mime parts list. We use
3525 a copy of the list to remove items in the original one */
3526 tmp = tny_list_copy (mime_parts);
3527 iter = tny_list_create_iterator (tmp);
3528 while (!tny_iterator_is_done (iter)) {
3529 TnyMimePart *part = TNY_MIME_PART (tny_iterator_get_current (iter));
3530 if (tny_mime_part_is_purged (part))
3531 tny_list_remove (mime_parts, (GObject *) part);
3533 g_object_unref (part);
3534 tny_iterator_next (iter);
3536 g_object_unref (tmp);
3537 g_object_unref (iter);
3539 if (tny_list_get_length (mime_parts) == 0) {
3540 g_object_unref (mime_parts);
3545 n_attachments = tny_list_get_length (mime_parts);
3546 if (n_attachments == 1) {
3550 iter = tny_list_create_iterator (mime_parts);
3551 part = (TnyMimePart *) tny_iterator_get_current (iter);
3552 g_object_unref (iter);
3553 if (modest_tny_mime_part_is_msg (part)) {
3555 header = tny_msg_get_header (TNY_MSG (part));
3556 filename = tny_header_dup_subject (header);
3557 g_object_unref (header);
3558 if (filename == NULL)
3559 filename = g_strdup (_("mail_va_no_subject"));
3561 filename = g_strdup (tny_mime_part_get_filename (TNY_MIME_PART (part)));
3563 confirmation_message = g_strdup_printf (_("mcen_nc_purge_file_text"), filename);
3565 g_object_unref (part);
3567 confirmation_message = g_strdup_printf (ngettext("mcen_nc_purge_file_text",
3568 "mcen_nc_purge_files_text",
3569 n_attachments), n_attachments);
3571 response = modest_platform_run_confirmation_dialog (GTK_WINDOW (gtk_widget_get_toplevel (GTK_WIDGET (window))),
3572 confirmation_message);
3573 g_free (confirmation_message);
3575 if (response != GTK_RESPONSE_OK) {
3576 g_object_unref (mime_parts);
3580 priv->purge_timeout = g_timeout_add (2000, show_remove_attachment_information, window);
3582 iter = tny_list_create_iterator (mime_parts);
3583 while (!tny_iterator_is_done (iter)) {
3586 part = (TnyMimePart *) tny_iterator_get_current (iter);
3587 tny_mime_part_set_purged (TNY_MIME_PART (part));
3588 g_object_unref (part);
3589 tny_iterator_next (iter);
3591 g_object_unref (iter);
3593 msg = tny_msg_view_get_msg (TNY_MSG_VIEW (priv->msg_view));
3594 tny_msg_view_clear (TNY_MSG_VIEW (priv->msg_view));
3595 tny_msg_rewrite_cache (msg);
3596 tny_msg_view_set_msg (TNY_MSG_VIEW (priv->msg_view), msg);
3597 g_object_unref (msg);
3598 update_branding (MODEST_MSG_VIEW_WINDOW (window));
3600 g_object_unref (mime_parts);
3602 if (priv->purge_timeout > 0) {
3603 g_source_remove (priv->purge_timeout);
3604 priv->purge_timeout = 0;
3607 if (priv->remove_attachment_banner) {
3608 gtk_widget_destroy (priv->remove_attachment_banner);
3609 g_object_unref (priv->remove_attachment_banner);
3610 priv->remove_attachment_banner = NULL;
3616 update_window_title (ModestMsgViewWindow *window)
3618 ModestMsgViewWindowPrivate *priv;
3620 TnyHeader *header = NULL;
3621 gchar *subject = NULL;
3623 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
3625 /* Note that if the window is closed while we're retrieving
3626 the message, this widget could de deleted */
3627 if (!priv->msg_view)
3630 msg = tny_msg_view_get_msg (TNY_MSG_VIEW (priv->msg_view));
3632 if (priv->other_body) {
3635 description = modest_tny_mime_part_get_header_value (priv->other_body, "Content-Description");
3637 g_strstrip (description);
3638 subject = description;
3640 } else if (msg != NULL) {
3641 header = tny_msg_get_header (msg);
3642 subject = tny_header_dup_subject (header);
3643 g_object_unref (header);
3644 g_object_unref (msg);
3647 if ((subject == NULL)||(subject[0] == '\0')) {
3649 subject = g_strdup (_("mail_va_no_subject"));
3652 modest_window_set_title (MODEST_WINDOW (window), subject);
3657 on_move_focus (GtkWidget *widget,
3658 GtkDirectionType direction,
3661 g_signal_stop_emission_by_name (G_OBJECT (widget), "move-focus");
3665 fetch_image_open_stream (TnyStreamCache *self, gint64 *expected_size, gchar *uri)
3667 GnomeVFSResult result;
3668 GnomeVFSHandle *handle = NULL;
3669 GnomeVFSFileInfo *info = NULL;
3672 result = gnome_vfs_open (&handle, uri, GNOME_VFS_OPEN_READ);
3673 if (result != GNOME_VFS_OK) {
3678 info = gnome_vfs_file_info_new ();
3679 result = gnome_vfs_get_file_info_from_handle (handle, info, GNOME_VFS_FILE_INFO_DEFAULT);
3680 if (result != GNOME_VFS_OK || ! (info->valid_fields & GNOME_VFS_FILE_INFO_FIELDS_SIZE)) {
3681 /* We put a "safe" default size for going to cache */
3682 *expected_size = (300*1024);
3684 *expected_size = info->size;
3686 gnome_vfs_file_info_unref (info);
3688 stream = tny_vfs_stream_new (handle);
3697 TnyStream *output_stream;
3698 GtkWidget *msg_view;
3703 on_fetch_image_timeout_refresh_view (gpointer userdata)
3705 ModestMsgViewWindowPrivate *priv;
3707 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (userdata);
3708 update_progress_hint (MODEST_MSG_VIEW_WINDOW (userdata));
3709 /* Note that priv->msg_view is set to NULL when this window is
3711 if (priv->msg_view && GTK_WIDGET_DRAWABLE (priv->msg_view)) {
3712 gtk_widget_queue_draw (GTK_WIDGET (priv->msg_view));
3714 priv->fetch_image_redraw_handler = 0;
3715 g_object_unref (userdata);
3720 on_fetch_image_idle_refresh_view (gpointer userdata)
3723 FetchImageData *fidata = (FetchImageData *) userdata;
3725 gdk_threads_enter ();
3726 if (GTK_WIDGET_DRAWABLE (fidata->msg_view)) {
3727 ModestMsgViewWindowPrivate *priv;
3729 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (fidata->window);
3730 priv->fetching_images--;
3731 if (priv->fetch_image_redraw_handler == 0) {
3732 priv->fetch_image_redraw_handler = g_timeout_add (500, on_fetch_image_timeout_refresh_view, g_object_ref (fidata->window));
3736 gdk_threads_leave ();
3738 g_object_unref (fidata->msg_view);
3739 g_object_unref (fidata->window);
3740 g_slice_free (FetchImageData, fidata);
3745 on_fetch_image_thread (gpointer userdata)
3747 FetchImageData *fidata = (FetchImageData *) userdata;
3748 TnyStreamCache *cache;
3749 TnyStream *cache_stream;
3751 cache = modest_runtime_get_images_cache ();
3753 tny_stream_cache_get_stream (cache,
3755 (TnyStreamCacheOpenStreamFetcher) fetch_image_open_stream,
3756 (gpointer) fidata->uri);
3757 g_free (fidata->cache_id);
3758 g_free (fidata->uri);
3760 if (cache_stream != NULL) {
3763 while (G_LIKELY (!tny_stream_is_eos (cache_stream))) {
3766 nb_read = tny_stream_read (cache_stream, buffer, sizeof (buffer));
3767 if (G_UNLIKELY (nb_read < 0)) {
3769 } else if (G_LIKELY (nb_read > 0)) {
3770 gssize nb_written = 0;
3772 while (G_UNLIKELY (nb_written < nb_read)) {
3775 len = tny_stream_write (fidata->output_stream, buffer + nb_written,
3776 nb_read - nb_written);
3777 if (G_UNLIKELY (len < 0))
3783 tny_stream_close (cache_stream);
3784 g_object_unref (cache_stream);
3787 tny_stream_close (fidata->output_stream);
3788 g_object_unref (fidata->output_stream);
3790 g_idle_add (on_fetch_image_idle_refresh_view, fidata);
3796 on_fetch_image (ModestMsgView *msgview,
3799 ModestMsgViewWindow *window)
3801 const gchar *current_account;
3802 ModestMsgViewWindowPrivate *priv;
3803 FetchImageData *fidata;
3805 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
3807 current_account = modest_window_get_active_account (MODEST_WINDOW (window));
3809 fidata = g_slice_new0 (FetchImageData);
3810 fidata->msg_view = g_object_ref (msgview);
3811 fidata->window = g_object_ref (window);
3812 fidata->uri = g_strdup (uri);
3813 fidata->cache_id = modest_images_cache_get_id (current_account, uri);
3814 fidata->output_stream = g_object_ref (stream);
3816 priv->fetching_images++;
3817 if (g_thread_create (on_fetch_image_thread, fidata, FALSE, NULL) == NULL) {
3818 g_object_unref (fidata->output_stream);
3819 g_free (fidata->cache_id);
3820 g_free (fidata->uri);
3821 g_object_unref (fidata->msg_view);
3822 g_slice_free (FetchImageData, fidata);
3823 tny_stream_close (stream);
3824 priv->fetching_images--;
3825 update_progress_hint (window);
3828 update_progress_hint (window);
3834 setup_menu (ModestMsgViewWindow *self)
3836 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW(self));
3838 /* Settings menu buttons */
3839 modest_window_add_to_menu (MODEST_WINDOW (self), _("mcen_me_viewer_find"), NULL,
3840 MODEST_WINDOW_MENU_CALLBACK (modest_msg_view_window_show_isearch_toolbar),
3841 MODEST_DIMMING_CALLBACK (modest_ui_dimming_rules_on_find_in_msg));
3843 modest_window_add_to_menu (MODEST_WINDOW (self),
3844 dngettext(GETTEXT_PACKAGE,
3845 "mcen_me_move_message",
3846 "mcen_me_move_messages",
3849 MODEST_WINDOW_MENU_CALLBACK (modest_ui_actions_on_move_to),
3850 MODEST_DIMMING_CALLBACK (modest_ui_dimming_rules_on_move_to));
3852 modest_window_add_to_menu (MODEST_WINDOW (self), _("mcen_me_inbox_mark_as_read"), NULL,
3853 MODEST_WINDOW_MENU_CALLBACK (modest_ui_actions_on_mark_as_read),
3854 MODEST_DIMMING_CALLBACK (modest_ui_dimming_rules_on_mark_as_read_msg_in_view));
3856 modest_window_add_to_menu (MODEST_WINDOW (self), _("mcen_me_inbox_mark_as_unread"), NULL,
3857 MODEST_WINDOW_MENU_CALLBACK (modest_ui_actions_on_mark_as_unread),
3858 MODEST_DIMMING_CALLBACK (modest_ui_dimming_rules_on_mark_as_unread_msg_in_view));
3860 modest_window_add_to_menu (MODEST_WINDOW (self), _("mcen_me_viewer_save_attachments"), NULL,
3861 MODEST_WINDOW_MENU_CALLBACK (modest_ui_actions_save_attachments),
3862 MODEST_DIMMING_CALLBACK (modest_ui_dimming_rules_on_save_attachments));
3863 modest_window_add_to_menu (MODEST_WINDOW (self), _("mcen_me_inbox_remove_attachments"), NULL,
3864 MODEST_WINDOW_MENU_CALLBACK (modest_ui_actions_remove_attachments),
3865 MODEST_DIMMING_CALLBACK (modest_ui_dimming_rules_on_remove_attachments));
3867 modest_window_add_to_menu (MODEST_WINDOW (self), _("mcen_me_new_message"), "<Control>n",
3868 MODEST_WINDOW_MENU_CALLBACK (modest_ui_actions_on_new_msg),
3869 MODEST_DIMMING_CALLBACK (modest_ui_dimming_rules_on_new_msg));
3870 modest_window_add_to_menu (MODEST_WINDOW (self), _("mcen_me_viewer_addtocontacts"), NULL,
3871 MODEST_WINDOW_MENU_CALLBACK (modest_ui_actions_add_to_contacts),
3872 MODEST_DIMMING_CALLBACK (modest_ui_dimming_rules_on_add_to_contacts));
3874 modest_window_add_to_menu (MODEST_WINDOW (self), _("mcen_ti_message_properties"), NULL,
3875 MODEST_WINDOW_MENU_CALLBACK (modest_ui_actions_on_details),
3876 MODEST_DIMMING_CALLBACK (modest_ui_dimming_rules_on_details));
3880 modest_msg_view_window_add_to_contacts (ModestMsgViewWindow *self)
3882 ModestMsgViewWindowPrivate *priv;
3883 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
3884 GSList *recipients = NULL;
3887 msg = tny_msg_view_get_msg (TNY_MSG_VIEW (priv->msg_view));
3891 header = modest_msg_view_window_get_header (self);
3894 recipients = modest_tny_msg_header_get_all_recipients_list (header);
3895 g_object_unref (header);
3897 recipients = modest_tny_msg_get_all_recipients_list (msg);
3898 g_object_unref (msg);
3902 /* Offer the user to add recipients to the address book */
3903 modest_address_book_add_address_list_with_selector (recipients, (GtkWindow *) self);
3904 g_slist_foreach (recipients, (GFunc) g_free, NULL); g_slist_free (recipients);
3909 _modest_msg_view_window_map_event (GtkWidget *widget,
3913 ModestMsgViewWindow *self = (ModestMsgViewWindow *) userdata;
3915 update_progress_hint (self);
3921 modest_msg_view_window_fetch_images (ModestMsgViewWindow *self)
3923 ModestMsgViewWindowPrivate *priv;
3924 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
3926 modest_msg_view_request_fetch_images (MODEST_MSG_VIEW (priv->msg_view));
3930 modest_msg_view_window_has_blocked_external_images (ModestMsgViewWindow *self)
3932 ModestMsgViewWindowPrivate *priv;
3933 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
3935 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self), FALSE);
3937 return modest_msg_view_has_blocked_external_images (MODEST_MSG_VIEW (priv->msg_view));
3941 modest_msg_view_window_reload (ModestMsgViewWindow *self)
3943 ModestMsgViewWindowPrivate *priv;
3944 const gchar *msg_uid;
3945 TnyHeader *header = NULL;
3946 TnyFolder *folder = NULL;
3948 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self));
3950 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
3952 header = modest_msg_view_window_get_header (MODEST_MSG_VIEW_WINDOW (self));
3956 folder = tny_header_get_folder (header);
3957 g_object_unref (header);
3962 msg_uid = modest_msg_view_window_get_message_uid (self);
3964 GtkTreeRowReference *row_reference;
3966 if (priv->row_reference && gtk_tree_row_reference_valid (priv->row_reference)) {
3967 row_reference = priv->row_reference;
3969 row_reference = NULL;
3971 if (!message_reader (self, priv, NULL, msg_uid, folder, row_reference))
3972 g_warning ("Shouldn't happen, trying to reload a message failed");
3975 g_object_unref (folder);
3979 update_branding (ModestMsgViewWindow *self)
3981 const gchar *account;
3982 const gchar *mailbox;
3983 ModestAccountMgr *mgr;
3984 ModestProtocol *protocol = NULL;
3985 gchar *service_name = NULL;
3986 const GdkPixbuf *service_icon = NULL;
3987 ModestMsgViewWindowPrivate *priv;
3989 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
3991 account = modest_window_get_active_account (MODEST_WINDOW (self));
3992 mailbox = modest_window_get_active_mailbox (MODEST_WINDOW (self));
3994 mgr = modest_runtime_get_account_mgr ();
3996 if (modest_account_mgr_account_is_multimailbox (mgr, account, &protocol)) {
3997 if (MODEST_IS_ACCOUNT_PROTOCOL (protocol)) {
3998 service_name = modest_account_protocol_get_service_name (MODEST_ACCOUNT_PROTOCOL (protocol),
4000 service_icon = modest_account_protocol_get_service_icon (MODEST_ACCOUNT_PROTOCOL (protocol),
4001 account, mailbox, MODEST_ICON_SIZE_SMALL);
4005 modest_msg_view_set_branding (MODEST_MSG_VIEW (priv->msg_view), service_name, service_icon);
4006 g_free (service_name);
4010 sync_flags (ModestMsgViewWindow *self)
4012 TnyHeader *header = NULL;
4014 header = modest_msg_view_window_get_header (self);
4016 TnyMsg *msg = modest_msg_view_window_get_message (self);
4018 header = tny_msg_get_header (msg);
4019 g_object_unref (msg);
4024 TnyFolder *folder = tny_header_get_folder (header);
4027 ModestMailOperation *mail_op;
4029 /* Sync folder, we need this to save the seen flag */
4030 mail_op = modest_mail_operation_new (NULL);
4031 modest_mail_operation_queue_add (modest_runtime_get_mail_operation_queue (),
4033 modest_mail_operation_sync_folder (mail_op, folder, FALSE, NULL, NULL);
4034 g_object_unref (mail_op);
4035 g_object_unref (folder);
4037 g_object_unref (header);