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 #define MYDOCS_ENV "MYDOCSDIR"
75 #define DOCS_FOLDER ".documents"
77 typedef struct _ModestMsgViewWindowPrivate ModestMsgViewWindowPrivate;
78 struct _ModestMsgViewWindowPrivate {
81 GtkWidget *main_scroll;
82 GtkWidget *isearch_toolbar;
85 /* Progress observers */
86 GSList *progress_widgets;
89 GtkWidget *prev_toolitem;
90 GtkWidget *next_toolitem;
91 gboolean progress_hint;
94 /* Optimized view enabled */
95 gboolean optimized_view;
97 /* Whether this was created via the *_new_for_search_result() function. */
98 gboolean is_search_result;
100 /* Whether the message is in outbox */
103 /* A reference to the @model of the header view
104 * to allow selecting previous/next messages,
105 * if the message is currently selected in the header view.
107 const gchar *header_folder_id;
108 GtkTreeModel *header_model;
109 GtkTreeRowReference *row_reference;
110 GtkTreeRowReference *next_row_reference;
112 gulong clipboard_change_handler;
113 gulong queue_change_handler;
114 gulong account_removed_handler;
115 gulong row_changed_handler;
116 gulong row_deleted_handler;
117 gulong row_inserted_handler;
118 gulong rows_reordered_handler;
121 GtkWidget *remove_attachment_banner;
124 TnyMimePart *other_body;
129 static void modest_msg_view_window_class_init (ModestMsgViewWindowClass *klass);
130 static void modest_msg_view_window_init (ModestMsgViewWindow *obj);
131 static void modest_header_view_observer_init (ModestHeaderViewObserverIface *iface_class);
132 static void modest_msg_view_window_finalize (GObject *obj);
133 static void modest_msg_view_window_show_isearch_toolbar (GtkWidget *obj, gpointer data);
134 static void modest_msg_view_window_isearch_toolbar_close (GtkWidget *widget,
135 ModestMsgViewWindow *obj);
136 static void modest_msg_view_window_isearch_toolbar_search (GtkWidget *widget,
137 ModestMsgViewWindow *obj);
138 static void modest_msg_view_window_toggle_isearch_toolbar (GtkWidget *obj,
140 static void modest_msg_view_window_disconnect_signals (ModestWindow *self);
142 static gdouble modest_msg_view_window_get_zoom (ModestWindow *window);
143 static void modest_msg_view_window_set_zoom (ModestWindow *window,
145 static gboolean modest_msg_view_window_zoom_minus (ModestWindow *window);
146 static gboolean modest_msg_view_window_zoom_plus (ModestWindow *window);
147 static gboolean modest_msg_view_window_key_event (GtkWidget *window,
150 static void modest_msg_view_window_update_priority (ModestMsgViewWindow *window);
152 static void modest_msg_view_window_show_toolbar (ModestWindow *window,
153 gboolean show_toolbar);
155 static void modest_msg_view_window_clipboard_owner_change (GtkClipboard *clipboard,
157 ModestMsgViewWindow *window);
159 static void modest_msg_view_window_on_row_changed (GtkTreeModel *header_model,
162 ModestMsgViewWindow *window);
164 static void modest_msg_view_window_on_row_deleted (GtkTreeModel *header_model,
166 ModestMsgViewWindow *window);
168 static void modest_msg_view_window_on_row_inserted (GtkTreeModel *header_model,
169 GtkTreePath *tree_path,
170 GtkTreeIter *tree_iter,
171 ModestMsgViewWindow *window);
173 static void modest_msg_view_window_on_row_reordered (GtkTreeModel *header_model,
177 ModestMsgViewWindow *window);
179 static void modest_msg_view_window_update_model_replaced (ModestHeaderViewObserver *window,
181 const gchar *tny_folder_id);
183 static void on_queue_changed (ModestMailOperationQueue *queue,
184 ModestMailOperation *mail_op,
185 ModestMailOperationQueueNotification type,
186 ModestMsgViewWindow *self);
188 static void on_account_removed (TnyAccountStore *account_store,
192 static void on_move_focus (GtkWidget *widget,
193 GtkDirectionType direction,
196 static void view_msg_cb (ModestMailOperation *mail_op,
203 static void set_progress_hint (ModestMsgViewWindow *self,
206 static void update_window_title (ModestMsgViewWindow *window);
208 static void init_window (ModestMsgViewWindow *obj);
210 static gboolean msg_is_visible (TnyHeader *header, gboolean check_outbox);
212 static void check_dimming_rules_after_change (ModestMsgViewWindow *window);
214 static gboolean on_fetch_image (ModestMsgView *msgview,
217 ModestMsgViewWindow *window);
219 static gboolean modest_msg_view_window_scroll_child (ModestMsgViewWindow *self,
220 GtkScrollType scroll_type,
223 static gboolean message_reader (ModestMsgViewWindow *window,
224 ModestMsgViewWindowPrivate *priv,
226 const gchar *msg_uid,
228 GtkTreeRowReference *row_reference);
230 static void setup_menu (ModestMsgViewWindow *self);
231 static gboolean _modest_msg_view_window_map_event (GtkWidget *widget,
234 static void update_branding (ModestMsgViewWindow *self);
235 static void sync_flags (ModestMsgViewWindow *self);
237 /* list my signals */
244 static const GtkActionEntry msg_view_toolbar_action_entries [] = {
247 { "ToolbarMessageReply", MODEST_STOCK_REPLY, N_("mcen_me_inbox_reply"), "<CTRL>R", NULL, G_CALLBACK (modest_ui_actions_on_reply) },
248 { "ToolbarMessageReplyAll", MODEST_STOCK_REPLY_ALL, N_("mcen_me_inbox_replytoall"), NULL, NULL, G_CALLBACK (modest_ui_actions_on_reply_all) },
249 { "ToolbarMessageForward", MODEST_STOCK_FORWARD, N_("mcen_me_inbox_forward"), NULL, NULL, G_CALLBACK (modest_ui_actions_on_forward) },
250 { "ToolbarDeleteMessage", MODEST_STOCK_DELETE, N_("qgn_toolb_gene_deletebutton"), NULL, NULL, G_CALLBACK (modest_ui_actions_on_delete_message_or_folder) },
251 { "ToolbarMessageBack", MODEST_TOOLBAR_ICON_PREV, N_("qgn_toolb_gene_back"), NULL, NULL, G_CALLBACK (modest_ui_actions_on_prev) },
252 { "ToolbarMessageNext", MODEST_TOOLBAR_ICON_NEXT, N_("qgn_toolb_gene_forward"), NULL, NULL, G_CALLBACK (modest_ui_actions_on_next) },
253 { "ToolbarDownloadExternalImages", MODEST_TOOLBAR_ICON_DOWNLOAD_IMAGES, N_("mail_bd_external_images"), NULL, NULL, G_CALLBACK (modest_ui_actions_on_fetch_images) },
256 static const GtkToggleActionEntry msg_view_toggle_action_entries [] = {
257 { "FindInMessage", MODEST_TOOLBAR_ICON_FIND, N_("qgn_toolb_gene_find"), "<CTRL>F", NULL, G_CALLBACK (modest_msg_view_window_toggle_isearch_toolbar), FALSE },
260 #define MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE((o), \
261 MODEST_TYPE_MSG_VIEW_WINDOW, \
262 ModestMsgViewWindowPrivate))
264 static GtkWindowClass *parent_class = NULL;
266 /* uncomment the following if you have defined any signals */
267 static guint signals[LAST_SIGNAL] = {0};
270 modest_msg_view_window_get_type (void)
272 static GType my_type = 0;
274 static const GTypeInfo my_info = {
275 sizeof(ModestMsgViewWindowClass),
276 NULL, /* base init */
277 NULL, /* base finalize */
278 (GClassInitFunc) modest_msg_view_window_class_init,
279 NULL, /* class finalize */
280 NULL, /* class data */
281 sizeof(ModestMsgViewWindow),
283 (GInstanceInitFunc) modest_msg_view_window_init,
286 #ifdef MODEST_TOOLKIT_GTK
287 my_type = g_type_register_static (MODEST_TYPE_WINDOW,
288 "ModestMsgViewWindow",
291 my_type = g_type_register_static (MODEST_TYPE_HILDON2_WINDOW,
292 "ModestMsgViewWindow",
296 static const GInterfaceInfo modest_header_view_observer_info =
298 (GInterfaceInitFunc) modest_header_view_observer_init,
299 NULL, /* interface_finalize */
300 NULL /* interface_data */
303 g_type_add_interface_static (my_type,
304 MODEST_TYPE_HEADER_VIEW_OBSERVER,
305 &modest_header_view_observer_info);
311 save_state (ModestWindow *self)
313 modest_widget_memory_save (modest_runtime_get_conf (),
315 MODEST_CONF_MSG_VIEW_WINDOW_KEY);
319 modest_msg_view_window_scroll_child (ModestMsgViewWindow *self,
320 GtkScrollType scroll_type,
324 ModestMsgViewWindowPrivate *priv;
327 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(self);
329 switch (scroll_type) {
330 case GTK_SCROLL_STEP_UP:
333 case GTK_SCROLL_STEP_DOWN:
336 case GTK_SCROLL_PAGE_UP:
339 case GTK_SCROLL_PAGE_DOWN:
342 case GTK_SCROLL_START:
353 modest_scrollable_scroll ((ModestScrollable *) priv->main_scroll, 0, step);
355 return (gboolean) step;
359 add_scroll_binding (GtkBindingSet *binding_set,
361 GtkScrollType scroll)
363 guint keypad_keyval = keyval - GDK_Left + GDK_KP_Left;
365 gtk_binding_entry_add_signal (binding_set, keyval, 0,
367 GTK_TYPE_SCROLL_TYPE, scroll,
368 G_TYPE_BOOLEAN, FALSE);
369 gtk_binding_entry_add_signal (binding_set, keypad_keyval, 0,
371 GTK_TYPE_SCROLL_TYPE, scroll,
372 G_TYPE_BOOLEAN, FALSE);
376 modest_msg_view_window_class_init (ModestMsgViewWindowClass *klass)
378 GObjectClass *gobject_class;
379 ModestWindowClass *modest_window_class;
380 GtkBindingSet *binding_set;
382 gobject_class = (GObjectClass*) klass;
383 modest_window_class = (ModestWindowClass *) klass;
385 parent_class = g_type_class_peek_parent (klass);
386 gobject_class->finalize = modest_msg_view_window_finalize;
388 modest_window_class->set_zoom_func = modest_msg_view_window_set_zoom;
389 modest_window_class->get_zoom_func = modest_msg_view_window_get_zoom;
390 modest_window_class->zoom_plus_func = modest_msg_view_window_zoom_plus;
391 modest_window_class->zoom_minus_func = modest_msg_view_window_zoom_minus;
392 modest_window_class->show_toolbar_func = modest_msg_view_window_show_toolbar;
393 modest_window_class->disconnect_signals_func = modest_msg_view_window_disconnect_signals;
395 modest_window_class->save_state_func = save_state;
397 klass->scroll_child = modest_msg_view_window_scroll_child;
399 signals[MSG_CHANGED_SIGNAL] =
400 g_signal_new ("msg-changed",
401 G_TYPE_FROM_CLASS (gobject_class),
403 G_STRUCT_OFFSET (ModestMsgViewWindowClass, msg_changed),
405 modest_marshal_VOID__POINTER_POINTER,
406 G_TYPE_NONE, 2, G_TYPE_POINTER, G_TYPE_POINTER);
408 signals[SCROLL_CHILD_SIGNAL] =
409 g_signal_new ("scroll-child",
410 G_TYPE_FROM_CLASS (gobject_class),
411 G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
412 G_STRUCT_OFFSET (ModestMsgViewWindowClass, scroll_child),
414 modest_marshal_BOOLEAN__ENUM_BOOLEAN,
415 G_TYPE_BOOLEAN, 2, GTK_TYPE_SCROLL_TYPE, G_TYPE_BOOLEAN);
417 binding_set = gtk_binding_set_by_class (klass);
418 add_scroll_binding (binding_set, GDK_Up, GTK_SCROLL_STEP_UP);
419 add_scroll_binding (binding_set, GDK_Down, GTK_SCROLL_STEP_DOWN);
420 add_scroll_binding (binding_set, GDK_Page_Up, GTK_SCROLL_PAGE_UP);
421 add_scroll_binding (binding_set, GDK_Page_Down, GTK_SCROLL_PAGE_DOWN);
422 add_scroll_binding (binding_set, GDK_Home, GTK_SCROLL_START);
423 add_scroll_binding (binding_set, GDK_End, GTK_SCROLL_END);
425 g_type_class_add_private (gobject_class, sizeof(ModestMsgViewWindowPrivate));
429 static void modest_header_view_observer_init(
430 ModestHeaderViewObserverIface *iface_class)
432 iface_class->update_func = modest_msg_view_window_update_model_replaced;
436 modest_msg_view_window_init (ModestMsgViewWindow *obj)
438 ModestMsgViewWindowPrivate *priv;
439 ModestWindowPrivate *parent_priv = NULL;
440 GtkActionGroup *action_group = NULL;
441 GError *error = NULL;
443 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(obj);
444 parent_priv = MODEST_WINDOW_GET_PRIVATE(obj);
445 parent_priv->ui_manager = gtk_ui_manager_new();
447 action_group = gtk_action_group_new ("ModestMsgViewWindowActions");
448 gtk_action_group_set_translation_domain (action_group, GETTEXT_PACKAGE);
450 /* Add common actions */
451 gtk_action_group_add_actions (action_group,
452 msg_view_toolbar_action_entries,
453 G_N_ELEMENTS (msg_view_toolbar_action_entries),
455 gtk_action_group_add_toggle_actions (action_group,
456 msg_view_toggle_action_entries,
457 G_N_ELEMENTS (msg_view_toggle_action_entries),
460 gtk_ui_manager_insert_action_group (parent_priv->ui_manager, action_group, 0);
461 g_object_unref (action_group);
463 /* Load the UI definition */
464 gtk_ui_manager_add_ui_from_file (parent_priv->ui_manager, MODEST_UIDIR "modest-msg-view-window-ui.xml",
467 g_printerr ("modest: could not merge modest-msg-view-window-ui.xml: %s\n", error->message);
468 g_error_free (error);
473 /* Add accelerators */
474 gtk_window_add_accel_group (GTK_WINDOW (obj),
475 gtk_ui_manager_get_accel_group (parent_priv->ui_manager));
477 priv->is_search_result = FALSE;
478 priv->is_outbox = FALSE;
480 priv->msg_view = NULL;
481 priv->header_model = NULL;
482 priv->header_folder_id = NULL;
483 priv->clipboard_change_handler = 0;
484 priv->queue_change_handler = 0;
485 priv->account_removed_handler = 0;
486 priv->row_changed_handler = 0;
487 priv->row_deleted_handler = 0;
488 priv->row_inserted_handler = 0;
489 priv->rows_reordered_handler = 0;
490 priv->progress_hint = FALSE;
491 priv->fetching_images = 0;
493 priv->optimized_view = FALSE;
494 priv->purge_timeout = 0;
495 priv->remove_attachment_banner = NULL;
496 priv->msg_uid = NULL;
497 priv->other_body = NULL;
499 priv->sighandlers = NULL;
502 init_window (MODEST_MSG_VIEW_WINDOW(obj));
507 update_progress_hint (ModestMsgViewWindow *self)
509 ModestMsgViewWindowPrivate *priv;
510 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(self);
512 if (GTK_WIDGET_VISIBLE (self)) {
513 modest_window_show_progress (MODEST_WINDOW (self),
514 (priv->progress_hint || (priv->fetching_images > 0))?1:0);
519 set_progress_hint (ModestMsgViewWindow *self,
522 ModestWindowPrivate *parent_priv;
523 ModestMsgViewWindowPrivate *priv;
525 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self));
527 parent_priv = MODEST_WINDOW_GET_PRIVATE(self);
528 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(self);
530 /* Sets current progress hint */
531 priv->progress_hint = enabled;
533 update_progress_hint (self);
539 init_window (ModestMsgViewWindow *obj)
541 GtkWidget *main_vbox;
542 ModestMsgViewWindowPrivate *priv;
544 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(obj);
546 priv->msg_view = GTK_WIDGET (tny_platform_factory_new_msg_view (modest_tny_platform_factory_get_instance ()));
547 modest_msg_view_set_shadow_type (MODEST_MSG_VIEW (priv->msg_view), GTK_SHADOW_NONE);
548 main_vbox = gtk_vbox_new (FALSE, 6);
550 priv->main_scroll = modest_toolkit_factory_create_scrollable (modest_runtime_get_toolkit_factory ());
551 modest_scrollable_set_horizontal_policy (MODEST_SCROLLABLE (priv->main_scroll), GTK_POLICY_AUTOMATIC);
552 g_object_set (G_OBJECT (priv->main_scroll),
553 "movement-mode", MODEST_MOVEMENT_MODE_BOTH,
554 "horizontal-max-overshoot", 0,
556 gtk_container_add (GTK_CONTAINER (priv->main_scroll), priv->msg_view);
557 gtk_box_pack_start (GTK_BOX(main_vbox), priv->main_scroll, TRUE, TRUE, 0);
558 gtk_container_add (GTK_CONTAINER(obj), main_vbox);
560 /* NULL-ize fields if the window is destroyed */
561 g_signal_connect (priv->msg_view, "destroy", G_CALLBACK (gtk_widget_destroyed), &(priv->msg_view));
563 gtk_widget_show_all (GTK_WIDGET(main_vbox));
567 modest_msg_view_window_disconnect_signals (ModestWindow *self)
569 ModestMsgViewWindowPrivate *priv;
570 GtkWidget *header_view = NULL;
571 GtkWindow *parent_window = NULL;
573 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
575 if (gtk_clipboard_get (GDK_SELECTION_PRIMARY) &&
576 g_signal_handler_is_connected (gtk_clipboard_get (GDK_SELECTION_PRIMARY),
577 priv->clipboard_change_handler))
578 g_signal_handler_disconnect (gtk_clipboard_get (GDK_SELECTION_PRIMARY),
579 priv->clipboard_change_handler);
581 if (g_signal_handler_is_connected (G_OBJECT (modest_runtime_get_mail_operation_queue ()),
582 priv->queue_change_handler))
583 g_signal_handler_disconnect (G_OBJECT (modest_runtime_get_mail_operation_queue ()),
584 priv->queue_change_handler);
586 if (g_signal_handler_is_connected (G_OBJECT (modest_runtime_get_account_store ()),
587 priv->account_removed_handler))
588 g_signal_handler_disconnect (G_OBJECT (modest_runtime_get_account_store ()),
589 priv->account_removed_handler);
591 if (priv->header_model) {
592 if (g_signal_handler_is_connected(G_OBJECT (priv->header_model),
593 priv->row_changed_handler))
594 g_signal_handler_disconnect(G_OBJECT (priv->header_model),
595 priv->row_changed_handler);
597 if (g_signal_handler_is_connected(G_OBJECT (priv->header_model),
598 priv->row_deleted_handler))
599 g_signal_handler_disconnect(G_OBJECT (priv->header_model),
600 priv->row_deleted_handler);
602 if (g_signal_handler_is_connected(G_OBJECT (priv->header_model),
603 priv->row_inserted_handler))
604 g_signal_handler_disconnect(G_OBJECT (priv->header_model),
605 priv->row_inserted_handler);
607 if (g_signal_handler_is_connected(G_OBJECT (priv->header_model),
608 priv->rows_reordered_handler))
609 g_signal_handler_disconnect(G_OBJECT (priv->header_model),
610 priv->rows_reordered_handler);
613 modest_signal_mgr_disconnect_all_and_destroy (priv->sighandlers);
614 priv->sighandlers = NULL;
616 parent_window = gtk_window_get_transient_for (GTK_WINDOW (self));
617 if (parent_window && MODEST_IS_HEADER_WINDOW (parent_window)) {
618 header_view = GTK_WIDGET (modest_header_window_get_header_view (MODEST_HEADER_WINDOW (parent_window)));
620 modest_header_view_remove_observer(MODEST_HEADER_VIEW (header_view),
621 MODEST_HEADER_VIEW_OBSERVER(self));
627 modest_msg_view_window_finalize (GObject *obj)
629 ModestMsgViewWindowPrivate *priv;
631 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (obj);
633 /* Sanity check: shouldn't be needed, the window mgr should
634 call this function before */
635 modest_msg_view_window_disconnect_signals (MODEST_WINDOW (obj));
637 if (priv->other_body != NULL) {
638 g_object_unref (priv->other_body);
639 priv->other_body = NULL;
642 if (priv->header_model != NULL) {
643 g_object_unref (priv->header_model);
644 priv->header_model = NULL;
647 if (priv->remove_attachment_banner) {
648 gtk_widget_destroy (priv->remove_attachment_banner);
649 g_object_unref (priv->remove_attachment_banner);
650 priv->remove_attachment_banner = NULL;
653 if (priv->purge_timeout > 0) {
654 g_source_remove (priv->purge_timeout);
655 priv->purge_timeout = 0;
658 if (priv->row_reference) {
659 gtk_tree_row_reference_free (priv->row_reference);
660 priv->row_reference = NULL;
663 if (priv->next_row_reference) {
664 gtk_tree_row_reference_free (priv->next_row_reference);
665 priv->next_row_reference = NULL;
669 g_free (priv->msg_uid);
670 priv->msg_uid = NULL;
673 G_OBJECT_CLASS(parent_class)->finalize (obj);
677 select_next_valid_row (GtkTreeModel *model,
678 GtkTreeRowReference **row_reference,
682 GtkTreeIter tmp_iter;
684 GtkTreePath *next = NULL;
685 gboolean retval = FALSE, finished;
687 g_return_val_if_fail (gtk_tree_row_reference_valid (*row_reference), FALSE);
689 path = gtk_tree_row_reference_get_path (*row_reference);
690 gtk_tree_model_get_iter (model, &tmp_iter, path);
691 gtk_tree_row_reference_free (*row_reference);
692 *row_reference = NULL;
696 TnyHeader *header = NULL;
698 if (gtk_tree_model_iter_next (model, &tmp_iter)) {
699 gtk_tree_model_get (model, &tmp_iter,
700 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
704 if (msg_is_visible (header, is_outbox)) {
705 next = gtk_tree_model_get_path (model, &tmp_iter);
706 *row_reference = gtk_tree_row_reference_new (model, next);
707 gtk_tree_path_free (next);
711 g_object_unref (header);
714 } else if (cycle && gtk_tree_model_get_iter_first (model, &tmp_iter)) {
715 next = gtk_tree_model_get_path (model, &tmp_iter);
717 /* Ensure that we are not selecting the same */
718 if (gtk_tree_path_compare (path, next) != 0) {
719 gtk_tree_model_get (model, &tmp_iter,
720 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
723 if (msg_is_visible (header, is_outbox)) {
724 *row_reference = gtk_tree_row_reference_new (model, next);
728 g_object_unref (header);
732 /* If we ended up in the same message
733 then there is no valid next
737 gtk_tree_path_free (next);
739 /* If there are no more messages and we don't
740 want to start again in the first one then
741 there is no valid next message */
747 gtk_tree_path_free (path);
752 /* TODO: This should be in _init(), with the parameters as properties. */
754 modest_msg_view_window_construct (ModestMsgViewWindow *self,
755 const gchar *modest_account_name,
756 const gchar *mailbox,
757 const gchar *msg_uid)
760 ModestMsgViewWindowPrivate *priv = NULL;
761 ModestWindowPrivate *parent_priv = NULL;
762 ModestDimmingRulesGroup *toolbar_rules_group = NULL;
763 ModestDimmingRulesGroup *clipboard_rules_group = NULL;
765 obj = G_OBJECT (self);
766 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(obj);
767 parent_priv = MODEST_WINDOW_GET_PRIVATE(obj);
769 priv->msg_uid = g_strdup (msg_uid);
772 parent_priv->menubar = NULL;
774 toolbar_rules_group = modest_dimming_rules_group_new (MODEST_DIMMING_RULES_TOOLBAR, TRUE);
775 clipboard_rules_group = modest_dimming_rules_group_new (MODEST_DIMMING_RULES_CLIPBOARD, FALSE);
778 /* Add common dimming rules */
779 modest_dimming_rules_group_add_rules (toolbar_rules_group,
780 modest_msg_view_toolbar_dimming_entries,
781 G_N_ELEMENTS (modest_msg_view_toolbar_dimming_entries),
782 MODEST_WINDOW (self));
783 modest_dimming_rules_group_add_rules (clipboard_rules_group,
784 modest_msg_view_clipboard_dimming_entries,
785 G_N_ELEMENTS (modest_msg_view_clipboard_dimming_entries),
786 MODEST_WINDOW (self));
788 /* Insert dimming rules group for this window */
789 modest_ui_dimming_manager_insert_rules_group (parent_priv->ui_dimming_manager, toolbar_rules_group);
790 modest_ui_dimming_manager_insert_rules_group (parent_priv->ui_dimming_manager, clipboard_rules_group);
791 g_object_unref (toolbar_rules_group);
792 g_object_unref (clipboard_rules_group);
794 /* g_signal_connect (G_OBJECT(obj), "delete-event", G_CALLBACK(on_delete_event), obj); */
796 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);
797 g_signal_connect (G_OBJECT(priv->msg_view), "activate_link",
798 G_CALLBACK (modest_ui_actions_on_msg_link_clicked), obj);
799 g_signal_connect (G_OBJECT(priv->msg_view), "link_hover",
800 G_CALLBACK (modest_ui_actions_on_msg_link_hover), obj);
801 g_signal_connect (G_OBJECT(priv->msg_view), "attachment_clicked",
802 G_CALLBACK (modest_ui_actions_on_msg_attachment_clicked), obj);
803 g_signal_connect (G_OBJECT(priv->msg_view), "recpt_activated",
804 G_CALLBACK (modest_ui_actions_on_msg_recpt_activated), obj);
805 g_signal_connect (G_OBJECT(priv->msg_view), "show_details",
806 G_CALLBACK (modest_ui_actions_on_details), obj);
807 g_signal_connect (G_OBJECT(priv->msg_view), "link_contextual",
808 G_CALLBACK (modest_ui_actions_on_msg_link_contextual), obj);
809 g_signal_connect (G_OBJECT(priv->msg_view), "limit_error",
810 G_CALLBACK (modest_ui_actions_on_limit_error), obj);
811 g_signal_connect (G_OBJECT (priv->msg_view), "fetch_image",
812 G_CALLBACK (on_fetch_image), obj);
814 g_signal_connect (G_OBJECT (obj), "key-release-event",
815 G_CALLBACK (modest_msg_view_window_key_event),
818 g_signal_connect (G_OBJECT (obj), "key-press-event",
819 G_CALLBACK (modest_msg_view_window_key_event),
822 g_signal_connect (G_OBJECT (obj), "move-focus",
823 G_CALLBACK (on_move_focus), obj);
825 g_signal_connect (G_OBJECT (obj), "map-event",
826 G_CALLBACK (_modest_msg_view_window_map_event),
829 /* Mail Operation Queue */
830 priv->queue_change_handler = g_signal_connect (G_OBJECT (modest_runtime_get_mail_operation_queue ()),
832 G_CALLBACK (on_queue_changed),
835 /* Account manager */
836 priv->account_removed_handler = g_signal_connect (G_OBJECT (modest_runtime_get_account_store ()),
838 G_CALLBACK(on_account_removed),
841 modest_window_set_active_account (MODEST_WINDOW(obj), modest_account_name);
842 modest_window_set_active_mailbox (MODEST_WINDOW(obj), mailbox);
844 /* First add out toolbar ... */
845 modest_msg_view_window_show_toolbar (MODEST_WINDOW (obj), TRUE);
847 priv->isearch_toolbar = modest_toolkit_factory_create_isearch_toolbar (modest_runtime_get_toolkit_factory (),
849 modest_window_add_toolbar (MODEST_WINDOW (obj), GTK_TOOLBAR (priv->isearch_toolbar));
850 gtk_widget_set_no_show_all (priv->isearch_toolbar, TRUE);
851 g_signal_connect (G_OBJECT (priv->isearch_toolbar), "isearch-close",
852 G_CALLBACK (modest_msg_view_window_isearch_toolbar_close), obj);
853 g_signal_connect (G_OBJECT (priv->isearch_toolbar), "isearch-search",
854 G_CALLBACK (modest_msg_view_window_isearch_toolbar_search), obj);
855 priv->last_search = NULL;
857 /* Init the clipboard actions dim status */
858 modest_msg_view_grab_focus(MODEST_MSG_VIEW (priv->msg_view));
860 update_window_title (MODEST_MSG_VIEW_WINDOW (obj));
865 /* FIXME: parameter checks */
867 modest_msg_view_window_new_with_header_model (TnyMsg *msg,
868 const gchar *modest_account_name,
869 const gchar *mailbox,
870 const gchar *msg_uid,
872 GtkTreeRowReference *row_reference)
874 ModestMsgViewWindow *window = NULL;
875 ModestMsgViewWindowPrivate *priv = NULL;
876 TnyFolder *header_folder = NULL;
877 ModestHeaderView *header_view = NULL;
878 ModestWindowMgr *mgr = NULL;
881 modest_tny_mime_part_to_string (TNY_MIME_PART (msg), 0);
884 mgr = modest_runtime_get_window_mgr ();
885 window = MODEST_MSG_VIEW_WINDOW (modest_window_mgr_get_msg_view_window (mgr));
886 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), NULL);
888 modest_msg_view_window_construct (window, modest_account_name, mailbox, msg_uid);
890 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
892 /* Remember the message list's TreeModel so we can detect changes
893 * and change the list selection when necessary: */
894 header_folder = modest_header_view_get_folder (header_view);
896 priv->is_outbox = (modest_tny_folder_guess_folder_type (header_folder) ==
897 TNY_FOLDER_TYPE_OUTBOX);
898 priv->header_folder_id = tny_folder_get_id (header_folder);
899 g_object_unref(header_folder);
902 /* Setup row references and connect signals */
903 priv->header_model = g_object_ref (model);
905 if (row_reference && gtk_tree_row_reference_valid (row_reference)) {
906 priv->row_reference = gtk_tree_row_reference_copy (row_reference);
907 priv->next_row_reference = gtk_tree_row_reference_copy (row_reference);
908 select_next_valid_row (model, &(priv->next_row_reference), TRUE, priv->is_outbox);
910 priv->row_reference = NULL;
911 priv->next_row_reference = NULL;
914 /* Connect signals */
915 priv->row_changed_handler =
916 g_signal_connect (GTK_TREE_MODEL(model), "row-changed",
917 G_CALLBACK(modest_msg_view_window_on_row_changed),
919 priv->row_deleted_handler =
920 g_signal_connect (GTK_TREE_MODEL(model), "row-deleted",
921 G_CALLBACK(modest_msg_view_window_on_row_deleted),
923 priv->row_inserted_handler =
924 g_signal_connect (GTK_TREE_MODEL(model), "row-inserted",
925 G_CALLBACK(modest_msg_view_window_on_row_inserted),
927 priv->rows_reordered_handler =
928 g_signal_connect(GTK_TREE_MODEL(model), "rows-reordered",
929 G_CALLBACK(modest_msg_view_window_on_row_reordered),
932 if (header_view != NULL){
933 modest_header_view_add_observer(header_view,
934 MODEST_HEADER_VIEW_OBSERVER(window));
937 tny_msg_view_set_msg (TNY_MSG_VIEW (priv->msg_view), msg);
938 update_window_title (MODEST_MSG_VIEW_WINDOW (window));
939 update_branding (MODEST_MSG_VIEW_WINDOW (window));
941 /* gtk_widget_show_all (GTK_WIDGET (window)); */
942 modest_msg_view_window_update_priority (window);
943 /* Check dimming rules */
944 modest_ui_actions_check_toolbar_dimming_rules (MODEST_WINDOW (window));
945 modest_ui_actions_check_menu_dimming_rules (MODEST_WINDOW (window));
946 modest_window_check_dimming_rules_group (MODEST_WINDOW (window), MODEST_DIMMING_RULES_CLIPBOARD);
948 return MODEST_WINDOW(window);
952 modest_msg_view_window_new_from_uid (const gchar *modest_account_name,
953 const gchar *mailbox,
954 const gchar *msg_uid)
956 ModestMsgViewWindow *window = NULL;
957 ModestMsgViewWindowPrivate *priv = NULL;
958 ModestWindowMgr *mgr = NULL;
960 TnyAccount *account = NULL;
962 mgr = modest_runtime_get_window_mgr ();
963 window = MODEST_MSG_VIEW_WINDOW (modest_window_mgr_get_msg_view_window (mgr));
964 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), NULL);
966 modest_msg_view_window_construct (window, modest_account_name, mailbox, msg_uid);
968 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
970 is_merge = g_str_has_prefix (msg_uid, "merge:");
972 /* Get the account */
974 account = tny_account_store_find_account (TNY_ACCOUNT_STORE (modest_runtime_get_account_store ()),
977 if (is_merge || account) {
978 TnyFolder *folder = NULL;
980 /* Try to get the message, if it's already downloaded
981 we don't need to connect */
983 folder = modest_tny_folder_store_find_folder_from_uri (TNY_FOLDER_STORE (account), msg_uid);
985 ModestTnyAccountStore *account_store;
986 ModestTnyLocalFoldersAccount *local_folders_account;
988 account_store = modest_runtime_get_account_store ();
989 local_folders_account = MODEST_TNY_LOCAL_FOLDERS_ACCOUNT (
990 modest_tny_account_store_get_local_folders_account (account_store));
991 folder = modest_tny_local_folders_account_get_merged_outbox (local_folders_account);
992 g_object_unref (local_folders_account);
996 gboolean device_online;
998 device = modest_runtime_get_device();
999 device_online = tny_device_is_online (device);
1000 if (device_online) {
1001 message_reader (window, priv, NULL, msg_uid, folder, NULL);
1003 TnyMsg *msg = tny_folder_find_msg (folder, msg_uid, NULL);
1005 tny_msg_view_set_msg (TNY_MSG_VIEW (priv->msg_view), msg);
1006 update_window_title (MODEST_MSG_VIEW_WINDOW (window));
1007 update_branding (MODEST_MSG_VIEW_WINDOW (window));
1008 g_object_unref (msg);
1009 /* Sync flags to server */
1010 sync_flags (MODEST_MSG_VIEW_WINDOW (window));
1012 message_reader (window, priv, NULL, msg_uid, folder, NULL);
1015 g_object_unref (folder);
1020 /* Check dimming rules */
1021 modest_ui_actions_check_toolbar_dimming_rules (MODEST_WINDOW (window));
1022 modest_ui_actions_check_menu_dimming_rules (MODEST_WINDOW (window));
1023 modest_window_check_dimming_rules_group (MODEST_WINDOW (window), MODEST_DIMMING_RULES_CLIPBOARD);
1025 return MODEST_WINDOW(window);
1029 modest_msg_view_window_new_from_header_view (ModestHeaderView *header_view,
1030 const gchar *modest_account_name,
1031 const gchar *mailbox,
1032 const gchar *msg_uid,
1033 GtkTreeRowReference *row_reference)
1035 ModestMsgViewWindow *window = NULL;
1036 ModestMsgViewWindowPrivate *priv = NULL;
1037 TnyFolder *header_folder = NULL;
1038 ModestWindowMgr *mgr = NULL;
1042 mgr = modest_runtime_get_window_mgr ();
1043 window = MODEST_MSG_VIEW_WINDOW (modest_window_mgr_get_msg_view_window (mgr));
1044 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), NULL);
1046 modest_msg_view_window_construct (window, modest_account_name, mailbox, msg_uid);
1048 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1050 /* Remember the message list's TreeModel so we can detect changes
1051 * and change the list selection when necessary: */
1053 if (header_view != NULL){
1054 header_folder = modest_header_view_get_folder(header_view);
1055 /* This could happen if the header folder was
1056 unseleted before opening this msg window (for
1057 example if the user selects an account in the
1058 folder view of the main window */
1059 if (header_folder) {
1060 priv->is_outbox = (modest_tny_folder_guess_folder_type (header_folder) ==
1061 TNY_FOLDER_TYPE_OUTBOX);
1062 priv->header_folder_id = tny_folder_get_id(header_folder);
1063 g_object_unref(header_folder);
1067 /* Setup row references and connect signals */
1068 priv->header_model = gtk_tree_view_get_model (GTK_TREE_VIEW (header_view));
1069 g_object_ref (priv->header_model);
1071 if (row_reference && gtk_tree_row_reference_valid (row_reference)) {
1072 priv->row_reference = gtk_tree_row_reference_copy (row_reference);
1073 priv->next_row_reference = gtk_tree_row_reference_copy (row_reference);
1074 select_next_valid_row (priv->header_model, &(priv->next_row_reference), TRUE, priv->is_outbox);
1076 priv->row_reference = NULL;
1077 priv->next_row_reference = NULL;
1080 /* Connect signals */
1081 priv->row_changed_handler =
1082 g_signal_connect (GTK_TREE_MODEL(priv->header_model), "row-changed",
1083 G_CALLBACK(modest_msg_view_window_on_row_changed),
1085 priv->row_deleted_handler =
1086 g_signal_connect (GTK_TREE_MODEL(priv->header_model), "row-deleted",
1087 G_CALLBACK(modest_msg_view_window_on_row_deleted),
1089 priv->row_inserted_handler =
1090 g_signal_connect (GTK_TREE_MODEL(priv->header_model), "row-inserted",
1091 G_CALLBACK(modest_msg_view_window_on_row_inserted),
1093 priv->rows_reordered_handler =
1094 g_signal_connect(GTK_TREE_MODEL(priv->header_model), "rows-reordered",
1095 G_CALLBACK(modest_msg_view_window_on_row_reordered),
1098 if (header_view != NULL){
1099 modest_header_view_add_observer(header_view,
1100 MODEST_HEADER_VIEW_OBSERVER(window));
1103 tny_msg_view_set_msg (TNY_MSG_VIEW (priv->msg_view), NULL);
1104 update_branding (MODEST_MSG_VIEW_WINDOW (window));
1106 if (priv->row_reference) {
1107 path = gtk_tree_row_reference_get_path (priv->row_reference);
1108 if (gtk_tree_model_get_iter (priv->header_model, &iter, path)) {
1110 gtk_tree_model_get (priv->header_model, &iter,
1111 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
1113 message_reader (window, priv, header, NULL, NULL, priv->row_reference);
1114 g_object_unref (header);
1116 gtk_tree_path_free (path);
1118 /* Check dimming rules */
1119 modest_ui_actions_check_toolbar_dimming_rules (MODEST_WINDOW (window));
1120 modest_ui_actions_check_menu_dimming_rules (MODEST_WINDOW (window));
1121 modest_window_check_dimming_rules_group (MODEST_WINDOW (window), MODEST_DIMMING_RULES_CLIPBOARD);
1123 return MODEST_WINDOW(window);
1127 modest_msg_view_window_new_for_search_result (TnyMsg *msg,
1128 const gchar *modest_account_name,
1129 const gchar *mailbox,
1130 const gchar *msg_uid)
1132 ModestMsgViewWindow *window = NULL;
1133 ModestMsgViewWindowPrivate *priv = NULL;
1134 ModestWindowMgr *mgr = NULL;
1136 mgr = modest_runtime_get_window_mgr ();
1137 window = MODEST_MSG_VIEW_WINDOW (modest_window_mgr_get_msg_view_window (mgr));
1138 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), NULL);
1139 modest_msg_view_window_construct (window, modest_account_name, mailbox, msg_uid);
1141 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1143 /* Remember that this is a search result,
1144 * so we can disable some UI appropriately: */
1145 priv->is_search_result = TRUE;
1147 tny_msg_view_set_msg (TNY_MSG_VIEW (priv->msg_view), msg);
1148 update_branding (MODEST_MSG_VIEW_WINDOW (window));
1150 update_window_title (window);
1151 /* gtk_widget_show_all (GTK_WIDGET (window));*/
1152 modest_msg_view_window_update_priority (window);
1154 /* Check dimming rules */
1155 modest_ui_actions_check_toolbar_dimming_rules (MODEST_WINDOW (window));
1156 modest_ui_actions_check_menu_dimming_rules (MODEST_WINDOW (window));
1157 modest_window_check_dimming_rules_group (MODEST_WINDOW (window), MODEST_DIMMING_RULES_CLIPBOARD);
1159 return MODEST_WINDOW(window);
1163 modest_msg_view_window_is_other_body (ModestMsgViewWindow *self)
1165 ModestMsgViewWindowPrivate *priv = NULL;
1167 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self), FALSE);
1168 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
1170 return (priv->other_body != NULL);
1174 modest_msg_view_window_new_with_other_body (TnyMsg *msg,
1175 TnyMimePart *other_body,
1176 const gchar *modest_account_name,
1177 const gchar *mailbox,
1178 const gchar *msg_uid)
1180 GObject *obj = NULL;
1181 ModestMsgViewWindowPrivate *priv;
1182 ModestWindowMgr *mgr = NULL;
1184 g_return_val_if_fail (msg, NULL);
1185 mgr = modest_runtime_get_window_mgr ();
1186 obj = G_OBJECT (modest_window_mgr_get_msg_view_window (mgr));
1187 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (obj);
1188 modest_msg_view_window_construct (MODEST_MSG_VIEW_WINDOW (obj),
1189 modest_account_name, mailbox, msg_uid);
1192 priv->other_body = g_object_ref (other_body);
1193 modest_msg_view_set_msg_with_other_body (MODEST_MSG_VIEW (priv->msg_view), msg, other_body);
1195 tny_msg_view_set_msg (TNY_MSG_VIEW (priv->msg_view), msg);
1197 update_window_title (MODEST_MSG_VIEW_WINDOW (obj));
1198 update_branding (MODEST_MSG_VIEW_WINDOW (obj));
1200 /* gtk_widget_show_all (GTK_WIDGET (obj)); */
1202 /* Check dimming rules */
1203 modest_ui_actions_check_toolbar_dimming_rules (MODEST_WINDOW (obj));
1204 modest_ui_actions_check_menu_dimming_rules (MODEST_WINDOW (obj));
1205 modest_window_check_dimming_rules_group (MODEST_WINDOW (obj), MODEST_DIMMING_RULES_CLIPBOARD);
1207 return MODEST_WINDOW(obj);
1211 modest_msg_view_window_new_for_attachment (TnyMsg *msg,
1212 const gchar *modest_account_name,
1213 const gchar *mailbox,
1214 const gchar *msg_uid)
1216 return modest_msg_view_window_new_with_other_body (msg, NULL, modest_account_name, mailbox, msg_uid);
1220 modest_msg_view_window_on_row_changed (GtkTreeModel *header_model,
1223 ModestMsgViewWindow *window)
1225 check_dimming_rules_after_change (window);
1229 modest_msg_view_window_on_row_deleted(GtkTreeModel *header_model,
1231 ModestMsgViewWindow *window)
1233 check_dimming_rules_after_change (window);
1235 /* The window could have dissapeared */
1238 check_dimming_rules_after_change (ModestMsgViewWindow *window)
1240 modest_ui_actions_check_menu_dimming_rules (MODEST_WINDOW (window));
1241 modest_ui_actions_check_toolbar_dimming_rules (MODEST_WINDOW (window));
1245 /* On insertions we check if the folder still has the message we are
1246 * showing or do not. If do not, we do nothing. Which means we are still
1247 * not attached to any header folder and thus next/prev buttons are
1248 * still dimmed. Once the message that is shown by msg-view is found, the
1249 * new model of header-view will be attached and the references will be set.
1250 * On each further insertions dimming rules will be checked. However
1251 * this requires extra CPU time at least works.
1252 * (An message might be deleted from TnyFolder and thus will not be
1253 * inserted into the model again for example if it is removed by the
1254 * imap server and the header view is refreshed.)
1257 modest_msg_view_window_on_row_inserted (GtkTreeModel *model,
1258 GtkTreePath *tree_path,
1259 GtkTreeIter *tree_iter,
1260 ModestMsgViewWindow *window)
1262 ModestMsgViewWindowPrivate *priv = NULL;
1263 TnyHeader *header = NULL;
1265 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window));
1266 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1268 g_assert (model == priv->header_model);
1270 /* Check if the newly inserted message is the same we are actually
1271 * showing. IF not, we should remain detached from the header model
1272 * and thus prev and next toolbar buttons should remain dimmed. */
1273 gtk_tree_model_get (model, tree_iter,
1274 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
1277 if (TNY_IS_HEADER (header)) {
1280 uid = modest_tny_folder_get_header_unique_id (header);
1281 if (!g_str_equal(priv->msg_uid, uid)) {
1282 check_dimming_rules_after_change (window);
1284 g_object_unref (G_OBJECT(header));
1288 g_object_unref(G_OBJECT(header));
1291 if (priv->row_reference) {
1292 gtk_tree_row_reference_free (priv->row_reference);
1295 /* Setup row_reference for the actual msg. */
1296 priv->row_reference = gtk_tree_row_reference_new (priv->header_model, tree_path);
1297 if (priv->row_reference == NULL) {
1298 g_warning("%s: No reference for msg header item.", __FUNCTION__);
1302 /* Now set up next_row_reference. */
1303 if (priv->next_row_reference) {
1304 gtk_tree_row_reference_free (priv->next_row_reference);
1307 priv->next_row_reference = gtk_tree_row_reference_copy (priv->row_reference);
1308 select_next_valid_row (priv->header_model,
1309 &(priv->next_row_reference), FALSE, priv->is_outbox);
1311 /* Connect the remaining callbacks to become able to detect
1312 * changes in header-view. */
1313 priv->row_changed_handler =
1314 g_signal_connect (priv->header_model, "row-changed",
1315 G_CALLBACK (modest_msg_view_window_on_row_changed),
1317 priv->row_deleted_handler =
1318 g_signal_connect (priv->header_model, "row-deleted",
1319 G_CALLBACK (modest_msg_view_window_on_row_deleted),
1321 priv->rows_reordered_handler =
1322 g_signal_connect (priv->header_model, "rows-reordered",
1323 G_CALLBACK (modest_msg_view_window_on_row_reordered),
1326 check_dimming_rules_after_change (window);
1330 modest_msg_view_window_on_row_reordered (GtkTreeModel *header_model,
1334 ModestMsgViewWindow *window)
1336 ModestMsgViewWindowPrivate *priv = NULL;
1337 gboolean already_changed = FALSE;
1339 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(window);
1341 /* If the current row was reordered select the proper next
1342 valid row. The same if the next row reference changes */
1343 if (!priv->row_reference ||
1344 !gtk_tree_row_reference_valid (priv->row_reference))
1347 if (priv->next_row_reference &&
1348 gtk_tree_row_reference_valid (priv->next_row_reference)) {
1349 GtkTreePath *cur, *next;
1350 /* Check that the order is still the correct one */
1351 cur = gtk_tree_row_reference_get_path (priv->row_reference);
1352 next = gtk_tree_row_reference_get_path (priv->next_row_reference);
1353 gtk_tree_path_next (cur);
1354 if (gtk_tree_path_compare (cur, next) != 0) {
1355 gtk_tree_row_reference_free (priv->next_row_reference);
1356 priv->next_row_reference = gtk_tree_row_reference_copy (priv->row_reference);
1357 select_next_valid_row (header_model, &(priv->next_row_reference), FALSE, priv->is_outbox);
1358 already_changed = TRUE;
1360 gtk_tree_path_free (cur);
1361 gtk_tree_path_free (next);
1363 if (priv->next_row_reference)
1364 gtk_tree_row_reference_free (priv->next_row_reference);
1365 /* Update next row reference */
1366 priv->next_row_reference = gtk_tree_row_reference_copy (priv->row_reference);
1367 select_next_valid_row (header_model, &(priv->next_row_reference), FALSE, priv->is_outbox);
1368 already_changed = TRUE;
1371 check_dimming_rules_after_change (window);
1374 /* The modest_msg_view_window_update_model_replaced implements update
1375 * function for ModestHeaderViewObserver. Checks whether the TnyFolder
1376 * actually belongs to the header-view is the same as the TnyFolder of
1377 * the message of msg-view or not. If they are different, there is
1378 * nothing to do. If they are the same, then the model has replaced and
1379 * the reference in msg-view shall be replaced from the old model to
1380 * the new model. In this case the view will be detached from it's
1381 * header folder. From this point the next/prev buttons are dimmed.
1384 modest_msg_view_window_update_model_replaced (ModestHeaderViewObserver *observer,
1385 GtkTreeModel *model,
1386 const gchar *tny_folder_id)
1388 ModestMsgViewWindowPrivate *priv = NULL;
1389 ModestMsgViewWindow *window = NULL;
1391 g_assert(MODEST_IS_HEADER_VIEW_OBSERVER(observer));
1392 g_assert(MODEST_IS_MSG_VIEW_WINDOW(observer));
1394 window = MODEST_MSG_VIEW_WINDOW(observer);
1395 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(window);
1397 /* If there is an other folder in the header-view then we do
1398 * not care about it's model (msg list). Else if the
1399 * header-view shows the folder the msg shown by us is in, we
1400 * shall replace our model reference and make some check. */
1401 if(model == NULL || tny_folder_id == NULL ||
1402 (priv->header_folder_id && !g_str_equal(tny_folder_id, priv->header_folder_id)))
1405 /* Model is changed(replaced), so we should forget the old
1406 * one. Because there might be other references and there
1407 * might be some change on the model even if we unreferenced
1408 * it, we need to disconnect our signals here. */
1409 if (priv->header_model) {
1410 if (g_signal_handler_is_connected(G_OBJECT (priv->header_model),
1411 priv->row_changed_handler))
1412 g_signal_handler_disconnect(G_OBJECT (priv->header_model),
1413 priv->row_changed_handler);
1414 if (g_signal_handler_is_connected(G_OBJECT (priv->header_model),
1415 priv->row_deleted_handler))
1416 g_signal_handler_disconnect(G_OBJECT (priv->header_model),
1417 priv->row_deleted_handler);
1418 if (g_signal_handler_is_connected(G_OBJECT (priv->header_model),
1419 priv->row_inserted_handler))
1420 g_signal_handler_disconnect(G_OBJECT (priv->header_model),
1421 priv->row_inserted_handler);
1422 if (g_signal_handler_is_connected(G_OBJECT (priv->header_model),
1423 priv->rows_reordered_handler))
1424 g_signal_handler_disconnect(G_OBJECT (priv->header_model),
1425 priv->rows_reordered_handler);
1428 if (priv->row_reference)
1429 gtk_tree_row_reference_free (priv->row_reference);
1430 if (priv->next_row_reference)
1431 gtk_tree_row_reference_free (priv->next_row_reference);
1432 g_object_unref(priv->header_model);
1435 priv->row_changed_handler = 0;
1436 priv->row_deleted_handler = 0;
1437 priv->row_inserted_handler = 0;
1438 priv->rows_reordered_handler = 0;
1439 priv->next_row_reference = NULL;
1440 priv->row_reference = NULL;
1441 priv->header_model = NULL;
1444 priv->header_model = g_object_ref (model);
1446 /* Also we must connect to the new model for row insertions.
1447 * Only for insertions now. We will need other ones only after
1448 * the msg is show by msg-view is added to the new model. */
1449 priv->row_inserted_handler =
1450 g_signal_connect (priv->header_model, "row-inserted",
1451 G_CALLBACK(modest_msg_view_window_on_row_inserted),
1454 modest_ui_actions_check_menu_dimming_rules(MODEST_WINDOW(window));
1455 modest_ui_actions_check_toolbar_dimming_rules(MODEST_WINDOW(window));
1459 modest_msg_view_window_toolbar_on_transfer_mode (ModestMsgViewWindow *self)
1461 ModestMsgViewWindowPrivate *priv= NULL;
1463 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self), FALSE);
1464 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
1466 return priv->progress_hint;
1470 modest_msg_view_window_get_header (ModestMsgViewWindow *self)
1472 ModestMsgViewWindowPrivate *priv= NULL;
1474 TnyHeader *header = NULL;
1475 GtkTreePath *path = NULL;
1478 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self), NULL);
1479 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
1481 /* If the message was not obtained from a treemodel,
1482 * for instance if it was opened directly by the search UI:
1484 if (priv->header_model == NULL ||
1485 priv->row_reference == NULL ||
1486 !gtk_tree_row_reference_valid (priv->row_reference)) {
1487 msg = modest_msg_view_window_get_message (self);
1489 header = tny_msg_get_header (msg);
1490 g_object_unref (msg);
1495 /* Get iter of the currently selected message in the header view: */
1496 path = gtk_tree_row_reference_get_path (priv->row_reference);
1497 g_return_val_if_fail (path != NULL, NULL);
1498 gtk_tree_model_get_iter (priv->header_model,
1502 /* Get current message header */
1503 gtk_tree_model_get (priv->header_model, &iter,
1504 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
1507 gtk_tree_path_free (path);
1512 modest_msg_view_window_get_message (ModestMsgViewWindow *self)
1514 ModestMsgViewWindowPrivate *priv;
1516 g_return_val_if_fail (self, NULL);
1518 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(self);
1520 return tny_msg_view_get_msg (TNY_MSG_VIEW (priv->msg_view));
1524 modest_msg_view_window_get_message_uid (ModestMsgViewWindow *self)
1526 ModestMsgViewWindowPrivate *priv;
1528 g_return_val_if_fail (self, NULL);
1530 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
1532 return (const gchar*) priv->msg_uid;
1535 /* Used for the Ctrl+F accelerator */
1537 modest_msg_view_window_toggle_isearch_toolbar (GtkWidget *obj,
1540 ModestMsgViewWindow *window = MODEST_MSG_VIEW_WINDOW (data);
1541 ModestMsgViewWindowPrivate *priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1543 if (GTK_WIDGET_VISIBLE (priv->isearch_toolbar)) {
1544 modest_msg_view_window_isearch_toolbar_close (obj, data);
1546 modest_msg_view_window_show_isearch_toolbar (obj, data);
1550 /* Handler for menu option */
1552 modest_msg_view_window_show_isearch_toolbar (GtkWidget *obj,
1555 ModestMsgViewWindow *window = MODEST_MSG_VIEW_WINDOW (data);
1556 ModestMsgViewWindowPrivate *priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1558 gtk_widget_show (priv->isearch_toolbar);
1559 modest_isearch_toolbar_highlight_entry (MODEST_ISEARCH_TOOLBAR (priv->isearch_toolbar), TRUE);
1562 /* Handler for click on the "X" close button in isearch toolbar */
1564 modest_msg_view_window_isearch_toolbar_close (GtkWidget *widget,
1565 ModestMsgViewWindow *obj)
1567 ModestMsgViewWindowPrivate *priv;
1569 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (obj);
1572 gtk_widget_hide (priv->isearch_toolbar);
1573 modest_msg_view_grab_focus (MODEST_MSG_VIEW (priv->msg_view));
1577 modest_msg_view_window_isearch_toolbar_search (GtkWidget *widget,
1578 ModestMsgViewWindow *obj)
1580 const gchar *current_search;
1581 ModestMsgViewWindowPrivate *priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (obj);
1583 if (modest_mime_part_view_is_empty (MODEST_MIME_PART_VIEW (priv->msg_view))) {
1584 modest_platform_system_banner (NULL, NULL, _("mail_ib_nothing_to_find"));
1588 current_search = modest_isearch_toolbar_get_search (MODEST_ISEARCH_TOOLBAR (widget));
1590 if ((current_search == NULL) || (strcmp (current_search, "") == 0)) {
1591 modest_platform_system_banner (NULL, NULL, _CS("ecdg_ib_find_rep_enter_text"));
1595 if ((priv->last_search == NULL) || (strcmp (priv->last_search, current_search) != 0)) {
1597 g_free (priv->last_search);
1598 priv->last_search = g_strdup (current_search);
1599 result = modest_isearch_view_search (MODEST_ISEARCH_VIEW (priv->msg_view),
1602 modest_platform_system_banner (NULL, NULL,
1603 _HL("ckct_ib_find_no_matches"));
1604 g_free (priv->last_search);
1605 priv->last_search = NULL;
1607 modest_isearch_toolbar_highlight_entry (MODEST_ISEARCH_TOOLBAR (priv->isearch_toolbar), TRUE);
1610 if (!modest_isearch_view_search_next (MODEST_ISEARCH_VIEW (priv->msg_view))) {
1611 modest_platform_system_banner (NULL, NULL,
1612 _HL("ckct_ib_find_search_complete"));
1613 g_free (priv->last_search);
1614 priv->last_search = NULL;
1616 modest_isearch_toolbar_highlight_entry (MODEST_ISEARCH_TOOLBAR (priv->isearch_toolbar), TRUE);
1623 modest_msg_view_window_set_zoom (ModestWindow *window,
1626 ModestMsgViewWindowPrivate *priv;
1627 ModestWindowPrivate *parent_priv;
1629 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window));
1631 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1632 parent_priv = MODEST_WINDOW_GET_PRIVATE (window);
1633 modest_zoomable_set_zoom (MODEST_ZOOMABLE (priv->msg_view), zoom);
1638 modest_msg_view_window_get_zoom (ModestWindow *window)
1640 ModestMsgViewWindowPrivate *priv;
1642 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), 1.0);
1644 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1645 return modest_zoomable_get_zoom (MODEST_ZOOMABLE (priv->msg_view));
1649 modest_msg_view_window_zoom_plus (ModestWindow *window)
1652 ModestMsgViewWindowPrivate *priv;
1656 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), 1.0);
1657 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1659 zoom_level = modest_zoomable_get_zoom (MODEST_ZOOMABLE (priv->msg_view));
1661 if (zoom_level >= 2.0) {
1662 modest_platform_system_banner (NULL, NULL,
1663 _CS("ckct_ib_max_zoom_level_reached"));
1665 } else if (zoom_level >= 1.5) {
1667 } else if (zoom_level >= 1.2) {
1669 } else if (zoom_level >= 1.0) {
1671 } else if (zoom_level >= 0.8) {
1673 } else if (zoom_level >= 0.5) {
1679 /* set zoom level */
1680 int_zoom = (gint) rint (zoom_level*100.0+0.1);
1681 banner_text = g_strdup_printf (_HL("wdgt_ib_zoom"), int_zoom);
1682 modest_platform_information_banner (GTK_WIDGET (window), NULL, banner_text);
1683 g_free (banner_text);
1684 modest_zoomable_set_zoom (MODEST_ZOOMABLE (priv->msg_view), zoom_level);
1690 modest_msg_view_window_zoom_minus (ModestWindow *window)
1693 ModestMsgViewWindowPrivate *priv;
1697 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), 1.0);
1698 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1700 zoom_level = modest_zoomable_get_zoom (MODEST_ZOOMABLE (priv->msg_view));
1702 if (zoom_level <= 0.5) {
1703 modest_platform_system_banner (NULL, NULL,
1704 _CS("ckct_ib_min_zoom_level_reached"));
1706 } else if (zoom_level <= 0.8) {
1708 } else if (zoom_level <= 1.0) {
1710 } else if (zoom_level <= 1.2) {
1712 } else if (zoom_level <= 1.5) {
1714 } else if (zoom_level <= 2.0) {
1720 /* set zoom level */
1721 int_zoom = (gint) rint (zoom_level*100.0+0.1);
1722 banner_text = g_strdup_printf (_HL("wdgt_ib_zoom"), int_zoom);
1723 modest_platform_information_banner (GTK_WIDGET (window), NULL, banner_text);
1724 g_free (banner_text);
1725 modest_zoomable_set_zoom (MODEST_ZOOMABLE (priv->msg_view), zoom_level);
1731 modest_msg_view_window_key_event (GtkWidget *window,
1737 focus = gtk_window_get_focus (GTK_WINDOW (window));
1739 /* for the isearch toolbar case */
1740 if (focus && GTK_IS_ENTRY (focus)) {
1741 if (event->keyval == GDK_BackSpace) {
1743 copy = gdk_event_copy ((GdkEvent *) event);
1744 gtk_widget_event (focus, copy);
1745 gdk_event_free (copy);
1755 modest_msg_view_window_last_message_selected (ModestMsgViewWindow *window)
1758 ModestMsgViewWindowPrivate *priv;
1759 GtkTreeIter tmp_iter;
1760 gboolean is_last_selected;
1762 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), TRUE);
1763 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1765 /*if no model (so no rows at all), then virtually we are the last*/
1766 if (!priv->header_model || !priv->row_reference)
1769 if (!gtk_tree_row_reference_valid (priv->row_reference))
1772 path = gtk_tree_row_reference_get_path (priv->row_reference);
1776 is_last_selected = TRUE;
1777 while (is_last_selected) {
1779 gtk_tree_path_next (path);
1780 if (!gtk_tree_model_get_iter (priv->header_model, &tmp_iter, path))
1782 gtk_tree_model_get (priv->header_model, &tmp_iter,
1783 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
1786 if (msg_is_visible (header, priv->is_outbox))
1787 is_last_selected = FALSE;
1788 g_object_unref(G_OBJECT(header));
1791 gtk_tree_path_free (path);
1792 return is_last_selected;
1796 modest_msg_view_window_has_headers_model (ModestMsgViewWindow *window)
1798 ModestMsgViewWindowPrivate *priv;
1800 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), TRUE);
1801 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1803 return priv->header_model != NULL;
1807 modest_msg_view_window_is_search_result (ModestMsgViewWindow *window)
1809 ModestMsgViewWindowPrivate *priv;
1811 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), TRUE);
1812 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1814 return priv->is_search_result;
1818 msg_is_visible (TnyHeader *header, gboolean check_outbox)
1820 if ((tny_header_get_flags(header) & TNY_HEADER_FLAG_DELETED))
1822 if (!check_outbox) {
1825 ModestTnySendQueueStatus status;
1826 status = modest_tny_all_send_queues_get_msg_status (header);
1827 return ((status != MODEST_TNY_SEND_QUEUE_FAILED) &&
1828 (status != MODEST_TNY_SEND_QUEUE_SENDING));
1833 modest_msg_view_window_first_message_selected (ModestMsgViewWindow *window)
1836 ModestMsgViewWindowPrivate *priv;
1837 gboolean is_first_selected;
1838 GtkTreeIter tmp_iter;
1840 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), TRUE);
1841 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1843 /*if no model (so no rows at all), then virtually we are the first*/
1844 if (!priv->header_model || !priv->row_reference)
1847 if (!gtk_tree_row_reference_valid (priv->row_reference))
1850 path = gtk_tree_row_reference_get_path (priv->row_reference);
1854 is_first_selected = TRUE;
1855 while (is_first_selected) {
1857 if(!gtk_tree_path_prev (path))
1859 /* Here the 'if' is needless for logic, but let make sure
1860 * iter is valid for gtk_tree_model_get. */
1861 if (!gtk_tree_model_get_iter (priv->header_model, &tmp_iter, path))
1863 gtk_tree_model_get (priv->header_model, &tmp_iter,
1864 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
1867 if (msg_is_visible (header, priv->is_outbox))
1868 is_first_selected = FALSE;
1869 g_object_unref(G_OBJECT(header));
1872 gtk_tree_path_free (path);
1873 return is_first_selected;
1880 GtkTreeRowReference *row_reference;
1884 message_reader_performer (gboolean canceled,
1886 GtkWindow *parent_window,
1887 TnyAccount *account,
1890 ModestMailOperation *mail_op = NULL;
1891 MsgReaderInfo *info;
1893 info = (MsgReaderInfo *) user_data;
1894 if (canceled || err) {
1895 update_window_title (MODEST_MSG_VIEW_WINDOW (parent_window));
1899 /* Register the header - it'll be unregistered in the callback */
1901 modest_window_mgr_register_header (modest_runtime_get_window_mgr (), info->header, NULL);
1903 /* New mail operation */
1904 mail_op = modest_mail_operation_new_with_error_handling (G_OBJECT(parent_window),
1905 modest_ui_actions_disk_operations_error_handler,
1908 modest_mail_operation_queue_add (modest_runtime_get_mail_operation_queue (), mail_op);
1910 modest_mail_operation_get_msg (mail_op, info->header, TRUE, view_msg_cb, info->row_reference);
1912 modest_mail_operation_find_msg (mail_op, info->folder, info->msg_uid, TRUE, view_msg_cb, NULL);
1913 g_object_unref (mail_op);
1915 /* Update dimming rules */
1916 modest_ui_actions_check_toolbar_dimming_rules (MODEST_WINDOW (parent_window));
1917 modest_ui_actions_check_menu_dimming_rules (MODEST_WINDOW (parent_window));
1920 /* Frees. The row_reference will be freed by the view_msg_cb callback */
1921 g_free (info->msg_uid);
1923 g_object_unref (info->folder);
1925 g_object_unref (info->header);
1926 g_slice_free (MsgReaderInfo, info);
1931 * Reads the message whose summary item is @header. It takes care of
1932 * several things, among others:
1934 * If the message was not previously downloaded then ask the user
1935 * before downloading. If there is no connection launch the connection
1936 * dialog. Update toolbar dimming rules.
1938 * Returns: TRUE if the mail operation was started, otherwise if the
1939 * user do not want to download the message, or if the user do not
1940 * want to connect, then the operation is not issued
1943 message_reader (ModestMsgViewWindow *window,
1944 ModestMsgViewWindowPrivate *priv,
1946 const gchar *msg_uid,
1948 GtkTreeRowReference *row_reference)
1950 ModestWindowMgr *mgr;
1951 TnyAccount *account = NULL;
1952 MsgReaderInfo *info;
1954 /* We set the header from model while we're loading */
1955 tny_header_view_set_header (TNY_HEADER_VIEW (priv->msg_view), header);
1956 modest_window_set_title (MODEST_WINDOW (window), _CS("ckdg_pb_updating"));
1962 g_object_ref (folder);
1964 mgr = modest_runtime_get_window_mgr ();
1965 /* Msg download completed */
1966 if (!header || !(tny_header_get_flags (header) & TNY_HEADER_FLAG_CACHED)) {
1968 /* Ask the user if he wants to download the message if
1970 if (!tny_device_is_online (modest_runtime_get_device())) {
1971 GtkResponseType response;
1973 response = modest_platform_run_confirmation_dialog (GTK_WINDOW (window),
1974 _("mcen_nc_get_msg"));
1975 if (response == GTK_RESPONSE_CANCEL) {
1976 update_window_title (window);
1981 folder = tny_header_get_folder (header);
1983 info = g_slice_new (MsgReaderInfo);
1984 info->msg_uid = g_strdup (msg_uid);
1986 info->header = g_object_ref (header);
1988 info->header = NULL;
1990 info->folder = g_object_ref (folder);
1992 info->folder = NULL;
1993 if (row_reference) {
1994 info->row_reference = gtk_tree_row_reference_copy (row_reference);
1996 info->row_reference = NULL;
1999 /* Offer the connection dialog if necessary */
2000 modest_platform_connect_if_remote_and_perform ((GtkWindow *) window,
2002 TNY_FOLDER_STORE (folder),
2003 message_reader_performer,
2006 g_object_unref (folder);
2012 folder = tny_header_get_folder (header);
2015 account = tny_folder_get_account (folder);
2017 info = g_slice_new (MsgReaderInfo);
2018 info->msg_uid = g_strdup (msg_uid);
2020 info->folder = g_object_ref (folder);
2022 info->folder = NULL;
2024 info->header = g_object_ref (header);
2026 info->header = NULL;
2028 info->row_reference = gtk_tree_row_reference_copy (row_reference);
2030 info->row_reference = NULL;
2032 message_reader_performer (FALSE, NULL, (GtkWindow *) window, account, info);
2034 g_object_unref (account);
2036 g_object_unref (folder);
2042 modest_msg_view_window_select_next_message (ModestMsgViewWindow *window)
2044 ModestMsgViewWindowPrivate *priv;
2045 GtkTreePath *path= NULL;
2046 GtkTreeIter tmp_iter;
2048 gboolean retval = TRUE;
2049 GtkTreeRowReference *row_reference = NULL;
2051 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), FALSE);
2052 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
2054 if (!priv->row_reference)
2057 /* Update the next row reference if it's not valid. This could
2058 happen if for example the header which it was pointing to,
2059 was deleted. The best place to do it is in the row-deleted
2060 handler but the tinymail model do not work like the glib
2061 tree models and reports the deletion when the row is still
2063 if (!gtk_tree_row_reference_valid (priv->next_row_reference)) {
2064 if (priv->next_row_reference) {
2065 gtk_tree_row_reference_free (priv->next_row_reference);
2067 if (gtk_tree_row_reference_valid (priv->row_reference)) {
2068 priv->next_row_reference = gtk_tree_row_reference_copy (priv->row_reference);
2069 select_next_valid_row (priv->header_model, &(priv->next_row_reference), FALSE, priv->is_outbox);
2071 priv->next_row_reference = NULL;
2074 if (priv->next_row_reference)
2075 path = gtk_tree_row_reference_get_path (priv->next_row_reference);
2079 row_reference = gtk_tree_row_reference_copy (priv->next_row_reference);
2081 gtk_tree_model_get_iter (priv->header_model,
2084 gtk_tree_path_free (path);
2086 gtk_tree_model_get (priv->header_model, &tmp_iter,
2087 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
2090 /* Read the message & show it */
2091 if (!message_reader (window, priv, header, NULL, NULL, row_reference)) {
2094 gtk_tree_row_reference_free (row_reference);
2097 g_object_unref (header);
2103 modest_msg_view_window_select_previous_message (ModestMsgViewWindow *window)
2105 ModestMsgViewWindowPrivate *priv = NULL;
2107 gboolean finished = FALSE;
2108 gboolean retval = FALSE;
2110 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), FALSE);
2111 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
2113 if (priv->row_reference && !gtk_tree_row_reference_valid (priv->row_reference)) {
2114 gtk_tree_row_reference_free (priv->row_reference);
2115 priv->row_reference = NULL;
2118 /* Return inmediatly if there is no header model */
2119 if (!priv->header_model || !priv->row_reference)
2122 path = gtk_tree_row_reference_get_path (priv->row_reference);
2123 while (!finished && gtk_tree_path_prev (path)) {
2127 gtk_tree_model_get_iter (priv->header_model, &iter, path);
2128 gtk_tree_model_get (priv->header_model, &iter,
2129 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
2133 if (msg_is_visible (header, priv->is_outbox)) {
2134 GtkTreeRowReference *row_reference;
2135 row_reference = gtk_tree_row_reference_new (priv->header_model, path);
2136 /* Read the message & show it */
2137 retval = message_reader (window, priv, header, NULL, NULL, row_reference);
2138 gtk_tree_row_reference_free (row_reference);
2142 g_object_unref (header);
2146 gtk_tree_path_free (path);
2151 view_msg_cb (ModestMailOperation *mail_op,
2158 ModestMsgViewWindow *self = NULL;
2159 ModestMsgViewWindowPrivate *priv = NULL;
2160 GtkTreeRowReference *row_reference = NULL;
2162 /* Unregister the header (it was registered before creating the mail operation) */
2163 modest_window_mgr_unregister_header (modest_runtime_get_window_mgr (), header);
2165 row_reference = (GtkTreeRowReference *) user_data;
2168 gtk_tree_row_reference_free (row_reference);
2169 self = (ModestMsgViewWindow *) modest_mail_operation_get_source (mail_op);
2171 /* Restore window title */
2172 update_window_title (self);
2173 modest_ui_actions_on_close_window (NULL, MODEST_WINDOW (self));
2174 g_object_unref (self);
2179 /* If there was any error */
2180 if (!modest_ui_actions_msg_retrieval_check (mail_op, header, msg)) {
2182 gtk_tree_row_reference_free (row_reference);
2183 self = (ModestMsgViewWindow *) modest_mail_operation_get_source (mail_op);
2185 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
2186 /* First we check if the parent is a folder window */
2187 if (priv->msg_uid && !modest_window_mgr_get_folder_window (MODEST_WINDOW_MGR (modest_runtime_get_window_mgr ()))) {
2189 TnyAccount *account = NULL;
2190 GtkWidget *header_window = NULL;
2192 is_merge = g_str_has_prefix (priv->msg_uid, "merge:");
2194 /* Get the account */
2196 account = tny_account_store_find_account (TNY_ACCOUNT_STORE (modest_runtime_get_account_store ()),
2199 if (is_merge || account) {
2200 TnyFolder *folder = NULL;
2202 /* Try to get the message, if it's already downloaded
2203 we don't need to connect */
2205 folder = modest_tny_folder_store_find_folder_from_uri (TNY_FOLDER_STORE (account),
2208 ModestTnyAccountStore *account_store;
2209 ModestTnyLocalFoldersAccount *local_folders_account;
2211 account_store = modest_runtime_get_account_store ();
2212 local_folders_account = MODEST_TNY_LOCAL_FOLDERS_ACCOUNT (
2213 modest_tny_account_store_get_local_folders_account (account_store));
2214 folder = modest_tny_local_folders_account_get_merged_outbox (local_folders_account);
2215 g_object_unref (local_folders_account);
2217 if (account) g_object_unref (account);
2220 header_window = (GtkWidget *)
2221 modest_header_window_new (
2223 modest_window_get_active_account (MODEST_WINDOW (self)),
2224 modest_window_get_active_mailbox (MODEST_WINDOW (self)));
2225 if (!modest_window_mgr_register_window (modest_runtime_get_window_mgr (),
2226 MODEST_WINDOW (header_window),
2228 gtk_widget_destroy (GTK_WIDGET (header_window));
2230 gtk_widget_show_all (GTK_WIDGET (header_window));
2232 g_object_unref (folder);
2238 /* Restore window title */
2239 update_window_title (self);
2240 modest_ui_actions_on_close_window (NULL, MODEST_WINDOW (self));
2241 g_object_unref (self);
2246 /* Get the window */
2247 self = (ModestMsgViewWindow *) modest_mail_operation_get_source (mail_op);
2248 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self));
2249 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
2251 /* Update the row reference */
2252 if (priv->row_reference != NULL) {
2253 gtk_tree_row_reference_free (priv->row_reference);
2254 priv->row_reference = (row_reference && gtk_tree_row_reference_valid (row_reference))?gtk_tree_row_reference_copy (row_reference):NULL;
2255 if (priv->next_row_reference != NULL) {
2256 gtk_tree_row_reference_free (priv->next_row_reference);
2258 if (priv->row_reference) {
2259 priv->next_row_reference = gtk_tree_row_reference_copy (priv->row_reference);
2260 select_next_valid_row (priv->header_model, &(priv->next_row_reference), TRUE, priv->is_outbox);
2262 priv->next_row_reference = NULL;
2266 /* Mark header as read */
2267 if (!(tny_header_get_flags (header) & TNY_HEADER_FLAG_SEEN))
2268 tny_header_set_flag (header, TNY_HEADER_FLAG_SEEN);
2270 /* Set new message */
2271 if (priv->msg_view != NULL && TNY_IS_MSG_VIEW (priv->msg_view)) {
2272 tny_msg_view_set_msg (TNY_MSG_VIEW (priv->msg_view), msg);
2273 modest_msg_view_window_update_priority (self);
2274 update_window_title (MODEST_MSG_VIEW_WINDOW (self));
2275 update_branding (MODEST_MSG_VIEW_WINDOW (self));
2276 modest_msg_view_grab_focus (MODEST_MSG_VIEW (priv->msg_view));
2279 /* Set the new message uid of the window */
2280 if (priv->msg_uid) {
2281 g_free (priv->msg_uid);
2282 priv->msg_uid = modest_tny_folder_get_header_unique_id (header);
2285 /* Notify the observers */
2286 g_signal_emit (G_OBJECT (self), signals[MSG_CHANGED_SIGNAL],
2287 0, priv->header_model, priv->row_reference);
2289 /* Sync the flags if the message is not opened from a header
2290 model, i.e, if it's opened from a notification */
2291 if (!priv->header_model)
2295 g_object_unref (self);
2297 gtk_tree_row_reference_free (row_reference);
2301 modest_msg_view_window_get_folder_type (ModestMsgViewWindow *window)
2303 ModestMsgViewWindowPrivate *priv;
2305 TnyFolderType folder_type;
2307 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
2309 folder_type = TNY_FOLDER_TYPE_UNKNOWN;
2311 msg = tny_msg_view_get_msg (TNY_MSG_VIEW (priv->msg_view));
2315 folder = tny_msg_get_folder (msg);
2317 folder_type = modest_tny_folder_guess_folder_type (folder);
2318 g_object_unref (folder);
2320 g_object_unref (msg);
2328 modest_msg_view_window_update_priority (ModestMsgViewWindow *window)
2330 ModestMsgViewWindowPrivate *priv;
2331 TnyHeader *header = NULL;
2332 TnyHeaderFlags flags = 0;
2334 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
2336 if (priv->header_model && priv->row_reference && gtk_tree_row_reference_valid (priv->row_reference)) {
2338 GtkTreePath *path = NULL;
2340 path = gtk_tree_row_reference_get_path (priv->row_reference);
2341 g_return_if_fail (path != NULL);
2342 gtk_tree_model_get_iter (priv->header_model,
2344 gtk_tree_row_reference_get_path (priv->row_reference));
2346 gtk_tree_model_get (priv->header_model, &iter, TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
2348 gtk_tree_path_free (path);
2351 msg = tny_msg_view_get_msg (TNY_MSG_VIEW (priv->msg_view));
2353 header = tny_msg_get_header (msg);
2354 g_object_unref (msg);
2359 flags = tny_header_get_flags (header);
2360 g_object_unref(G_OBJECT(header));
2363 modest_msg_view_set_priority (MODEST_MSG_VIEW(priv->msg_view), flags);
2368 toolbar_resize (ModestMsgViewWindow *self)
2370 ModestMsgViewWindowPrivate *priv = NULL;
2371 ModestWindowPrivate *parent_priv = NULL;
2373 gint static_button_size;
2374 ModestWindowMgr *mgr;
2376 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self));
2377 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
2378 parent_priv = MODEST_WINDOW_GET_PRIVATE(self);
2380 mgr = modest_runtime_get_window_mgr ();
2381 static_button_size = modest_window_mgr_get_fullscreen_mode (mgr)?120:120;
2383 if (parent_priv->toolbar) {
2384 /* Set expandable and homogeneous tool buttons */
2385 widget = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/ToolbarMessageReply");
2386 gtk_tool_item_set_expand (GTK_TOOL_ITEM (widget), TRUE);
2387 gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (widget), TRUE);
2388 widget = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/ToolbarMessageReplyAll");
2389 gtk_tool_item_set_expand (GTK_TOOL_ITEM (widget), TRUE);
2390 gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (widget), TRUE);
2391 widget = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/ToolbarMessageForward");
2392 gtk_tool_item_set_expand (GTK_TOOL_ITEM (widget), TRUE);
2393 gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (widget), TRUE);
2394 widget = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/ToolbarDeleteMessage");
2395 gtk_tool_item_set_expand (GTK_TOOL_ITEM (widget), TRUE);
2396 gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (widget), TRUE);
2397 widget = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/ToolbarDownloadExternalImages");
2398 gtk_tool_item_set_expand (GTK_TOOL_ITEM (widget), TRUE);
2399 gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (widget), TRUE);
2400 gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (priv->next_toolitem), TRUE);
2401 gtk_tool_item_set_expand (GTK_TOOL_ITEM (priv->next_toolitem), TRUE);
2402 gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (priv->prev_toolitem), TRUE);
2403 gtk_tool_item_set_expand (GTK_TOOL_ITEM (priv->prev_toolitem), TRUE);
2408 modest_msg_view_window_show_toolbar (ModestWindow *self,
2409 gboolean show_toolbar)
2411 ModestMsgViewWindowPrivate *priv = NULL;
2412 ModestWindowPrivate *parent_priv;
2414 parent_priv = MODEST_WINDOW_GET_PRIVATE(self);
2415 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(self);
2417 /* Set optimized view status */
2418 priv->optimized_view = !show_toolbar;
2420 if (!parent_priv->toolbar) {
2421 parent_priv->toolbar = gtk_ui_manager_get_widget (parent_priv->ui_manager,
2424 /* We don't use HILDON_ICON_SIZE_FINGER in order to avoid the ifdef here */
2425 gtk_toolbar_set_icon_size (GTK_TOOLBAR (parent_priv->toolbar), gtk_icon_size_from_name ("hildon-finger"));
2426 gtk_widget_set_no_show_all (parent_priv->toolbar, TRUE);
2428 priv->next_toolitem = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/ToolbarMessageNext");
2429 priv->prev_toolitem = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/ToolbarMessageBack");
2430 toolbar_resize (MODEST_MSG_VIEW_WINDOW (self));
2432 modest_window_add_toolbar (MODEST_WINDOW (self),
2433 GTK_TOOLBAR (parent_priv->toolbar));
2438 /* Quick hack: this prevents toolbar icons "dance" when progress bar show status is changed */
2439 /* TODO: resize mode migth be GTK_RESIZE_QUEUE, in order to avoid unneccesary shows */
2440 gtk_container_set_resize_mode (GTK_CONTAINER(parent_priv->toolbar), GTK_RESIZE_IMMEDIATE);
2442 gtk_widget_show (GTK_WIDGET (parent_priv->toolbar));
2443 if (modest_msg_view_window_transfer_mode_enabled (MODEST_MSG_VIEW_WINDOW (self)))
2444 set_progress_hint (MODEST_MSG_VIEW_WINDOW (self), TRUE);
2446 set_progress_hint (MODEST_MSG_VIEW_WINDOW (self), FALSE);
2449 gtk_widget_set_no_show_all (parent_priv->toolbar, TRUE);
2450 gtk_widget_hide (GTK_WIDGET (parent_priv->toolbar));
2455 modest_msg_view_window_clipboard_owner_change (GtkClipboard *clipboard,
2457 ModestMsgViewWindow *window)
2459 if (!GTK_WIDGET_VISIBLE (window))
2462 modest_window_check_dimming_rules_group (MODEST_WINDOW (window), MODEST_DIMMING_RULES_CLIPBOARD);
2466 modest_msg_view_window_transfer_mode_enabled (ModestMsgViewWindow *self)
2468 ModestMsgViewWindowPrivate *priv;
2470 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self), FALSE);
2471 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(self);
2473 return priv->progress_hint;
2477 observers_empty (ModestMsgViewWindow *self)
2480 ModestMsgViewWindowPrivate *priv;
2481 gboolean is_empty = TRUE;
2482 guint pending_ops = 0;
2484 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(self);
2485 tmp = priv->progress_widgets;
2487 /* Check all observers */
2488 while (tmp && is_empty) {
2489 pending_ops = modest_progress_object_num_pending_operations (MODEST_PROGRESS_OBJECT(tmp->data));
2490 is_empty = pending_ops == 0;
2492 tmp = g_slist_next(tmp);
2499 on_account_removed (TnyAccountStore *account_store,
2500 TnyAccount *account,
2503 /* Do nothing if it's a transport account, because we only
2504 show the messages of a store account */
2505 if (tny_account_get_account_type(account) == TNY_ACCOUNT_TYPE_STORE) {
2506 const gchar *parent_acc = NULL;
2507 const gchar *our_acc = NULL;
2509 our_acc = modest_window_get_active_account (MODEST_WINDOW (user_data));
2510 parent_acc = modest_tny_account_get_parent_modest_account_name_for_server_account (account);
2512 /* Close this window if I'm showing a message of the removed account */
2513 if (our_acc && parent_acc && strcmp (parent_acc, our_acc) == 0)
2514 modest_ui_actions_on_close_window (NULL, MODEST_WINDOW (user_data));
2519 on_mail_operation_started (ModestMailOperation *mail_op,
2522 ModestMsgViewWindow *self;
2523 ModestMailOperationTypeOperation op_type;
2525 ModestMsgViewWindowPrivate *priv;
2526 GObject *source = NULL;
2528 self = MODEST_MSG_VIEW_WINDOW (user_data);
2529 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
2530 op_type = modest_mail_operation_get_type_operation (mail_op);
2531 tmp = priv->progress_widgets;
2532 source = modest_mail_operation_get_source(mail_op);
2533 if (G_OBJECT (self) == source) {
2534 if (op_type == MODEST_MAIL_OPERATION_TYPE_RECEIVE ||
2535 op_type == MODEST_MAIL_OPERATION_TYPE_OPEN ||
2536 op_type == MODEST_MAIL_OPERATION_TYPE_DELETE) {
2537 set_progress_hint (self, TRUE);
2539 modest_progress_object_add_operation (
2540 MODEST_PROGRESS_OBJECT (tmp->data),
2542 tmp = g_slist_next (tmp);
2546 g_object_unref (source);
2548 /* Update dimming rules */
2549 check_dimming_rules_after_change (self);
2553 on_mail_operation_finished (ModestMailOperation *mail_op,
2556 ModestMsgViewWindow *self;
2557 ModestMailOperationTypeOperation op_type;
2559 ModestMsgViewWindowPrivate *priv;
2561 self = MODEST_MSG_VIEW_WINDOW (user_data);
2562 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
2563 op_type = modest_mail_operation_get_type_operation (mail_op);
2564 tmp = priv->progress_widgets;
2566 if (op_type == MODEST_MAIL_OPERATION_TYPE_RECEIVE ||
2567 op_type == MODEST_MAIL_OPERATION_TYPE_OPEN ||
2568 op_type == MODEST_MAIL_OPERATION_TYPE_DELETE) {
2570 modest_progress_object_remove_operation (MODEST_PROGRESS_OBJECT (tmp->data),
2572 tmp = g_slist_next (tmp);
2575 /* If no more operations are being observed, NORMAL mode is enabled again */
2576 if (observers_empty (self)) {
2577 set_progress_hint (self, FALSE);
2581 /* Update dimming rules. We have to do this right here
2582 and not in view_msg_cb because at that point the
2583 transfer mode is still enabled so the dimming rule
2584 won't let the user delete the message that has been
2585 readed for example */
2586 check_dimming_rules_after_change (self);
2590 on_queue_changed (ModestMailOperationQueue *queue,
2591 ModestMailOperation *mail_op,
2592 ModestMailOperationQueueNotification type,
2593 ModestMsgViewWindow *self)
2595 ModestMsgViewWindowPrivate *priv;
2597 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
2599 /* If this operations was created by another window, do nothing */
2600 if (!modest_mail_operation_is_mine (mail_op, G_OBJECT(self)))
2603 if (type == MODEST_MAIL_OPERATION_QUEUE_OPERATION_ADDED) {
2604 priv->sighandlers = modest_signal_mgr_connect (priv->sighandlers,
2606 "operation-started",
2607 G_CALLBACK (on_mail_operation_started),
2609 priv->sighandlers = modest_signal_mgr_connect (priv->sighandlers,
2611 "operation-finished",
2612 G_CALLBACK (on_mail_operation_finished),
2614 } else if (type == MODEST_MAIL_OPERATION_QUEUE_OPERATION_REMOVED) {
2615 priv->sighandlers = modest_signal_mgr_disconnect (priv->sighandlers,
2617 "operation-started");
2618 priv->sighandlers = modest_signal_mgr_disconnect (priv->sighandlers,
2620 "operation-finished");
2625 modest_msg_view_window_get_attachments (ModestMsgViewWindow *win)
2627 ModestMsgViewWindowPrivate *priv;
2628 TnyList *selected_attachments = NULL;
2630 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (win), NULL);
2631 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (win);
2633 /* In Hildon 2.2 as there's no selection we assume we have all attachments selected */
2634 selected_attachments = modest_msg_view_get_attachments (MODEST_MSG_VIEW (priv->msg_view));
2636 return selected_attachments;
2640 ModestMsgViewWindow *self;
2642 gchar *attachment_uid;
2643 } DecodeAsyncHelper;
2646 on_decode_to_stream_async_handler (TnyMimePart *mime_part,
2652 DecodeAsyncHelper *helper = (DecodeAsyncHelper *) user_data;
2653 const gchar *content_type;
2655 if (cancelled || err) {
2658 if ((err->domain == TNY_ERROR_DOMAIN) &&
2659 (err->code == TNY_IO_ERROR_WRITE) &&
2660 (errno == ENOSPC)) {
2661 msg = g_strdup_printf (_KR("cerm_device_memory_full"), "");
2663 msg = g_strdup (_("mail_ib_file_operation_failed"));
2665 modest_platform_information_banner (NULL, NULL, msg);
2671 /* It could happen that the window was closed. So we
2672 assume it is a cancelation */
2673 if (!GTK_WIDGET_VISIBLE (helper->self))
2676 /* Remove the progress hint */
2677 set_progress_hint (helper->self, FALSE);
2679 content_type = tny_mime_part_get_content_type (mime_part);
2680 if (g_str_has_prefix (content_type, "message/rfc822")) {
2681 ModestWindowMgr *mgr;
2682 ModestWindow *msg_win = NULL;
2685 const gchar *mailbox;
2686 TnyStream *file_stream;
2689 fd = g_open (helper->file_path, O_RDONLY, 0644);
2691 file_stream = tny_fs_stream_new (fd);
2693 mgr = modest_runtime_get_window_mgr ();
2695 account = g_strdup (modest_window_get_active_account (MODEST_WINDOW (helper->self)));
2696 mailbox = modest_window_get_active_mailbox (MODEST_WINDOW (helper->self));
2699 account = modest_account_mgr_get_default_account (modest_runtime_get_account_mgr ());
2701 msg = tny_camel_msg_new ();
2702 tny_camel_msg_parse (msg, file_stream);
2703 msg_win = modest_msg_view_window_new_for_attachment (TNY_MSG (msg), account, mailbox, helper->attachment_uid);
2704 modest_window_set_zoom (MODEST_WINDOW (msg_win),
2705 modest_window_get_zoom (MODEST_WINDOW (helper->self)));
2706 if (modest_window_mgr_register_window (mgr, msg_win, MODEST_WINDOW (helper->self)))
2707 gtk_widget_show_all (GTK_WIDGET (msg_win));
2709 gtk_widget_destroy (GTK_WIDGET (msg_win));
2710 g_object_unref (msg);
2711 g_object_unref (file_stream);
2713 modest_platform_information_banner (NULL, NULL, _("mail_ib_file_operation_failed"));
2718 /* make the file read-only */
2719 g_chmod(helper->file_path, 0444);
2721 /* Activate the file */
2722 modest_platform_activate_file (helper->file_path, tny_mime_part_get_content_type (mime_part));
2727 g_object_unref (helper->self);
2728 g_free (helper->file_path);
2729 g_free (helper->attachment_uid);
2730 g_slice_free (DecodeAsyncHelper, helper);
2734 modest_msg_view_window_view_attachment (ModestMsgViewWindow *window,
2735 TnyMimePart *mime_part)
2737 ModestMsgViewWindowPrivate *priv;
2738 const gchar *msg_uid;
2739 gchar *attachment_uid = NULL;
2740 gint attachment_index = 0;
2741 TnyList *attachments;
2743 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window));
2744 g_return_if_fail (TNY_IS_MIME_PART (mime_part) || (mime_part == NULL));
2745 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
2747 msg_uid = modest_msg_view_window_get_message_uid (MODEST_MSG_VIEW_WINDOW (window));
2748 attachments = modest_msg_view_get_attachments (MODEST_MSG_VIEW (priv->msg_view));
2749 attachment_index = modest_list_index (attachments, (GObject *) mime_part);
2750 g_object_unref (attachments);
2752 if (msg_uid && attachment_index >= 0) {
2753 attachment_uid = g_strdup_printf ("%s/%d", msg_uid, attachment_index);
2756 if (mime_part == NULL) {
2757 gboolean error = FALSE;
2758 TnyList *selected_attachments = modest_msg_view_get_selected_attachments (MODEST_MSG_VIEW (priv->msg_view));
2759 if (selected_attachments == NULL || tny_list_get_length (selected_attachments) == 0) {
2761 } else if (tny_list_get_length (selected_attachments) > 1) {
2762 modest_platform_system_banner (NULL, NULL, _("mcen_ib_unable_to_display_more"));
2766 iter = tny_list_create_iterator (selected_attachments);
2767 mime_part = (TnyMimePart *) tny_iterator_get_current (iter);
2768 g_object_unref (iter);
2770 if (selected_attachments)
2771 g_object_unref (selected_attachments);
2776 g_object_ref (mime_part);
2779 if (tny_mime_part_is_purged (mime_part))
2782 if (!modest_tny_mime_part_is_msg (mime_part) && tny_mime_part_get_filename (mime_part)) {
2783 gchar *filepath = NULL;
2784 const gchar *att_filename = tny_mime_part_get_filename (mime_part);
2785 gboolean show_error_banner = FALSE;
2786 TnyFsStream *temp_stream = NULL;
2787 temp_stream = modest_utils_create_temp_stream (att_filename, attachment_uid,
2790 if (temp_stream != NULL) {
2791 ModestAccountMgr *mgr;
2792 DecodeAsyncHelper *helper;
2793 gboolean decode_in_provider;
2794 ModestProtocol *protocol;
2795 const gchar *account;
2797 /* Activate progress hint */
2798 set_progress_hint (window, TRUE);
2800 helper = g_slice_new0 (DecodeAsyncHelper);
2801 helper->self = g_object_ref (window);
2802 helper->file_path = g_strdup (filepath);
2803 helper->attachment_uid = g_strdup (attachment_uid);
2805 decode_in_provider = FALSE;
2806 mgr = modest_runtime_get_account_mgr ();
2807 account = modest_window_get_active_account (MODEST_WINDOW (window));
2808 if (modest_account_mgr_account_is_multimailbox (mgr, account, &protocol)) {
2809 if (MODEST_IS_ACCOUNT_PROTOCOL (protocol)) {
2811 uri = g_strconcat ("file://", filepath, NULL);
2812 decode_in_provider =
2813 modest_account_protocol_decode_part_to_stream_async (
2814 MODEST_ACCOUNT_PROTOCOL (protocol),
2817 TNY_STREAM (temp_stream),
2818 on_decode_to_stream_async_handler,
2825 if (!decode_in_provider)
2826 tny_mime_part_decode_to_stream_async (mime_part, TNY_STREAM (temp_stream),
2827 on_decode_to_stream_async_handler,
2830 g_object_unref (temp_stream);
2831 /* NOTE: files in the temporary area will be automatically
2832 * cleaned after some time if they are no longer in use */
2835 const gchar *content_type;
2836 /* the file may already exist but it isn't writable,
2837 * let's try to open it anyway */
2838 content_type = tny_mime_part_get_content_type (mime_part);
2839 modest_platform_activate_file (filepath, content_type);
2841 g_warning ("%s: modest_utils_create_temp_stream failed", __FUNCTION__);
2842 show_error_banner = TRUE;
2847 if (show_error_banner)
2848 modest_platform_information_banner (NULL, NULL, _("mail_ib_file_operation_failed"));
2849 } else if (!modest_tny_mime_part_is_msg (mime_part)) {
2850 ModestWindowMgr *mgr;
2851 ModestWindow *msg_win = NULL;
2852 TnyMsg *current_msg;
2856 current_msg = modest_msg_view_window_get_message (MODEST_MSG_VIEW_WINDOW (window));
2857 mgr = modest_runtime_get_window_mgr ();
2858 header = tny_msg_get_header (TNY_MSG (current_msg));
2859 found = modest_window_mgr_find_registered_message_uid (mgr,
2864 g_debug ("window for this body is already being created");
2867 /* it's not found, so create a new window for it */
2868 modest_window_mgr_register_header (mgr, header, attachment_uid); /* register the uid before building the window */
2869 gchar *account = g_strdup (modest_window_get_active_account (MODEST_WINDOW (window)));
2870 const gchar *mailbox = modest_window_get_active_mailbox (MODEST_WINDOW (window));
2872 account = modest_account_mgr_get_default_account (modest_runtime_get_account_mgr ());
2874 msg_win = modest_msg_view_window_new_with_other_body (TNY_MSG (current_msg), TNY_MIME_PART (mime_part),
2875 account, mailbox, attachment_uid);
2877 modest_window_set_zoom (MODEST_WINDOW (msg_win),
2878 modest_window_get_zoom (MODEST_WINDOW (window)));
2879 if (modest_window_mgr_register_window (mgr, msg_win, MODEST_WINDOW (window)))
2880 gtk_widget_show_all (GTK_WIDGET (msg_win));
2882 gtk_widget_destroy (GTK_WIDGET (msg_win));
2884 g_object_unref (current_msg);
2886 /* message attachment */
2887 TnyHeader *header = NULL;
2888 ModestWindowMgr *mgr;
2889 ModestWindow *msg_win = NULL;
2892 header = tny_msg_get_header (TNY_MSG (mime_part));
2893 mgr = modest_runtime_get_window_mgr ();
2894 found = modest_window_mgr_find_registered_header (mgr, header, &msg_win);
2897 /* if it's found, but there is no msg_win, it's probably in the process of being created;
2898 * thus, we don't do anything */
2899 g_debug ("window for is already being created");
2901 /* it's not found, so create a new window for it */
2902 modest_window_mgr_register_header (mgr, header, attachment_uid); /* register the uid before building the window */
2903 gchar *account = g_strdup (modest_window_get_active_account (MODEST_WINDOW (window)));
2904 const gchar *mailbox = modest_window_get_active_mailbox (MODEST_WINDOW (window));
2906 account = modest_account_mgr_get_default_account (modest_runtime_get_account_mgr ());
2907 msg_win = modest_msg_view_window_new_for_attachment (TNY_MSG (mime_part), account,
2908 mailbox, attachment_uid);
2909 modest_window_set_zoom (MODEST_WINDOW (msg_win),
2910 modest_window_get_zoom (MODEST_WINDOW (window)));
2911 if (modest_window_mgr_register_window (mgr, msg_win, MODEST_WINDOW (window)))
2912 gtk_widget_show_all (GTK_WIDGET (msg_win));
2914 gtk_widget_destroy (GTK_WIDGET (msg_win));
2920 g_free (attachment_uid);
2922 g_object_unref (mime_part);
2934 GnomeVFSResult result;
2936 ModestMsgViewWindow *window;
2939 static void save_mime_part_info_free (SaveMimePartInfo *info, gboolean with_struct);
2940 static gboolean idle_save_mime_part_show_result (SaveMimePartInfo *info);
2941 static gpointer save_mime_part_to_file (SaveMimePartInfo *info);
2942 static void save_mime_parts_to_file_with_checks (GtkWindow *parent, SaveMimePartInfo *info);
2945 save_mime_part_info_free (SaveMimePartInfo *info, gboolean with_struct)
2949 for (node = info->pairs; node != NULL; node = g_list_next (node)) {
2950 SaveMimePartPair *pair = (SaveMimePartPair *) node->data;
2951 g_free (pair->filename);
2952 g_object_unref (pair->part);
2953 g_slice_free (SaveMimePartPair, pair);
2955 g_list_free (info->pairs);
2958 g_object_unref (info->window);
2959 info->window = NULL;
2961 g_slice_free (SaveMimePartInfo, info);
2966 idle_save_mime_part_show_result (SaveMimePartInfo *info)
2968 /* This is a GDK lock because we are an idle callback and
2969 * modest_platform_system_banner is or does Gtk+ code */
2971 gdk_threads_enter (); /* CHECKED */
2972 if (info->result == GNOME_VFS_OK) {
2973 modest_platform_system_banner (NULL, NULL, _CS("sfil_ib_saved"));
2974 } else if (info->result == GNOME_VFS_ERROR_NO_SPACE) {
2977 /* Check if the uri belongs to the external mmc */
2978 if (g_str_has_prefix (info->uri, g_getenv (MODEST_MMC1_VOLUMEPATH_ENV)))
2979 msg = g_strdup_printf (_KR("cerm_device_memory_full"), "");
2981 msg = g_strdup (_KR("cerm_memory_card_full"));
2982 modest_platform_information_banner (NULL, NULL, msg);
2985 modest_platform_system_banner (NULL, NULL, _("mail_ib_file_operation_failed"));
2987 save_mime_part_info_free (info, FALSE);
2988 gdk_threads_leave (); /* CHECKED */
2994 save_mime_part_to_file (SaveMimePartInfo *info)
2996 GnomeVFSHandle *handle;
2998 SaveMimePartPair *pair = (SaveMimePartPair *) info->pairs->data;
3000 info->result = gnome_vfs_create (&handle, pair->filename, GNOME_VFS_OPEN_WRITE, FALSE, 0644);
3001 if (info->result == GNOME_VFS_OK) {
3002 GError *error = NULL;
3003 gboolean decode_in_provider;
3005 ModestAccountMgr *mgr;
3006 const gchar *account;
3007 ModestProtocol *protocol = NULL;
3009 stream = tny_vfs_stream_new (handle);
3011 decode_in_provider = FALSE;
3012 mgr = modest_runtime_get_account_mgr ();
3013 account = modest_window_get_active_account (MODEST_WINDOW (info->window));
3014 if (modest_account_mgr_account_is_multimailbox (mgr, account, &protocol)) {
3015 if (MODEST_IS_ACCOUNT_PROTOCOL (protocol)) {
3016 decode_in_provider =
3017 modest_account_protocol_decode_part_to_stream (
3018 MODEST_ACCOUNT_PROTOCOL (protocol),
3026 if (!decode_in_provider)
3027 written = tny_mime_part_decode_to_stream (pair->part, stream, &error);
3030 g_warning ("modest: could not save attachment %s: %d (%s)\n", pair->filename, error?error->code:-1, error?error->message:"Unknown error");
3032 if ((error->domain == TNY_ERROR_DOMAIN) &&
3033 (error->code == TNY_IO_ERROR_WRITE) &&
3034 (errno == ENOSPC)) {
3035 info->result = GNOME_VFS_ERROR_NO_SPACE;
3037 info->result = GNOME_VFS_ERROR_IO;
3040 g_object_unref (G_OBJECT (stream));
3042 g_warning ("Could not create save attachment %s: %s\n",
3043 pair->filename, gnome_vfs_result_to_string (info->result));
3046 /* Go on saving remaining files */
3047 info->pairs = g_list_remove_link (info->pairs, info->pairs);
3048 if (info->pairs != NULL) {
3049 save_mime_part_to_file (info);
3051 g_idle_add ((GSourceFunc) idle_save_mime_part_show_result, info);
3058 save_mime_parts_to_file_with_checks (GtkWindow *parent,
3059 SaveMimePartInfo *info)
3061 gboolean is_ok = TRUE;
3062 gint replaced_files = 0;
3063 const GList *files = info->pairs;
3064 const GList *iter, *to_replace = NULL;
3066 for (iter = files; (iter != NULL) && (replaced_files < 2); iter = g_list_next(iter)) {
3067 SaveMimePartPair *pair = iter->data;
3068 gchar *unescaped = g_uri_unescape_string (pair->filename, NULL);
3070 if (modest_utils_file_exists (unescaped)) {
3072 if (replaced_files == 1)
3077 if (replaced_files) {
3080 if (replaced_files == 1) {
3081 SaveMimePartPair *pair = to_replace->data;
3082 const gchar *basename = strrchr (pair->filename, G_DIR_SEPARATOR) + 1;
3083 gchar *escaped_basename, *message;
3085 escaped_basename = g_uri_unescape_string (basename, NULL);
3086 message = g_strdup_printf ("%s\n%s",
3087 _FM("docm_nc_replace_file"),
3088 (escaped_basename) ? escaped_basename : "");
3089 response = modest_platform_run_confirmation_dialog (parent, message);
3091 g_free (escaped_basename);
3093 response = modest_platform_run_confirmation_dialog (parent,
3094 _FM("docm_nc_replace_multiple"));
3096 if (response != GTK_RESPONSE_OK)
3101 save_mime_part_info_free (info, TRUE);
3103 g_thread_create ((GThreadFunc)save_mime_part_to_file, info, FALSE, NULL);
3108 typedef struct _SaveAttachmentsInfo {
3109 TnyList *attachments_list;
3110 ModestMsgViewWindow *window;
3111 } SaveAttachmentsInfo;
3114 save_attachments_response (GtkDialog *dialog,
3118 TnyList *mime_parts;
3120 GList *files_to_save = NULL;
3121 gchar *current_folder;
3122 SaveAttachmentsInfo *sa_info = (SaveAttachmentsInfo *) user_data;
3124 mime_parts = TNY_LIST (sa_info->attachments_list);
3126 if (arg1 != GTK_RESPONSE_OK)
3129 chooser_uri = gtk_file_chooser_get_uri (GTK_FILE_CHOOSER (dialog));
3130 current_folder = gtk_file_chooser_get_current_folder_uri (GTK_FILE_CHOOSER (dialog));
3131 if (current_folder && *current_folder != '\0') {
3133 modest_conf_set_string (modest_runtime_get_conf (), MODEST_CONF_LATEST_SAVE_ATTACHMENT_PATH,
3134 current_folder,&err);
3136 g_debug ("Error storing latest used folder: %s", err->message);
3140 g_free (current_folder);
3142 if (!modest_utils_folder_writable (chooser_uri)) {
3143 const gchar *err_msg;
3145 #ifdef MODEST_PLATFORM_MAEMO
3146 if (modest_maemo_utils_in_usb_mode ()) {
3147 err_msg = dgettext ("hildon-status-bar-usb", "usbh_ib_mmc_usb_connected");
3149 err_msg = _FM("sfil_ib_readonly_location");
3152 err_msg = _FM("sfil_ib_readonly_location");
3154 modest_platform_system_banner (NULL, NULL, err_msg);
3158 iter = tny_list_create_iterator (mime_parts);
3159 while (!tny_iterator_is_done (iter)) {
3160 TnyMimePart *mime_part = (TnyMimePart *) tny_iterator_get_current (iter);
3162 if ((modest_tny_mime_part_is_attachment_for_modest (mime_part)) &&
3163 !tny_mime_part_is_purged (mime_part) &&
3164 (tny_mime_part_get_filename (mime_part) != NULL)) {
3165 SaveMimePartPair *pair;
3167 pair = g_slice_new0 (SaveMimePartPair);
3169 if (tny_list_get_length (mime_parts) > 1) {
3171 gnome_vfs_escape_slashes (tny_mime_part_get_filename (mime_part));
3172 pair->filename = g_build_filename (chooser_uri, escaped, NULL);
3175 pair->filename = g_strdup (chooser_uri);
3177 pair->part = mime_part;
3178 files_to_save = g_list_prepend (files_to_save, pair);
3180 tny_iterator_next (iter);
3182 g_object_unref (iter);
3185 if (files_to_save != NULL) {
3186 SaveMimePartInfo *info = g_slice_new0 (SaveMimePartInfo);
3187 info->pairs = files_to_save;
3188 info->result = TRUE;
3189 info->uri = g_strdup (chooser_uri);
3190 info->window = g_object_ref (sa_info->window);
3191 save_mime_parts_to_file_with_checks ((GtkWindow *) dialog, info);
3193 g_free (chooser_uri);
3196 /* Free and close the dialog */
3197 g_object_unref (mime_parts);
3198 g_object_unref (sa_info->window);
3199 g_slice_free (SaveAttachmentsInfo, sa_info);
3200 gtk_widget_destroy (GTK_WIDGET (dialog));
3204 modest_msg_view_window_save_attachments (ModestMsgViewWindow *window,
3205 TnyList *mime_parts)
3207 ModestMsgViewWindowPrivate *priv;
3208 GtkWidget *save_dialog = NULL;
3209 gchar *conf_folder = NULL;
3210 gchar *filename = NULL;
3211 gchar *save_multiple_str = NULL;
3212 const gchar *root_folder = "file:///";
3214 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window));
3215 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
3217 if (mime_parts == NULL) {
3218 /* In Hildon 2.2 save and delete operate over all the attachments as there's no
3219 * selection available */
3220 mime_parts = modest_msg_view_get_attachments (MODEST_MSG_VIEW (priv->msg_view));
3221 if (mime_parts && !modest_toolkit_utils_select_attachments (GTK_WINDOW (window), mime_parts, FALSE)) {
3222 g_object_unref (mime_parts);
3225 if (mime_parts == NULL || tny_list_get_length (mime_parts) == 0) {
3227 g_object_unref (mime_parts);
3233 g_object_ref (mime_parts);
3236 /* prepare dialog */
3237 if (tny_list_get_length (mime_parts) == 1) {
3239 /* only one attachment selected */
3240 iter = tny_list_create_iterator (mime_parts);
3241 TnyMimePart *mime_part = (TnyMimePart *) tny_iterator_get_current (iter);
3242 g_object_unref (iter);
3243 if (!modest_tny_mime_part_is_msg (mime_part) &&
3244 modest_tny_mime_part_is_attachment_for_modest (mime_part) &&
3245 !tny_mime_part_is_purged (mime_part)) {
3246 filename = g_strdup (tny_mime_part_get_filename (mime_part));
3248 /* TODO: show any error? */
3249 g_warning ("%s: Tried to save a non-file attachment", __FUNCTION__);
3250 g_object_unref (mime_parts);
3253 g_object_unref (mime_part);
3255 gint num = tny_list_get_length (mime_parts);
3256 save_multiple_str = g_strdup_printf (dngettext("hildon-fm",
3257 "sfil_va_number_of_objects_attachment",
3258 "sfil_va_number_of_objects_attachments",
3262 /* Creation of hildon file chooser dialog for saving */
3263 save_dialog = modest_toolkit_factory_create_file_chooser_dialog (modest_runtime_get_toolkit_factory (),
3265 (GtkWindow *) window,
3266 GTK_FILE_CHOOSER_ACTION_SAVE);
3268 /* Get last used folder */
3269 conf_folder = modest_conf_get_string (modest_runtime_get_conf (),
3270 MODEST_CONF_LATEST_SAVE_ATTACHMENT_PATH, NULL);
3272 /* File chooser stops working if we select "file:///" as current folder */
3273 if (conf_folder && g_ascii_strcasecmp (root_folder, conf_folder) != 0) {
3274 g_free (conf_folder);
3278 if (conf_folder && conf_folder[0] != '\0') {
3279 gtk_file_chooser_set_current_folder_uri (GTK_FILE_CHOOSER (save_dialog), conf_folder);
3282 /* Set the default folder to documents folder */
3283 docs_folder = (gchar *) g_strdup(g_get_user_special_dir (G_USER_DIRECTORY_DOCUMENTS));
3286 docs_folder = g_build_filename (g_getenv (MYDOCS_ENV), DOCS_FOLDER, NULL);
3288 gtk_file_chooser_set_current_folder (GTK_FILE_CHOOSER (save_dialog), docs_folder);
3289 g_free (docs_folder);
3291 g_free (conf_folder);
3295 gtk_file_chooser_set_current_name (GTK_FILE_CHOOSER (save_dialog),
3300 /* if multiple, set multiple string */
3301 if (save_multiple_str) {
3302 g_object_set (G_OBJECT (save_dialog), "save-multiple", save_multiple_str, NULL);
3303 gtk_window_set_title (GTK_WINDOW (save_dialog), _FM("sfil_ti_save_objects_files"));
3304 g_free (save_multiple_str);
3307 /* We must run this asynchronously, because the hildon dialog
3308 performs a gtk_dialog_run by itself which leads to gdk
3310 SaveAttachmentsInfo *sa_info;
3311 sa_info = g_slice_new (SaveAttachmentsInfo);
3312 sa_info->attachments_list = mime_parts;
3313 sa_info->window = g_object_ref (window);
3314 g_signal_connect (save_dialog, "response",
3315 G_CALLBACK (save_attachments_response), sa_info);
3317 gtk_widget_show_all (save_dialog);
3321 show_remove_attachment_information (gpointer userdata)
3323 ModestMsgViewWindow *window = (ModestMsgViewWindow *) userdata;
3324 ModestMsgViewWindowPrivate *priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
3326 /* We're outside the main lock */
3327 gdk_threads_enter ();
3329 if (priv->remove_attachment_banner != NULL) {
3330 gtk_widget_destroy (priv->remove_attachment_banner);
3331 g_object_unref (priv->remove_attachment_banner);
3334 priv->remove_attachment_banner = g_object_ref (
3335 modest_platform_animation_banner (NULL, NULL, _("mcen_me_inbox_remove_attachments")));
3337 gdk_threads_leave ();
3343 modest_msg_view_window_remove_attachments (ModestMsgViewWindow *window, gboolean get_all)
3345 ModestMsgViewWindowPrivate *priv;
3346 TnyList *mime_parts = NULL, *tmp;
3347 gchar *confirmation_message;
3353 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window));
3354 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
3356 /* In hildon 2.2 we ignore the get_all flag as we always get all attachments. This is
3357 * because we don't have selection
3359 mime_parts = modest_msg_view_get_attachments (MODEST_MSG_VIEW (priv->msg_view));
3361 /* Remove already purged messages from mime parts list. We use
3362 a copy of the list to remove items in the original one */
3363 tmp = tny_list_copy (mime_parts);
3364 iter = tny_list_create_iterator (tmp);
3365 while (!tny_iterator_is_done (iter)) {
3366 TnyMimePart *part = TNY_MIME_PART (tny_iterator_get_current (iter));
3367 if (tny_mime_part_is_purged (part))
3368 tny_list_remove (mime_parts, (GObject *) part);
3370 g_object_unref (part);
3371 tny_iterator_next (iter);
3373 g_object_unref (tmp);
3374 g_object_unref (iter);
3376 if (!modest_toolkit_utils_select_attachments (GTK_WINDOW (window), mime_parts, TRUE) ||
3377 tny_list_get_length (mime_parts) == 0) {
3378 g_object_unref (mime_parts);
3382 n_attachments = tny_list_get_length (mime_parts);
3383 if (n_attachments == 1) {
3387 iter = tny_list_create_iterator (mime_parts);
3388 part = (TnyMimePart *) tny_iterator_get_current (iter);
3389 g_object_unref (iter);
3390 if (modest_tny_mime_part_is_msg (part)) {
3392 header = tny_msg_get_header (TNY_MSG (part));
3393 filename = tny_header_dup_subject (header);
3394 g_object_unref (header);
3395 if (filename == NULL)
3396 filename = g_strdup (_("mail_va_no_subject"));
3398 filename = g_strdup (tny_mime_part_get_filename (TNY_MIME_PART (part)));
3400 confirmation_message = g_strdup_printf (_("mcen_nc_purge_file_text"), filename);
3402 g_object_unref (part);
3404 confirmation_message = g_strdup_printf (ngettext("mcen_nc_purge_file_text",
3405 "mcen_nc_purge_files_text",
3406 n_attachments), n_attachments);
3408 response = modest_platform_run_confirmation_dialog (GTK_WINDOW (window),
3409 confirmation_message);
3410 g_free (confirmation_message);
3412 if (response != GTK_RESPONSE_OK) {
3413 g_object_unref (mime_parts);
3417 priv->purge_timeout = g_timeout_add (2000, show_remove_attachment_information, window);
3419 iter = tny_list_create_iterator (mime_parts);
3420 while (!tny_iterator_is_done (iter)) {
3423 part = (TnyMimePart *) tny_iterator_get_current (iter);
3424 tny_mime_part_set_purged (TNY_MIME_PART (part));
3425 g_object_unref (part);
3426 tny_iterator_next (iter);
3428 g_object_unref (iter);
3430 msg = tny_msg_view_get_msg (TNY_MSG_VIEW (priv->msg_view));
3431 tny_msg_view_clear (TNY_MSG_VIEW (priv->msg_view));
3432 tny_msg_rewrite_cache (msg);
3433 tny_msg_view_set_msg (TNY_MSG_VIEW (priv->msg_view), msg);
3434 g_object_unref (msg);
3435 update_branding (MODEST_MSG_VIEW_WINDOW (window));
3437 g_object_unref (mime_parts);
3439 if (priv->purge_timeout > 0) {
3440 g_source_remove (priv->purge_timeout);
3441 priv->purge_timeout = 0;
3444 if (priv->remove_attachment_banner) {
3445 gtk_widget_destroy (priv->remove_attachment_banner);
3446 g_object_unref (priv->remove_attachment_banner);
3447 priv->remove_attachment_banner = NULL;
3453 update_window_title (ModestMsgViewWindow *window)
3455 ModestMsgViewWindowPrivate *priv;
3457 TnyHeader *header = NULL;
3458 gchar *subject = NULL;
3460 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
3462 /* Note that if the window is closed while we're retrieving
3463 the message, this widget could de deleted */
3464 if (!priv->msg_view)
3467 msg = tny_msg_view_get_msg (TNY_MSG_VIEW (priv->msg_view));
3469 if (priv->other_body) {
3472 description = modest_tny_mime_part_get_header_value (priv->other_body, "Content-Description");
3474 g_strstrip (description);
3475 subject = description;
3477 } else if (msg != NULL) {
3478 header = tny_msg_get_header (msg);
3479 subject = tny_header_dup_subject (header);
3480 g_object_unref (header);
3481 g_object_unref (msg);
3484 if ((subject == NULL)||(subject[0] == '\0')) {
3486 subject = g_strdup (_("mail_va_no_subject"));
3489 modest_window_set_title (MODEST_WINDOW (window), subject);
3494 on_move_focus (GtkWidget *widget,
3495 GtkDirectionType direction,
3498 g_signal_stop_emission_by_name (G_OBJECT (widget), "move-focus");
3502 fetch_image_open_stream (TnyStreamCache *self, gint64 *expected_size, gchar *uri)
3504 GnomeVFSResult result;
3505 GnomeVFSHandle *handle = NULL;
3506 GnomeVFSFileInfo *info = NULL;
3509 result = gnome_vfs_open (&handle, uri, GNOME_VFS_OPEN_READ);
3510 if (result != GNOME_VFS_OK) {
3515 info = gnome_vfs_file_info_new ();
3516 result = gnome_vfs_get_file_info_from_handle (handle, info, GNOME_VFS_FILE_INFO_DEFAULT);
3517 if (result != GNOME_VFS_OK || ! (info->valid_fields & GNOME_VFS_FILE_INFO_FIELDS_SIZE)) {
3518 /* We put a "safe" default size for going to cache */
3519 *expected_size = (300*1024);
3521 *expected_size = info->size;
3523 gnome_vfs_file_info_unref (info);
3525 stream = tny_vfs_stream_new (handle);
3534 TnyStream *output_stream;
3535 GtkWidget *msg_view;
3540 on_fetch_image_idle_refresh_view (gpointer userdata)
3543 FetchImageData *fidata = (FetchImageData *) userdata;
3545 gdk_threads_enter ();
3546 if (GTK_WIDGET_DRAWABLE (fidata->msg_view)) {
3547 ModestMsgViewWindowPrivate *priv;
3549 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (fidata->window);
3550 priv->fetching_images--;
3551 gtk_widget_queue_draw (fidata->msg_view);
3552 update_progress_hint (MODEST_MSG_VIEW_WINDOW (fidata->window));
3554 gdk_threads_leave ();
3556 g_object_unref (fidata->msg_view);
3557 g_object_unref (fidata->window);
3558 g_slice_free (FetchImageData, fidata);
3563 on_fetch_image_thread (gpointer userdata)
3565 FetchImageData *fidata = (FetchImageData *) userdata;
3566 TnyStreamCache *cache;
3567 TnyStream *cache_stream;
3569 cache = modest_runtime_get_images_cache ();
3571 tny_stream_cache_get_stream (cache,
3573 (TnyStreamCacheOpenStreamFetcher) fetch_image_open_stream,
3574 (gpointer) fidata->uri);
3575 g_free (fidata->cache_id);
3576 g_free (fidata->uri);
3578 if (cache_stream != NULL) {
3581 while (G_LIKELY (!tny_stream_is_eos (cache_stream))) {
3584 nb_read = tny_stream_read (cache_stream, buffer, sizeof (buffer));
3585 if (G_UNLIKELY (nb_read < 0)) {
3587 } else if (G_LIKELY (nb_read > 0)) {
3588 gssize nb_written = 0;
3590 while (G_UNLIKELY (nb_written < nb_read)) {
3593 len = tny_stream_write (fidata->output_stream, buffer + nb_written,
3594 nb_read - nb_written);
3595 if (G_UNLIKELY (len < 0))
3601 tny_stream_close (cache_stream);
3602 g_object_unref (cache_stream);
3605 tny_stream_close (fidata->output_stream);
3606 g_object_unref (fidata->output_stream);
3608 g_idle_add (on_fetch_image_idle_refresh_view, fidata);
3614 on_fetch_image (ModestMsgView *msgview,
3617 ModestMsgViewWindow *window)
3619 const gchar *current_account;
3620 ModestMsgViewWindowPrivate *priv;
3621 FetchImageData *fidata;
3623 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
3625 current_account = modest_window_get_active_account (MODEST_WINDOW (window));
3627 fidata = g_slice_new0 (FetchImageData);
3628 fidata->msg_view = g_object_ref (msgview);
3629 fidata->window = g_object_ref (window);
3630 fidata->uri = g_strdup (uri);
3631 fidata->cache_id = modest_images_cache_get_id (current_account, uri);
3632 fidata->output_stream = g_object_ref (stream);
3634 priv->fetching_images++;
3635 if (g_thread_create (on_fetch_image_thread, fidata, FALSE, NULL) == NULL) {
3636 g_object_unref (fidata->output_stream);
3637 g_free (fidata->cache_id);
3638 g_free (fidata->uri);
3639 g_object_unref (fidata->msg_view);
3640 g_slice_free (FetchImageData, fidata);
3641 tny_stream_close (stream);
3642 priv->fetching_images--;
3643 update_progress_hint (window);
3646 update_progress_hint (window);
3652 setup_menu (ModestMsgViewWindow *self)
3654 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW(self));
3656 /* Settings menu buttons */
3657 modest_window_add_to_menu (MODEST_WINDOW (self), _("mcen_me_viewer_find"), NULL,
3658 MODEST_WINDOW_MENU_CALLBACK (modest_msg_view_window_show_isearch_toolbar),
3659 MODEST_DIMMING_CALLBACK (modest_ui_dimming_rules_on_find_in_msg));
3661 modest_window_add_to_menu (MODEST_WINDOW (self),
3662 dngettext(GETTEXT_PACKAGE,
3663 "mcen_me_move_message",
3664 "mcen_me_move_messages",
3667 MODEST_WINDOW_MENU_CALLBACK (modest_ui_actions_on_move_to),
3668 MODEST_DIMMING_CALLBACK (modest_ui_dimming_rules_on_move_to));
3670 modest_window_add_to_menu (MODEST_WINDOW (self), _("mcen_me_inbox_mark_as_read"), NULL,
3671 MODEST_WINDOW_MENU_CALLBACK (modest_ui_actions_on_mark_as_read),
3672 MODEST_DIMMING_CALLBACK (modest_ui_dimming_rules_on_mark_as_read_msg_in_view));
3674 modest_window_add_to_menu (MODEST_WINDOW (self), _("mcen_me_inbox_mark_as_unread"), NULL,
3675 MODEST_WINDOW_MENU_CALLBACK (modest_ui_actions_on_mark_as_unread),
3676 MODEST_DIMMING_CALLBACK (modest_ui_dimming_rules_on_mark_as_unread_msg_in_view));
3678 modest_window_add_to_menu (MODEST_WINDOW (self), _("mcen_me_viewer_save_attachments"), NULL,
3679 MODEST_WINDOW_MENU_CALLBACK (modest_ui_actions_save_attachments),
3680 MODEST_DIMMING_CALLBACK (modest_ui_dimming_rules_on_save_attachments));
3681 modest_window_add_to_menu (MODEST_WINDOW (self), _("mcen_me_inbox_remove_attachments"), NULL,
3682 MODEST_WINDOW_MENU_CALLBACK (modest_ui_actions_remove_attachments),
3683 MODEST_DIMMING_CALLBACK (modest_ui_dimming_rules_on_remove_attachments));
3685 modest_window_add_to_menu (MODEST_WINDOW (self), _("mcen_me_new_message"), "<Control>n",
3686 MODEST_WINDOW_MENU_CALLBACK (modest_ui_actions_on_new_msg),
3687 MODEST_DIMMING_CALLBACK (modest_ui_dimming_rules_on_new_msg));
3688 modest_window_add_to_menu (MODEST_WINDOW (self), _("mcen_me_viewer_addtocontacts"), NULL,
3689 MODEST_WINDOW_MENU_CALLBACK (modest_ui_actions_add_to_contacts),
3690 MODEST_DIMMING_CALLBACK (modest_ui_dimming_rules_on_add_to_contacts));
3692 modest_window_add_to_menu (MODEST_WINDOW (self), _("mcen_ti_message_properties"), NULL,
3693 MODEST_WINDOW_MENU_CALLBACK (modest_ui_actions_on_details),
3694 MODEST_DIMMING_CALLBACK (modest_ui_dimming_rules_on_details));
3698 modest_msg_view_window_add_to_contacts (ModestMsgViewWindow *self)
3700 ModestMsgViewWindowPrivate *priv;
3701 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
3702 GSList *recipients = NULL;
3705 msg = tny_msg_view_get_msg (TNY_MSG_VIEW (priv->msg_view));
3709 header = modest_msg_view_window_get_header (self);
3712 recipients = modest_tny_msg_header_get_all_recipients_list (header);
3713 g_object_unref (header);
3715 recipients = modest_tny_msg_get_all_recipients_list (msg);
3716 g_object_unref (msg);
3720 /* Offer the user to add recipients to the address book */
3721 modest_address_book_add_address_list_with_selector (recipients, (GtkWindow *) self);
3722 g_slist_foreach (recipients, (GFunc) g_free, NULL); g_slist_free (recipients);
3727 _modest_msg_view_window_map_event (GtkWidget *widget,
3731 ModestMsgViewWindow *self = (ModestMsgViewWindow *) userdata;
3733 update_progress_hint (self);
3739 modest_msg_view_window_fetch_images (ModestMsgViewWindow *self)
3741 ModestMsgViewWindowPrivate *priv;
3742 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
3744 modest_msg_view_request_fetch_images (MODEST_MSG_VIEW (priv->msg_view));
3748 modest_msg_view_window_has_blocked_external_images (ModestMsgViewWindow *self)
3750 ModestMsgViewWindowPrivate *priv;
3751 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
3753 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self), FALSE);
3755 return modest_msg_view_has_blocked_external_images (MODEST_MSG_VIEW (priv->msg_view));
3759 modest_msg_view_window_reload (ModestMsgViewWindow *self)
3761 ModestMsgViewWindowPrivate *priv;
3762 const gchar *msg_uid;
3763 TnyHeader *header = NULL;
3764 TnyFolder *folder = NULL;
3766 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self));
3768 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
3770 header = modest_msg_view_window_get_header (MODEST_MSG_VIEW_WINDOW (self));
3774 folder = tny_header_get_folder (header);
3775 g_object_unref (header);
3780 msg_uid = modest_msg_view_window_get_message_uid (self);
3782 GtkTreeRowReference *row_reference;
3784 if (priv->row_reference && gtk_tree_row_reference_valid (priv->row_reference)) {
3785 row_reference = priv->row_reference;
3787 row_reference = NULL;
3789 if (!message_reader (self, priv, NULL, msg_uid, folder, row_reference))
3790 g_warning ("Shouldn't happen, trying to reload a message failed");
3793 g_object_unref (folder);
3797 update_branding (ModestMsgViewWindow *self)
3799 const gchar *account;
3800 const gchar *mailbox;
3801 ModestAccountMgr *mgr;
3802 ModestProtocol *protocol = NULL;
3803 gchar *service_name = NULL;
3804 const GdkPixbuf *service_icon = NULL;
3805 ModestMsgViewWindowPrivate *priv;
3807 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
3809 account = modest_window_get_active_account (MODEST_WINDOW (self));
3810 mailbox = modest_window_get_active_mailbox (MODEST_WINDOW (self));
3812 mgr = modest_runtime_get_account_mgr ();
3814 if (modest_account_mgr_account_is_multimailbox (mgr, account, &protocol)) {
3815 if (MODEST_IS_ACCOUNT_PROTOCOL (protocol)) {
3816 service_name = modest_account_protocol_get_service_name (MODEST_ACCOUNT_PROTOCOL (protocol),
3818 service_icon = modest_account_protocol_get_service_icon (MODEST_ACCOUNT_PROTOCOL (protocol),
3819 account, mailbox, MODEST_ICON_SIZE_SMALL);
3823 modest_msg_view_set_branding (MODEST_MSG_VIEW (priv->msg_view), service_name, service_icon);
3824 g_free (service_name);
3828 sync_flags (ModestMsgViewWindow *self)
3830 TnyHeader *header = NULL;
3832 header = modest_msg_view_window_get_header (self);
3834 TnyMsg *msg = modest_msg_view_window_get_message (self);
3836 header = tny_msg_get_header (msg);
3837 g_object_unref (msg);
3842 TnyFolder *folder = tny_header_get_folder (header);
3845 ModestMailOperation *mail_op;
3847 /* Sync folder, we need this to save the seen flag */
3848 mail_op = modest_mail_operation_new (NULL);
3849 modest_mail_operation_queue_add (modest_runtime_get_mail_operation_queue (),
3851 modest_mail_operation_sync_folder (mail_op, folder, FALSE, NULL, NULL);
3852 g_object_unref (mail_op);
3853 g_object_unref (folder);
3855 g_object_unref (header);