1 /* Copyright (c) 2006, Nokia Corporation
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions are
8 * * Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 * * Redistributions in binary form must reproduce the above copyright
11 * notice, this list of conditions and the following disclaimer in the
12 * documentation and/or other materials provided with the distribution.
13 * * Neither the name of the Nokia Corporation nor the names of its
14 * contributors may be used to endorse or promote products derived from
15 * this software without specific prior written permission.
17 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
18 * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
19 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
20 * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
21 * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
22 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
23 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
24 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
25 * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
26 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
27 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29 #include <glib/gi18n.h>
31 #include <tny-account-store.h>
32 #include <tny-simple-list.h>
34 #include <tny-mime-part.h>
35 #include <tny-vfs-stream.h>
36 #include <tny-error.h>
37 #include "modest-marshal.h"
38 #include "modest-platform.h"
39 #include <modest-utils.h>
40 #include <modest-maemo-utils.h>
41 #include <modest-tny-msg.h>
42 #include <modest-msg-view-window.h>
43 #include "modest-msg-view-window-ui-dimming.h"
44 #include <modest-widget-memory.h>
45 #include <modest-progress-object.h>
46 #include <modest-runtime.h>
47 #include <modest-window-priv.h>
48 #include <modest-tny-folder.h>
49 #include <modest-text-utils.h>
50 #include <modest-account-mgr-helpers.h>
51 #include <modest-toolkit-factory.h>
52 #include <modest-scrollable.h>
53 #include <modest-isearch-toolbar.h>
54 #include <hildon/hildon-picker-dialog.h>
55 #include <hildon/hildon-app-menu.h>
56 #include "modest-defs.h"
57 #include "modest-hildon-includes.h"
58 #include "modest-ui-dimming-manager.h"
59 #include <gdk/gdkkeysyms.h>
60 #include <modest-tny-account.h>
61 #include <modest-mime-part-view.h>
62 #include <modest-isearch-view.h>
63 #include <modest-tny-mime-part.h>
64 #include <modest-address-book.h>
67 #include <glib/gstdio.h>
68 #include <modest-debug.h>
69 #include <modest-header-window.h>
70 #include <modest-account-protocol.h>
71 #include <modest-icon-names.h>
72 #include <modest-ui-actions.h>
73 #include <tny-camel-msg.h>
75 #define MYDOCS_ENV "MYDOCSDIR"
76 #define DOCS_FOLDER ".documents"
78 typedef struct _ModestMsgViewWindowPrivate ModestMsgViewWindowPrivate;
79 struct _ModestMsgViewWindowPrivate {
82 GtkWidget *main_scroll;
83 GtkWidget *isearch_toolbar;
86 /* Progress observers */
87 GSList *progress_widgets;
90 GtkWidget *prev_toolitem;
91 GtkWidget *next_toolitem;
92 gboolean progress_hint;
95 /* Optimized view enabled */
96 gboolean optimized_view;
98 /* Whether this was created via the *_new_for_search_result() function. */
99 gboolean is_search_result;
101 /* Whether the message is in outbox */
104 /* A reference to the @model of the header view
105 * to allow selecting previous/next messages,
106 * if the message is currently selected in the header view.
108 const gchar *header_folder_id;
109 GtkTreeModel *header_model;
110 GtkTreeRowReference *row_reference;
111 GtkTreeRowReference *next_row_reference;
113 gulong clipboard_change_handler;
114 gulong queue_change_handler;
115 gulong account_removed_handler;
116 gulong row_changed_handler;
117 gulong row_deleted_handler;
118 gulong row_inserted_handler;
119 gulong rows_reordered_handler;
122 GtkWidget *remove_attachment_banner;
125 TnyMimePart *other_body;
130 static void modest_msg_view_window_class_init (ModestMsgViewWindowClass *klass);
131 static void modest_msg_view_window_init (ModestMsgViewWindow *obj);
132 static void modest_header_view_observer_init (ModestHeaderViewObserverIface *iface_class);
133 static void modest_msg_view_window_finalize (GObject *obj);
134 static void modest_msg_view_window_show_isearch_toolbar (GtkWidget *obj, gpointer data);
135 static void modest_msg_view_window_isearch_toolbar_close (GtkWidget *widget,
136 ModestMsgViewWindow *obj);
137 static void modest_msg_view_window_isearch_toolbar_search (GtkWidget *widget,
138 ModestMsgViewWindow *obj);
139 static void modest_msg_view_window_toggle_isearch_toolbar (GtkWidget *obj,
141 static void modest_msg_view_window_disconnect_signals (ModestWindow *self);
143 static gdouble modest_msg_view_window_get_zoom (ModestWindow *window);
144 static void modest_msg_view_window_set_zoom (ModestWindow *window,
146 static gboolean modest_msg_view_window_zoom_minus (ModestWindow *window);
147 static gboolean modest_msg_view_window_zoom_plus (ModestWindow *window);
148 static gboolean modest_msg_view_window_key_event (GtkWidget *window,
151 static void modest_msg_view_window_update_priority (ModestMsgViewWindow *window);
153 static void modest_msg_view_window_show_toolbar (ModestWindow *window,
154 gboolean show_toolbar);
156 static void modest_msg_view_window_clipboard_owner_change (GtkClipboard *clipboard,
158 ModestMsgViewWindow *window);
160 static void modest_msg_view_window_on_row_changed (GtkTreeModel *header_model,
163 ModestMsgViewWindow *window);
165 static void modest_msg_view_window_on_row_deleted (GtkTreeModel *header_model,
167 ModestMsgViewWindow *window);
169 static void modest_msg_view_window_on_row_inserted (GtkTreeModel *header_model,
170 GtkTreePath *tree_path,
171 GtkTreeIter *tree_iter,
172 ModestMsgViewWindow *window);
174 static void modest_msg_view_window_on_row_reordered (GtkTreeModel *header_model,
178 ModestMsgViewWindow *window);
180 static void modest_msg_view_window_update_model_replaced (ModestHeaderViewObserver *window,
182 const gchar *tny_folder_id);
184 static void on_queue_changed (ModestMailOperationQueue *queue,
185 ModestMailOperation *mail_op,
186 ModestMailOperationQueueNotification type,
187 ModestMsgViewWindow *self);
189 static void on_account_removed (TnyAccountStore *account_store,
193 static void on_move_focus (GtkWidget *widget,
194 GtkDirectionType direction,
197 static void view_msg_cb (ModestMailOperation *mail_op,
204 static void set_progress_hint (ModestMsgViewWindow *self,
207 static void update_window_title (ModestMsgViewWindow *window);
209 static void init_window (ModestMsgViewWindow *obj);
211 static gboolean msg_is_visible (TnyHeader *header, gboolean check_outbox);
213 static void check_dimming_rules_after_change (ModestMsgViewWindow *window);
215 static gboolean on_fetch_image (ModestMsgView *msgview,
218 ModestMsgViewWindow *window);
220 static gboolean modest_msg_view_window_scroll_child (ModestMsgViewWindow *self,
221 GtkScrollType scroll_type,
224 static gboolean message_reader (ModestMsgViewWindow *window,
225 ModestMsgViewWindowPrivate *priv,
227 const gchar *msg_uid,
229 GtkTreeRowReference *row_reference);
231 static void setup_menu (ModestMsgViewWindow *self);
232 static gboolean _modest_msg_view_window_map_event (GtkWidget *widget,
235 static void update_branding (ModestMsgViewWindow *self);
236 static void sync_flags (ModestMsgViewWindow *self);
238 /* list my signals */
245 static const GtkActionEntry msg_view_toolbar_action_entries [] = {
248 { "ToolbarMessageReply", MODEST_STOCK_REPLY, N_("mcen_me_inbox_reply"), "<CTRL>R", NULL, G_CALLBACK (modest_ui_actions_on_reply) },
249 { "ToolbarMessageReplyAll", MODEST_STOCK_REPLY_ALL, N_("mcen_me_inbox_replytoall"), NULL, NULL, G_CALLBACK (modest_ui_actions_on_reply_all) },
250 { "ToolbarMessageForward", MODEST_STOCK_FORWARD, N_("mcen_me_inbox_forward"), NULL, NULL, G_CALLBACK (modest_ui_actions_on_forward) },
251 { "ToolbarDeleteMessage", MODEST_STOCK_DELETE, N_("qgn_toolb_gene_deletebutton"), NULL, NULL, G_CALLBACK (modest_ui_actions_on_delete_message_or_folder) },
252 { "ToolbarMessageBack", MODEST_TOOLBAR_ICON_PREV, N_("qgn_toolb_gene_back"), NULL, NULL, G_CALLBACK (modest_ui_actions_on_prev) },
253 { "ToolbarMessageNext", MODEST_TOOLBAR_ICON_NEXT, N_("qgn_toolb_gene_forward"), NULL, NULL, G_CALLBACK (modest_ui_actions_on_next) },
254 { "ToolbarDownloadExternalImages", MODEST_TOOLBAR_ICON_DOWNLOAD_IMAGES, N_("mail_bd_external_images"), NULL, NULL, G_CALLBACK (modest_ui_actions_on_fetch_images) },
257 static const GtkToggleActionEntry msg_view_toggle_action_entries [] = {
258 { "FindInMessage", MODEST_TOOLBAR_ICON_FIND, N_("qgn_toolb_gene_find"), "<CTRL>F", NULL, G_CALLBACK (modest_msg_view_window_toggle_isearch_toolbar), FALSE },
261 #define MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE((o), \
262 MODEST_TYPE_MSG_VIEW_WINDOW, \
263 ModestMsgViewWindowPrivate))
265 static GtkWindowClass *parent_class = NULL;
267 /* uncomment the following if you have defined any signals */
268 static guint signals[LAST_SIGNAL] = {0};
271 modest_msg_view_window_get_type (void)
273 static GType my_type = 0;
275 static const GTypeInfo my_info = {
276 sizeof(ModestMsgViewWindowClass),
277 NULL, /* base init */
278 NULL, /* base finalize */
279 (GClassInitFunc) modest_msg_view_window_class_init,
280 NULL, /* class finalize */
281 NULL, /* class data */
282 sizeof(ModestMsgViewWindow),
284 (GInstanceInitFunc) modest_msg_view_window_init,
287 #ifdef MODEST_TOOLKIT_GTK
288 my_type = g_type_register_static (MODEST_TYPE_WINDOW,
289 "ModestMsgViewWindow",
292 my_type = g_type_register_static (MODEST_TYPE_HILDON2_WINDOW,
293 "ModestMsgViewWindow",
297 static const GInterfaceInfo modest_header_view_observer_info =
299 (GInterfaceInitFunc) modest_header_view_observer_init,
300 NULL, /* interface_finalize */
301 NULL /* interface_data */
304 g_type_add_interface_static (my_type,
305 MODEST_TYPE_HEADER_VIEW_OBSERVER,
306 &modest_header_view_observer_info);
312 save_state (ModestWindow *self)
314 modest_widget_memory_save (modest_runtime_get_conf (),
316 MODEST_CONF_MSG_VIEW_WINDOW_KEY);
320 modest_msg_view_window_scroll_child (ModestMsgViewWindow *self,
321 GtkScrollType scroll_type,
325 ModestMsgViewWindowPrivate *priv;
328 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(self);
330 switch (scroll_type) {
331 case GTK_SCROLL_STEP_UP:
334 case GTK_SCROLL_STEP_DOWN:
337 case GTK_SCROLL_PAGE_UP:
340 case GTK_SCROLL_PAGE_DOWN:
343 case GTK_SCROLL_START:
354 modest_scrollable_scroll ((ModestScrollable *) priv->main_scroll, 0, step);
356 return (gboolean) step;
360 add_scroll_binding (GtkBindingSet *binding_set,
362 GtkScrollType scroll)
364 guint keypad_keyval = keyval - GDK_Left + GDK_KP_Left;
366 gtk_binding_entry_add_signal (binding_set, keyval, 0,
368 GTK_TYPE_SCROLL_TYPE, scroll,
369 G_TYPE_BOOLEAN, FALSE);
370 gtk_binding_entry_add_signal (binding_set, keypad_keyval, 0,
372 GTK_TYPE_SCROLL_TYPE, scroll,
373 G_TYPE_BOOLEAN, FALSE);
377 modest_msg_view_window_class_init (ModestMsgViewWindowClass *klass)
379 GObjectClass *gobject_class;
380 HildonWindowClass *hildon_window_class;
381 ModestWindowClass *modest_window_class;
382 GtkBindingSet *binding_set;
384 gobject_class = (GObjectClass*) klass;
385 hildon_window_class = (HildonWindowClass *) klass;
386 modest_window_class = (ModestWindowClass *) klass;
388 parent_class = g_type_class_peek_parent (klass);
389 gobject_class->finalize = modest_msg_view_window_finalize;
391 modest_window_class->set_zoom_func = modest_msg_view_window_set_zoom;
392 modest_window_class->get_zoom_func = modest_msg_view_window_get_zoom;
393 modest_window_class->zoom_plus_func = modest_msg_view_window_zoom_plus;
394 modest_window_class->zoom_minus_func = modest_msg_view_window_zoom_minus;
395 modest_window_class->show_toolbar_func = modest_msg_view_window_show_toolbar;
396 modest_window_class->disconnect_signals_func = modest_msg_view_window_disconnect_signals;
398 modest_window_class->save_state_func = save_state;
400 klass->scroll_child = modest_msg_view_window_scroll_child;
402 signals[MSG_CHANGED_SIGNAL] =
403 g_signal_new ("msg-changed",
404 G_TYPE_FROM_CLASS (gobject_class),
406 G_STRUCT_OFFSET (ModestMsgViewWindowClass, msg_changed),
408 modest_marshal_VOID__POINTER_POINTER,
409 G_TYPE_NONE, 2, G_TYPE_POINTER, G_TYPE_POINTER);
411 signals[SCROLL_CHILD_SIGNAL] =
412 g_signal_new ("scroll-child",
413 G_TYPE_FROM_CLASS (gobject_class),
414 G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
415 G_STRUCT_OFFSET (ModestMsgViewWindowClass, scroll_child),
417 modest_marshal_BOOLEAN__ENUM_BOOLEAN,
418 G_TYPE_BOOLEAN, 2, GTK_TYPE_SCROLL_TYPE, G_TYPE_BOOLEAN);
420 binding_set = gtk_binding_set_by_class (klass);
421 add_scroll_binding (binding_set, GDK_Up, GTK_SCROLL_STEP_UP);
422 add_scroll_binding (binding_set, GDK_Down, GTK_SCROLL_STEP_DOWN);
423 add_scroll_binding (binding_set, GDK_Page_Up, GTK_SCROLL_PAGE_UP);
424 add_scroll_binding (binding_set, GDK_Page_Down, GTK_SCROLL_PAGE_DOWN);
425 add_scroll_binding (binding_set, GDK_Home, GTK_SCROLL_START);
426 add_scroll_binding (binding_set, GDK_End, GTK_SCROLL_END);
428 g_type_class_add_private (gobject_class, sizeof(ModestMsgViewWindowPrivate));
432 static void modest_header_view_observer_init(
433 ModestHeaderViewObserverIface *iface_class)
435 iface_class->update_func = modest_msg_view_window_update_model_replaced;
439 modest_msg_view_window_init (ModestMsgViewWindow *obj)
441 ModestMsgViewWindowPrivate *priv;
442 ModestWindowPrivate *parent_priv = NULL;
443 GtkActionGroup *action_group = NULL;
444 GError *error = NULL;
446 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(obj);
447 parent_priv = MODEST_WINDOW_GET_PRIVATE(obj);
448 parent_priv->ui_manager = gtk_ui_manager_new();
450 action_group = gtk_action_group_new ("ModestMsgViewWindowActions");
451 gtk_action_group_set_translation_domain (action_group, GETTEXT_PACKAGE);
453 /* Add common actions */
454 gtk_action_group_add_actions (action_group,
455 msg_view_toolbar_action_entries,
456 G_N_ELEMENTS (msg_view_toolbar_action_entries),
458 gtk_action_group_add_toggle_actions (action_group,
459 msg_view_toggle_action_entries,
460 G_N_ELEMENTS (msg_view_toggle_action_entries),
463 gtk_ui_manager_insert_action_group (parent_priv->ui_manager, action_group, 0);
464 g_object_unref (action_group);
466 /* Load the UI definition */
467 gtk_ui_manager_add_ui_from_file (parent_priv->ui_manager, MODEST_UIDIR "modest-msg-view-window-ui.xml",
470 g_printerr ("modest: could not merge modest-msg-view-window-ui.xml: %s\n", error->message);
471 g_error_free (error);
476 /* Add accelerators */
477 gtk_window_add_accel_group (GTK_WINDOW (obj),
478 gtk_ui_manager_get_accel_group (parent_priv->ui_manager));
480 priv->is_search_result = FALSE;
481 priv->is_outbox = FALSE;
483 priv->msg_view = NULL;
484 priv->header_model = NULL;
485 priv->header_folder_id = NULL;
486 priv->clipboard_change_handler = 0;
487 priv->queue_change_handler = 0;
488 priv->account_removed_handler = 0;
489 priv->row_changed_handler = 0;
490 priv->row_deleted_handler = 0;
491 priv->row_inserted_handler = 0;
492 priv->rows_reordered_handler = 0;
493 priv->progress_hint = FALSE;
494 priv->fetching_images = 0;
496 priv->optimized_view = FALSE;
497 priv->purge_timeout = 0;
498 priv->remove_attachment_banner = NULL;
499 priv->msg_uid = NULL;
500 priv->other_body = NULL;
502 priv->sighandlers = NULL;
505 init_window (MODEST_MSG_VIEW_WINDOW(obj));
510 update_progress_hint (ModestMsgViewWindow *self)
512 ModestMsgViewWindowPrivate *priv;
513 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(self);
515 if (GTK_WIDGET_VISIBLE (self)) {
516 modest_window_show_progress (MODEST_WINDOW (self),
517 (priv->progress_hint || (priv->fetching_images > 0))?1:0);
522 set_progress_hint (ModestMsgViewWindow *self,
525 ModestWindowPrivate *parent_priv;
526 ModestMsgViewWindowPrivate *priv;
528 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self));
530 parent_priv = MODEST_WINDOW_GET_PRIVATE(self);
531 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(self);
533 /* Sets current progress hint */
534 priv->progress_hint = enabled;
536 update_progress_hint (self);
542 init_window (ModestMsgViewWindow *obj)
544 GtkWidget *main_vbox;
545 ModestMsgViewWindowPrivate *priv;
547 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(obj);
549 priv->msg_view = GTK_WIDGET (tny_platform_factory_new_msg_view (modest_tny_platform_factory_get_instance ()));
550 modest_msg_view_set_shadow_type (MODEST_MSG_VIEW (priv->msg_view), GTK_SHADOW_NONE);
551 main_vbox = gtk_vbox_new (FALSE, 6);
553 priv->main_scroll = modest_toolkit_factory_create_scrollable (modest_runtime_get_toolkit_factory ());
554 modest_scrollable_set_horizontal_policy (MODEST_SCROLLABLE (priv->main_scroll), GTK_POLICY_AUTOMATIC);
555 g_object_set (G_OBJECT (priv->main_scroll),
556 "movement-mode", MODEST_MOVEMENT_MODE_BOTH,
557 "horizontal-max-overshoot", 0,
559 gtk_container_add (GTK_CONTAINER (priv->main_scroll), priv->msg_view);
560 gtk_box_pack_start (GTK_BOX(main_vbox), priv->main_scroll, TRUE, TRUE, 0);
561 gtk_container_add (GTK_CONTAINER(obj), main_vbox);
563 /* NULL-ize fields if the window is destroyed */
564 g_signal_connect (priv->msg_view, "destroy", G_CALLBACK (gtk_widget_destroyed), &(priv->msg_view));
566 gtk_widget_show_all (GTK_WIDGET(main_vbox));
570 modest_msg_view_window_disconnect_signals (ModestWindow *self)
572 ModestMsgViewWindowPrivate *priv;
573 GtkWidget *header_view = NULL;
574 GtkWindow *parent_window = NULL;
576 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
578 if (gtk_clipboard_get (GDK_SELECTION_PRIMARY) &&
579 g_signal_handler_is_connected (gtk_clipboard_get (GDK_SELECTION_PRIMARY),
580 priv->clipboard_change_handler))
581 g_signal_handler_disconnect (gtk_clipboard_get (GDK_SELECTION_PRIMARY),
582 priv->clipboard_change_handler);
584 if (g_signal_handler_is_connected (G_OBJECT (modest_runtime_get_mail_operation_queue ()),
585 priv->queue_change_handler))
586 g_signal_handler_disconnect (G_OBJECT (modest_runtime_get_mail_operation_queue ()),
587 priv->queue_change_handler);
589 if (g_signal_handler_is_connected (G_OBJECT (modest_runtime_get_account_store ()),
590 priv->account_removed_handler))
591 g_signal_handler_disconnect (G_OBJECT (modest_runtime_get_account_store ()),
592 priv->account_removed_handler);
594 if (priv->header_model) {
595 if (g_signal_handler_is_connected(G_OBJECT (priv->header_model),
596 priv->row_changed_handler))
597 g_signal_handler_disconnect(G_OBJECT (priv->header_model),
598 priv->row_changed_handler);
600 if (g_signal_handler_is_connected(G_OBJECT (priv->header_model),
601 priv->row_deleted_handler))
602 g_signal_handler_disconnect(G_OBJECT (priv->header_model),
603 priv->row_deleted_handler);
605 if (g_signal_handler_is_connected(G_OBJECT (priv->header_model),
606 priv->row_inserted_handler))
607 g_signal_handler_disconnect(G_OBJECT (priv->header_model),
608 priv->row_inserted_handler);
610 if (g_signal_handler_is_connected(G_OBJECT (priv->header_model),
611 priv->rows_reordered_handler))
612 g_signal_handler_disconnect(G_OBJECT (priv->header_model),
613 priv->rows_reordered_handler);
616 modest_signal_mgr_disconnect_all_and_destroy (priv->sighandlers);
617 priv->sighandlers = NULL;
619 parent_window = gtk_window_get_transient_for (GTK_WINDOW (self));
620 if (parent_window && MODEST_IS_HEADER_WINDOW (parent_window)) {
621 header_view = GTK_WIDGET (modest_header_window_get_header_view (MODEST_HEADER_WINDOW (parent_window)));
623 modest_header_view_remove_observer(MODEST_HEADER_VIEW (header_view),
624 MODEST_HEADER_VIEW_OBSERVER(self));
630 modest_msg_view_window_finalize (GObject *obj)
632 ModestMsgViewWindowPrivate *priv;
634 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (obj);
636 /* Sanity check: shouldn't be needed, the window mgr should
637 call this function before */
638 modest_msg_view_window_disconnect_signals (MODEST_WINDOW (obj));
640 if (priv->other_body != NULL) {
641 g_object_unref (priv->other_body);
642 priv->other_body = NULL;
645 if (priv->header_model != NULL) {
646 g_object_unref (priv->header_model);
647 priv->header_model = NULL;
650 if (priv->remove_attachment_banner) {
651 gtk_widget_destroy (priv->remove_attachment_banner);
652 g_object_unref (priv->remove_attachment_banner);
653 priv->remove_attachment_banner = NULL;
656 if (priv->purge_timeout > 0) {
657 g_source_remove (priv->purge_timeout);
658 priv->purge_timeout = 0;
661 if (priv->row_reference) {
662 gtk_tree_row_reference_free (priv->row_reference);
663 priv->row_reference = NULL;
666 if (priv->next_row_reference) {
667 gtk_tree_row_reference_free (priv->next_row_reference);
668 priv->next_row_reference = NULL;
672 g_free (priv->msg_uid);
673 priv->msg_uid = NULL;
676 G_OBJECT_CLASS(parent_class)->finalize (obj);
680 select_next_valid_row (GtkTreeModel *model,
681 GtkTreeRowReference **row_reference,
685 GtkTreeIter tmp_iter;
687 GtkTreePath *next = NULL;
688 gboolean retval = FALSE, finished;
690 g_return_val_if_fail (gtk_tree_row_reference_valid (*row_reference), FALSE);
692 path = gtk_tree_row_reference_get_path (*row_reference);
693 gtk_tree_model_get_iter (model, &tmp_iter, path);
694 gtk_tree_row_reference_free (*row_reference);
695 *row_reference = NULL;
699 TnyHeader *header = NULL;
701 if (gtk_tree_model_iter_next (model, &tmp_iter)) {
702 gtk_tree_model_get (model, &tmp_iter,
703 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
707 if (msg_is_visible (header, is_outbox)) {
708 next = gtk_tree_model_get_path (model, &tmp_iter);
709 *row_reference = gtk_tree_row_reference_new (model, next);
710 gtk_tree_path_free (next);
714 g_object_unref (header);
717 } else if (cycle && gtk_tree_model_get_iter_first (model, &tmp_iter)) {
718 next = gtk_tree_model_get_path (model, &tmp_iter);
720 /* Ensure that we are not selecting the same */
721 if (gtk_tree_path_compare (path, next) != 0) {
722 gtk_tree_model_get (model, &tmp_iter,
723 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
726 if (msg_is_visible (header, is_outbox)) {
727 *row_reference = gtk_tree_row_reference_new (model, next);
731 g_object_unref (header);
735 /* If we ended up in the same message
736 then there is no valid next
740 gtk_tree_path_free (next);
742 /* If there are no more messages and we don't
743 want to start again in the first one then
744 there is no valid next message */
750 gtk_tree_path_free (path);
755 /* TODO: This should be in _init(), with the parameters as properties. */
757 modest_msg_view_window_construct (ModestMsgViewWindow *self,
758 const gchar *modest_account_name,
759 const gchar *mailbox,
760 const gchar *msg_uid)
763 ModestMsgViewWindowPrivate *priv = NULL;
764 ModestWindowPrivate *parent_priv = NULL;
765 ModestDimmingRulesGroup *toolbar_rules_group = NULL;
766 ModestDimmingRulesGroup *clipboard_rules_group = NULL;
768 obj = G_OBJECT (self);
769 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(obj);
770 parent_priv = MODEST_WINDOW_GET_PRIVATE(obj);
772 priv->msg_uid = g_strdup (msg_uid);
775 parent_priv->menubar = NULL;
777 toolbar_rules_group = modest_dimming_rules_group_new (MODEST_DIMMING_RULES_TOOLBAR, TRUE);
778 clipboard_rules_group = modest_dimming_rules_group_new (MODEST_DIMMING_RULES_CLIPBOARD, FALSE);
781 /* Add common dimming rules */
782 modest_dimming_rules_group_add_rules (toolbar_rules_group,
783 modest_msg_view_toolbar_dimming_entries,
784 G_N_ELEMENTS (modest_msg_view_toolbar_dimming_entries),
785 MODEST_WINDOW (self));
786 modest_dimming_rules_group_add_rules (clipboard_rules_group,
787 modest_msg_view_clipboard_dimming_entries,
788 G_N_ELEMENTS (modest_msg_view_clipboard_dimming_entries),
789 MODEST_WINDOW (self));
791 /* Insert dimming rules group for this window */
792 modest_ui_dimming_manager_insert_rules_group (parent_priv->ui_dimming_manager, toolbar_rules_group);
793 modest_ui_dimming_manager_insert_rules_group (parent_priv->ui_dimming_manager, clipboard_rules_group);
794 g_object_unref (toolbar_rules_group);
795 g_object_unref (clipboard_rules_group);
797 /* g_signal_connect (G_OBJECT(obj), "delete-event", G_CALLBACK(on_delete_event), obj); */
799 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);
800 g_signal_connect (G_OBJECT(priv->msg_view), "activate_link",
801 G_CALLBACK (modest_ui_actions_on_msg_link_clicked), obj);
802 g_signal_connect (G_OBJECT(priv->msg_view), "link_hover",
803 G_CALLBACK (modest_ui_actions_on_msg_link_hover), obj);
804 g_signal_connect (G_OBJECT(priv->msg_view), "attachment_clicked",
805 G_CALLBACK (modest_ui_actions_on_msg_attachment_clicked), obj);
806 g_signal_connect (G_OBJECT(priv->msg_view), "recpt_activated",
807 G_CALLBACK (modest_ui_actions_on_msg_recpt_activated), obj);
808 g_signal_connect (G_OBJECT(priv->msg_view), "show_details",
809 G_CALLBACK (modest_ui_actions_on_details), obj);
810 g_signal_connect (G_OBJECT(priv->msg_view), "link_contextual",
811 G_CALLBACK (modest_ui_actions_on_msg_link_contextual), obj);
812 g_signal_connect (G_OBJECT(priv->msg_view), "limit_error",
813 G_CALLBACK (modest_ui_actions_on_limit_error), obj);
814 g_signal_connect (G_OBJECT (priv->msg_view), "fetch_image",
815 G_CALLBACK (on_fetch_image), obj);
817 g_signal_connect (G_OBJECT (obj), "key-release-event",
818 G_CALLBACK (modest_msg_view_window_key_event),
821 g_signal_connect (G_OBJECT (obj), "key-press-event",
822 G_CALLBACK (modest_msg_view_window_key_event),
825 g_signal_connect (G_OBJECT (obj), "move-focus",
826 G_CALLBACK (on_move_focus), obj);
828 g_signal_connect (G_OBJECT (obj), "map-event",
829 G_CALLBACK (_modest_msg_view_window_map_event),
832 /* Mail Operation Queue */
833 priv->queue_change_handler = g_signal_connect (G_OBJECT (modest_runtime_get_mail_operation_queue ()),
835 G_CALLBACK (on_queue_changed),
838 /* Account manager */
839 priv->account_removed_handler = g_signal_connect (G_OBJECT (modest_runtime_get_account_store ()),
841 G_CALLBACK(on_account_removed),
844 modest_window_set_active_account (MODEST_WINDOW(obj), modest_account_name);
845 modest_window_set_active_mailbox (MODEST_WINDOW(obj), mailbox);
847 /* First add out toolbar ... */
848 modest_msg_view_window_show_toolbar (MODEST_WINDOW (obj), TRUE);
850 priv->isearch_toolbar = modest_toolkit_factory_create_isearch_toolbar (modest_runtime_get_toolkit_factory (),
852 modest_window_add_toolbar (MODEST_WINDOW (obj), GTK_TOOLBAR (priv->isearch_toolbar));
853 gtk_widget_set_no_show_all (priv->isearch_toolbar, TRUE);
854 g_signal_connect (G_OBJECT (priv->isearch_toolbar), "isearch-close",
855 G_CALLBACK (modest_msg_view_window_isearch_toolbar_close), obj);
856 g_signal_connect (G_OBJECT (priv->isearch_toolbar), "isearch-search",
857 G_CALLBACK (modest_msg_view_window_isearch_toolbar_search), obj);
858 priv->last_search = NULL;
860 /* Init the clipboard actions dim status */
861 modest_msg_view_grab_focus(MODEST_MSG_VIEW (priv->msg_view));
863 update_window_title (MODEST_MSG_VIEW_WINDOW (obj));
868 /* FIXME: parameter checks */
870 modest_msg_view_window_new_with_header_model (TnyMsg *msg,
871 const gchar *modest_account_name,
872 const gchar *mailbox,
873 const gchar *msg_uid,
875 GtkTreeRowReference *row_reference)
877 ModestMsgViewWindow *window = NULL;
878 ModestMsgViewWindowPrivate *priv = NULL;
879 TnyFolder *header_folder = NULL;
880 ModestHeaderView *header_view = NULL;
881 ModestWindowMgr *mgr = NULL;
884 modest_tny_mime_part_to_string (TNY_MIME_PART (msg), 0);
887 mgr = modest_runtime_get_window_mgr ();
888 window = MODEST_MSG_VIEW_WINDOW (modest_window_mgr_get_msg_view_window (mgr));
889 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), NULL);
891 modest_msg_view_window_construct (window, modest_account_name, mailbox, msg_uid);
893 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
895 /* Remember the message list's TreeModel so we can detect changes
896 * and change the list selection when necessary: */
897 header_folder = modest_header_view_get_folder (header_view);
899 priv->is_outbox = (modest_tny_folder_guess_folder_type (header_folder) ==
900 TNY_FOLDER_TYPE_OUTBOX);
901 priv->header_folder_id = tny_folder_get_id (header_folder);
902 g_object_unref(header_folder);
905 /* Setup row references and connect signals */
906 priv->header_model = g_object_ref (model);
908 if (row_reference && gtk_tree_row_reference_valid (row_reference)) {
909 priv->row_reference = gtk_tree_row_reference_copy (row_reference);
910 priv->next_row_reference = gtk_tree_row_reference_copy (row_reference);
911 select_next_valid_row (model, &(priv->next_row_reference), TRUE, priv->is_outbox);
913 priv->row_reference = NULL;
914 priv->next_row_reference = NULL;
917 /* Connect signals */
918 priv->row_changed_handler =
919 g_signal_connect (GTK_TREE_MODEL(model), "row-changed",
920 G_CALLBACK(modest_msg_view_window_on_row_changed),
922 priv->row_deleted_handler =
923 g_signal_connect (GTK_TREE_MODEL(model), "row-deleted",
924 G_CALLBACK(modest_msg_view_window_on_row_deleted),
926 priv->row_inserted_handler =
927 g_signal_connect (GTK_TREE_MODEL(model), "row-inserted",
928 G_CALLBACK(modest_msg_view_window_on_row_inserted),
930 priv->rows_reordered_handler =
931 g_signal_connect(GTK_TREE_MODEL(model), "rows-reordered",
932 G_CALLBACK(modest_msg_view_window_on_row_reordered),
935 if (header_view != NULL){
936 modest_header_view_add_observer(header_view,
937 MODEST_HEADER_VIEW_OBSERVER(window));
940 tny_msg_view_set_msg (TNY_MSG_VIEW (priv->msg_view), msg);
941 update_window_title (MODEST_MSG_VIEW_WINDOW (window));
942 update_branding (MODEST_MSG_VIEW_WINDOW (window));
944 /* gtk_widget_show_all (GTK_WIDGET (window)); */
945 modest_msg_view_window_update_priority (window);
946 /* Check dimming rules */
947 modest_ui_actions_check_toolbar_dimming_rules (MODEST_WINDOW (window));
948 modest_ui_actions_check_menu_dimming_rules (MODEST_WINDOW (window));
949 modest_window_check_dimming_rules_group (MODEST_WINDOW (window), MODEST_DIMMING_RULES_CLIPBOARD);
951 return MODEST_WINDOW(window);
955 modest_msg_view_window_new_from_uid (const gchar *modest_account_name,
956 const gchar *mailbox,
957 const gchar *msg_uid)
959 ModestMsgViewWindow *window = NULL;
960 ModestMsgViewWindowPrivate *priv = NULL;
961 ModestWindowMgr *mgr = NULL;
963 TnyAccount *account = NULL;
965 mgr = modest_runtime_get_window_mgr ();
966 window = MODEST_MSG_VIEW_WINDOW (modest_window_mgr_get_msg_view_window (mgr));
967 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), NULL);
969 modest_msg_view_window_construct (window, modest_account_name, mailbox, msg_uid);
971 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
973 is_merge = g_str_has_prefix (msg_uid, "merge:");
975 /* Get the account */
977 account = tny_account_store_find_account (TNY_ACCOUNT_STORE (modest_runtime_get_account_store ()),
980 if (is_merge || account) {
981 TnyFolder *folder = NULL;
983 /* Try to get the message, if it's already downloaded
984 we don't need to connect */
986 folder = modest_tny_folder_store_find_folder_from_uri (TNY_FOLDER_STORE (account), msg_uid);
988 ModestTnyAccountStore *account_store;
989 ModestTnyLocalFoldersAccount *local_folders_account;
991 account_store = modest_runtime_get_account_store ();
992 local_folders_account = MODEST_TNY_LOCAL_FOLDERS_ACCOUNT (
993 modest_tny_account_store_get_local_folders_account (account_store));
994 folder = modest_tny_local_folders_account_get_merged_outbox (local_folders_account);
995 g_object_unref (local_folders_account);
999 gboolean device_online;
1001 device = modest_runtime_get_device();
1002 device_online = tny_device_is_online (device);
1003 if (device_online) {
1004 message_reader (window, priv, NULL, msg_uid, folder, NULL);
1006 TnyMsg *msg = tny_folder_find_msg (folder, msg_uid, NULL);
1008 tny_msg_view_set_msg (TNY_MSG_VIEW (priv->msg_view), msg);
1009 update_window_title (MODEST_MSG_VIEW_WINDOW (window));
1010 update_branding (MODEST_MSG_VIEW_WINDOW (window));
1011 g_object_unref (msg);
1012 /* Sync flags to server */
1013 sync_flags (MODEST_MSG_VIEW_WINDOW (window));
1015 message_reader (window, priv, NULL, msg_uid, folder, NULL);
1018 g_object_unref (folder);
1023 /* Check dimming rules */
1024 modest_ui_actions_check_toolbar_dimming_rules (MODEST_WINDOW (window));
1025 modest_ui_actions_check_menu_dimming_rules (MODEST_WINDOW (window));
1026 modest_window_check_dimming_rules_group (MODEST_WINDOW (window), MODEST_DIMMING_RULES_CLIPBOARD);
1028 return MODEST_WINDOW(window);
1032 modest_msg_view_window_new_from_header_view (ModestHeaderView *header_view,
1033 const gchar *modest_account_name,
1034 const gchar *mailbox,
1035 const gchar *msg_uid,
1036 GtkTreeRowReference *row_reference)
1038 ModestMsgViewWindow *window = NULL;
1039 ModestMsgViewWindowPrivate *priv = NULL;
1040 TnyFolder *header_folder = NULL;
1041 ModestWindowMgr *mgr = NULL;
1045 mgr = modest_runtime_get_window_mgr ();
1046 window = MODEST_MSG_VIEW_WINDOW (modest_window_mgr_get_msg_view_window (mgr));
1047 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), NULL);
1049 modest_msg_view_window_construct (window, modest_account_name, mailbox, msg_uid);
1051 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1053 /* Remember the message list's TreeModel so we can detect changes
1054 * and change the list selection when necessary: */
1056 if (header_view != NULL){
1057 header_folder = modest_header_view_get_folder(header_view);
1058 /* This could happen if the header folder was
1059 unseleted before opening this msg window (for
1060 example if the user selects an account in the
1061 folder view of the main window */
1062 if (header_folder) {
1063 priv->is_outbox = (modest_tny_folder_guess_folder_type (header_folder) ==
1064 TNY_FOLDER_TYPE_OUTBOX);
1065 priv->header_folder_id = tny_folder_get_id(header_folder);
1066 g_object_unref(header_folder);
1070 /* Setup row references and connect signals */
1071 priv->header_model = gtk_tree_view_get_model (GTK_TREE_VIEW (header_view));
1072 g_object_ref (priv->header_model);
1074 if (row_reference && gtk_tree_row_reference_valid (row_reference)) {
1075 priv->row_reference = gtk_tree_row_reference_copy (row_reference);
1076 priv->next_row_reference = gtk_tree_row_reference_copy (row_reference);
1077 select_next_valid_row (priv->header_model, &(priv->next_row_reference), TRUE, priv->is_outbox);
1079 priv->row_reference = NULL;
1080 priv->next_row_reference = NULL;
1083 /* Connect signals */
1084 priv->row_changed_handler =
1085 g_signal_connect (GTK_TREE_MODEL(priv->header_model), "row-changed",
1086 G_CALLBACK(modest_msg_view_window_on_row_changed),
1088 priv->row_deleted_handler =
1089 g_signal_connect (GTK_TREE_MODEL(priv->header_model), "row-deleted",
1090 G_CALLBACK(modest_msg_view_window_on_row_deleted),
1092 priv->row_inserted_handler =
1093 g_signal_connect (GTK_TREE_MODEL(priv->header_model), "row-inserted",
1094 G_CALLBACK(modest_msg_view_window_on_row_inserted),
1096 priv->rows_reordered_handler =
1097 g_signal_connect(GTK_TREE_MODEL(priv->header_model), "rows-reordered",
1098 G_CALLBACK(modest_msg_view_window_on_row_reordered),
1101 if (header_view != NULL){
1102 modest_header_view_add_observer(header_view,
1103 MODEST_HEADER_VIEW_OBSERVER(window));
1106 tny_msg_view_set_msg (TNY_MSG_VIEW (priv->msg_view), NULL);
1107 update_branding (MODEST_MSG_VIEW_WINDOW (window));
1109 if (priv->row_reference) {
1110 path = gtk_tree_row_reference_get_path (priv->row_reference);
1111 if (gtk_tree_model_get_iter (priv->header_model, &iter, path)) {
1113 gtk_tree_model_get (priv->header_model, &iter,
1114 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
1116 message_reader (window, priv, header, NULL, NULL, priv->row_reference);
1117 g_object_unref (header);
1119 gtk_tree_path_free (path);
1121 /* Check dimming rules */
1122 modest_ui_actions_check_toolbar_dimming_rules (MODEST_WINDOW (window));
1123 modest_ui_actions_check_menu_dimming_rules (MODEST_WINDOW (window));
1124 modest_window_check_dimming_rules_group (MODEST_WINDOW (window), MODEST_DIMMING_RULES_CLIPBOARD);
1126 return MODEST_WINDOW(window);
1130 modest_msg_view_window_new_for_search_result (TnyMsg *msg,
1131 const gchar *modest_account_name,
1132 const gchar *mailbox,
1133 const gchar *msg_uid)
1135 ModestMsgViewWindow *window = NULL;
1136 ModestMsgViewWindowPrivate *priv = NULL;
1137 ModestWindowMgr *mgr = NULL;
1139 mgr = modest_runtime_get_window_mgr ();
1140 window = MODEST_MSG_VIEW_WINDOW (modest_window_mgr_get_msg_view_window (mgr));
1141 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), NULL);
1142 modest_msg_view_window_construct (window, modest_account_name, mailbox, msg_uid);
1144 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1146 /* Remember that this is a search result,
1147 * so we can disable some UI appropriately: */
1148 priv->is_search_result = TRUE;
1150 tny_msg_view_set_msg (TNY_MSG_VIEW (priv->msg_view), msg);
1151 update_branding (MODEST_MSG_VIEW_WINDOW (window));
1153 update_window_title (window);
1154 /* gtk_widget_show_all (GTK_WIDGET (window));*/
1155 modest_msg_view_window_update_priority (window);
1157 /* Check dimming rules */
1158 modest_ui_actions_check_toolbar_dimming_rules (MODEST_WINDOW (window));
1159 modest_ui_actions_check_menu_dimming_rules (MODEST_WINDOW (window));
1160 modest_window_check_dimming_rules_group (MODEST_WINDOW (window), MODEST_DIMMING_RULES_CLIPBOARD);
1162 return MODEST_WINDOW(window);
1166 modest_msg_view_window_is_other_body (ModestMsgViewWindow *self)
1168 ModestMsgViewWindowPrivate *priv = NULL;
1170 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self), FALSE);
1171 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
1173 return (priv->other_body != NULL);
1177 modest_msg_view_window_new_with_other_body (TnyMsg *msg,
1178 TnyMimePart *other_body,
1179 const gchar *modest_account_name,
1180 const gchar *mailbox,
1181 const gchar *msg_uid)
1183 GObject *obj = NULL;
1184 ModestMsgViewWindowPrivate *priv;
1185 ModestWindowMgr *mgr = NULL;
1187 g_return_val_if_fail (msg, NULL);
1188 mgr = modest_runtime_get_window_mgr ();
1189 obj = G_OBJECT (modest_window_mgr_get_msg_view_window (mgr));
1190 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (obj);
1191 modest_msg_view_window_construct (MODEST_MSG_VIEW_WINDOW (obj),
1192 modest_account_name, mailbox, msg_uid);
1195 priv->other_body = g_object_ref (other_body);
1196 modest_msg_view_set_msg_with_other_body (MODEST_MSG_VIEW (priv->msg_view), msg, other_body);
1198 tny_msg_view_set_msg (TNY_MSG_VIEW (priv->msg_view), msg);
1200 update_window_title (MODEST_MSG_VIEW_WINDOW (obj));
1201 update_branding (MODEST_MSG_VIEW_WINDOW (obj));
1203 /* gtk_widget_show_all (GTK_WIDGET (obj)); */
1205 /* Check dimming rules */
1206 modest_ui_actions_check_toolbar_dimming_rules (MODEST_WINDOW (obj));
1207 modest_ui_actions_check_menu_dimming_rules (MODEST_WINDOW (obj));
1208 modest_window_check_dimming_rules_group (MODEST_WINDOW (obj), MODEST_DIMMING_RULES_CLIPBOARD);
1210 return MODEST_WINDOW(obj);
1214 modest_msg_view_window_new_for_attachment (TnyMsg *msg,
1215 const gchar *modest_account_name,
1216 const gchar *mailbox,
1217 const gchar *msg_uid)
1219 return modest_msg_view_window_new_with_other_body (msg, NULL, modest_account_name, mailbox, msg_uid);
1223 modest_msg_view_window_on_row_changed (GtkTreeModel *header_model,
1226 ModestMsgViewWindow *window)
1228 check_dimming_rules_after_change (window);
1232 modest_msg_view_window_on_row_deleted(GtkTreeModel *header_model,
1234 ModestMsgViewWindow *window)
1236 check_dimming_rules_after_change (window);
1238 /* The window could have dissapeared */
1241 check_dimming_rules_after_change (ModestMsgViewWindow *window)
1243 modest_ui_actions_check_menu_dimming_rules (MODEST_WINDOW (window));
1244 modest_ui_actions_check_toolbar_dimming_rules (MODEST_WINDOW (window));
1248 /* On insertions we check if the folder still has the message we are
1249 * showing or do not. If do not, we do nothing. Which means we are still
1250 * not attached to any header folder and thus next/prev buttons are
1251 * still dimmed. Once the message that is shown by msg-view is found, the
1252 * new model of header-view will be attached and the references will be set.
1253 * On each further insertions dimming rules will be checked. However
1254 * this requires extra CPU time at least works.
1255 * (An message might be deleted from TnyFolder and thus will not be
1256 * inserted into the model again for example if it is removed by the
1257 * imap server and the header view is refreshed.)
1260 modest_msg_view_window_on_row_inserted (GtkTreeModel *model,
1261 GtkTreePath *tree_path,
1262 GtkTreeIter *tree_iter,
1263 ModestMsgViewWindow *window)
1265 ModestMsgViewWindowPrivate *priv = NULL;
1266 TnyHeader *header = NULL;
1268 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window));
1269 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1271 g_assert (model == priv->header_model);
1273 /* Check if the newly inserted message is the same we are actually
1274 * showing. IF not, we should remain detached from the header model
1275 * and thus prev and next toolbar buttons should remain dimmed. */
1276 gtk_tree_model_get (model, tree_iter,
1277 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
1280 if (TNY_IS_HEADER (header)) {
1283 uid = modest_tny_folder_get_header_unique_id (header);
1284 if (!g_str_equal(priv->msg_uid, uid)) {
1285 check_dimming_rules_after_change (window);
1287 g_object_unref (G_OBJECT(header));
1291 g_object_unref(G_OBJECT(header));
1294 if (priv->row_reference) {
1295 gtk_tree_row_reference_free (priv->row_reference);
1298 /* Setup row_reference for the actual msg. */
1299 priv->row_reference = gtk_tree_row_reference_new (priv->header_model, tree_path);
1300 if (priv->row_reference == NULL) {
1301 g_warning("%s: No reference for msg header item.", __FUNCTION__);
1305 /* Now set up next_row_reference. */
1306 if (priv->next_row_reference) {
1307 gtk_tree_row_reference_free (priv->next_row_reference);
1310 priv->next_row_reference = gtk_tree_row_reference_copy (priv->row_reference);
1311 select_next_valid_row (priv->header_model,
1312 &(priv->next_row_reference), FALSE, priv->is_outbox);
1314 /* Connect the remaining callbacks to become able to detect
1315 * changes in header-view. */
1316 priv->row_changed_handler =
1317 g_signal_connect (priv->header_model, "row-changed",
1318 G_CALLBACK (modest_msg_view_window_on_row_changed),
1320 priv->row_deleted_handler =
1321 g_signal_connect (priv->header_model, "row-deleted",
1322 G_CALLBACK (modest_msg_view_window_on_row_deleted),
1324 priv->rows_reordered_handler =
1325 g_signal_connect (priv->header_model, "rows-reordered",
1326 G_CALLBACK (modest_msg_view_window_on_row_reordered),
1329 check_dimming_rules_after_change (window);
1333 modest_msg_view_window_on_row_reordered (GtkTreeModel *header_model,
1337 ModestMsgViewWindow *window)
1339 ModestMsgViewWindowPrivate *priv = NULL;
1340 gboolean already_changed = FALSE;
1342 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(window);
1344 /* If the current row was reordered select the proper next
1345 valid row. The same if the next row reference changes */
1346 if (!priv->row_reference ||
1347 !gtk_tree_row_reference_valid (priv->row_reference))
1350 if (priv->next_row_reference &&
1351 gtk_tree_row_reference_valid (priv->next_row_reference)) {
1352 GtkTreePath *cur, *next;
1353 /* Check that the order is still the correct one */
1354 cur = gtk_tree_row_reference_get_path (priv->row_reference);
1355 next = gtk_tree_row_reference_get_path (priv->next_row_reference);
1356 gtk_tree_path_next (cur);
1357 if (gtk_tree_path_compare (cur, next) != 0) {
1358 gtk_tree_row_reference_free (priv->next_row_reference);
1359 priv->next_row_reference = gtk_tree_row_reference_copy (priv->row_reference);
1360 select_next_valid_row (header_model, &(priv->next_row_reference), FALSE, priv->is_outbox);
1361 already_changed = TRUE;
1363 gtk_tree_path_free (cur);
1364 gtk_tree_path_free (next);
1366 if (priv->next_row_reference)
1367 gtk_tree_row_reference_free (priv->next_row_reference);
1368 /* Update next row reference */
1369 priv->next_row_reference = gtk_tree_row_reference_copy (priv->row_reference);
1370 select_next_valid_row (header_model, &(priv->next_row_reference), FALSE, priv->is_outbox);
1371 already_changed = TRUE;
1374 check_dimming_rules_after_change (window);
1377 /* The modest_msg_view_window_update_model_replaced implements update
1378 * function for ModestHeaderViewObserver. Checks whether the TnyFolder
1379 * actually belongs to the header-view is the same as the TnyFolder of
1380 * the message of msg-view or not. If they are different, there is
1381 * nothing to do. If they are the same, then the model has replaced and
1382 * the reference in msg-view shall be replaced from the old model to
1383 * the new model. In this case the view will be detached from it's
1384 * header folder. From this point the next/prev buttons are dimmed.
1387 modest_msg_view_window_update_model_replaced (ModestHeaderViewObserver *observer,
1388 GtkTreeModel *model,
1389 const gchar *tny_folder_id)
1391 ModestMsgViewWindowPrivate *priv = NULL;
1392 ModestMsgViewWindow *window = NULL;
1394 g_assert(MODEST_IS_HEADER_VIEW_OBSERVER(observer));
1395 g_assert(MODEST_IS_MSG_VIEW_WINDOW(observer));
1397 window = MODEST_MSG_VIEW_WINDOW(observer);
1398 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(window);
1400 /* If there is an other folder in the header-view then we do
1401 * not care about it's model (msg list). Else if the
1402 * header-view shows the folder the msg shown by us is in, we
1403 * shall replace our model reference and make some check. */
1404 if(model == NULL || tny_folder_id == NULL ||
1405 (priv->header_folder_id && !g_str_equal(tny_folder_id, priv->header_folder_id)))
1408 /* Model is changed(replaced), so we should forget the old
1409 * one. Because there might be other references and there
1410 * might be some change on the model even if we unreferenced
1411 * it, we need to disconnect our signals here. */
1412 if (priv->header_model) {
1413 if (g_signal_handler_is_connected(G_OBJECT (priv->header_model),
1414 priv->row_changed_handler))
1415 g_signal_handler_disconnect(G_OBJECT (priv->header_model),
1416 priv->row_changed_handler);
1417 if (g_signal_handler_is_connected(G_OBJECT (priv->header_model),
1418 priv->row_deleted_handler))
1419 g_signal_handler_disconnect(G_OBJECT (priv->header_model),
1420 priv->row_deleted_handler);
1421 if (g_signal_handler_is_connected(G_OBJECT (priv->header_model),
1422 priv->row_inserted_handler))
1423 g_signal_handler_disconnect(G_OBJECT (priv->header_model),
1424 priv->row_inserted_handler);
1425 if (g_signal_handler_is_connected(G_OBJECT (priv->header_model),
1426 priv->rows_reordered_handler))
1427 g_signal_handler_disconnect(G_OBJECT (priv->header_model),
1428 priv->rows_reordered_handler);
1431 if (priv->row_reference)
1432 gtk_tree_row_reference_free (priv->row_reference);
1433 if (priv->next_row_reference)
1434 gtk_tree_row_reference_free (priv->next_row_reference);
1435 g_object_unref(priv->header_model);
1438 priv->row_changed_handler = 0;
1439 priv->row_deleted_handler = 0;
1440 priv->row_inserted_handler = 0;
1441 priv->rows_reordered_handler = 0;
1442 priv->next_row_reference = NULL;
1443 priv->row_reference = NULL;
1444 priv->header_model = NULL;
1447 priv->header_model = g_object_ref (model);
1449 /* Also we must connect to the new model for row insertions.
1450 * Only for insertions now. We will need other ones only after
1451 * the msg is show by msg-view is added to the new model. */
1452 priv->row_inserted_handler =
1453 g_signal_connect (priv->header_model, "row-inserted",
1454 G_CALLBACK(modest_msg_view_window_on_row_inserted),
1457 modest_ui_actions_check_menu_dimming_rules(MODEST_WINDOW(window));
1458 modest_ui_actions_check_toolbar_dimming_rules(MODEST_WINDOW(window));
1462 modest_msg_view_window_toolbar_on_transfer_mode (ModestMsgViewWindow *self)
1464 ModestMsgViewWindowPrivate *priv= NULL;
1466 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self), FALSE);
1467 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
1469 return priv->progress_hint;
1473 modest_msg_view_window_get_header (ModestMsgViewWindow *self)
1475 ModestMsgViewWindowPrivate *priv= NULL;
1477 TnyHeader *header = NULL;
1478 GtkTreePath *path = NULL;
1481 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self), NULL);
1482 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
1484 /* If the message was not obtained from a treemodel,
1485 * for instance if it was opened directly by the search UI:
1487 if (priv->header_model == NULL ||
1488 priv->row_reference == NULL ||
1489 !gtk_tree_row_reference_valid (priv->row_reference)) {
1490 msg = modest_msg_view_window_get_message (self);
1492 header = tny_msg_get_header (msg);
1493 g_object_unref (msg);
1498 /* Get iter of the currently selected message in the header view: */
1499 path = gtk_tree_row_reference_get_path (priv->row_reference);
1500 g_return_val_if_fail (path != NULL, NULL);
1501 gtk_tree_model_get_iter (priv->header_model,
1505 /* Get current message header */
1506 gtk_tree_model_get (priv->header_model, &iter,
1507 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
1510 gtk_tree_path_free (path);
1515 modest_msg_view_window_get_message (ModestMsgViewWindow *self)
1517 ModestMsgViewWindowPrivate *priv;
1519 g_return_val_if_fail (self, NULL);
1521 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(self);
1523 return tny_msg_view_get_msg (TNY_MSG_VIEW (priv->msg_view));
1527 modest_msg_view_window_get_message_uid (ModestMsgViewWindow *self)
1529 ModestMsgViewWindowPrivate *priv;
1531 g_return_val_if_fail (self, NULL);
1533 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
1535 return (const gchar*) priv->msg_uid;
1538 /* Used for the Ctrl+F accelerator */
1540 modest_msg_view_window_toggle_isearch_toolbar (GtkWidget *obj,
1543 ModestMsgViewWindow *window = MODEST_MSG_VIEW_WINDOW (data);
1544 ModestMsgViewWindowPrivate *priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1546 if (GTK_WIDGET_VISIBLE (priv->isearch_toolbar)) {
1547 modest_msg_view_window_isearch_toolbar_close (obj, data);
1549 modest_msg_view_window_show_isearch_toolbar (obj, data);
1553 /* Handler for menu option */
1555 modest_msg_view_window_show_isearch_toolbar (GtkWidget *obj,
1558 ModestMsgViewWindow *window = MODEST_MSG_VIEW_WINDOW (data);
1559 ModestMsgViewWindowPrivate *priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1561 gtk_widget_show (priv->isearch_toolbar);
1562 modest_isearch_toolbar_highlight_entry (MODEST_ISEARCH_TOOLBAR (priv->isearch_toolbar), TRUE);
1565 /* Handler for click on the "X" close button in isearch toolbar */
1567 modest_msg_view_window_isearch_toolbar_close (GtkWidget *widget,
1568 ModestMsgViewWindow *obj)
1570 ModestMsgViewWindowPrivate *priv;
1572 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (obj);
1575 gtk_widget_hide (priv->isearch_toolbar);
1576 modest_msg_view_grab_focus (MODEST_MSG_VIEW (priv->msg_view));
1580 modest_msg_view_window_isearch_toolbar_search (GtkWidget *widget,
1581 ModestMsgViewWindow *obj)
1583 const gchar *current_search;
1584 ModestMsgViewWindowPrivate *priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (obj);
1586 if (modest_mime_part_view_is_empty (MODEST_MIME_PART_VIEW (priv->msg_view))) {
1587 modest_platform_system_banner (NULL, NULL, _("mail_ib_nothing_to_find"));
1591 current_search = modest_isearch_toolbar_get_search (MODEST_ISEARCH_TOOLBAR (widget));
1593 if ((current_search == NULL) || (strcmp (current_search, "") == 0)) {
1594 modest_platform_system_banner (NULL, NULL, _CS("ecdg_ib_find_rep_enter_text"));
1598 if ((priv->last_search == NULL) || (strcmp (priv->last_search, current_search) != 0)) {
1600 g_free (priv->last_search);
1601 priv->last_search = g_strdup (current_search);
1602 result = modest_isearch_view_search (MODEST_ISEARCH_VIEW (priv->msg_view),
1605 modest_platform_system_banner (NULL, NULL,
1606 _HL("ckct_ib_find_no_matches"));
1607 g_free (priv->last_search);
1608 priv->last_search = NULL;
1610 modest_isearch_toolbar_highlight_entry (MODEST_ISEARCH_TOOLBAR (priv->isearch_toolbar), TRUE);
1613 if (!modest_isearch_view_search_next (MODEST_ISEARCH_VIEW (priv->msg_view))) {
1614 modest_platform_system_banner (NULL, NULL,
1615 _HL("ckct_ib_find_search_complete"));
1616 g_free (priv->last_search);
1617 priv->last_search = NULL;
1619 modest_isearch_toolbar_highlight_entry (MODEST_ISEARCH_TOOLBAR (priv->isearch_toolbar), TRUE);
1626 modest_msg_view_window_set_zoom (ModestWindow *window,
1629 ModestMsgViewWindowPrivate *priv;
1630 ModestWindowPrivate *parent_priv;
1632 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window));
1634 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1635 parent_priv = MODEST_WINDOW_GET_PRIVATE (window);
1636 modest_zoomable_set_zoom (MODEST_ZOOMABLE (priv->msg_view), zoom);
1641 modest_msg_view_window_get_zoom (ModestWindow *window)
1643 ModestMsgViewWindowPrivate *priv;
1645 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), 1.0);
1647 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1648 return modest_zoomable_get_zoom (MODEST_ZOOMABLE (priv->msg_view));
1652 modest_msg_view_window_zoom_plus (ModestWindow *window)
1655 ModestMsgViewWindowPrivate *priv;
1659 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), 1.0);
1660 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1662 zoom_level = modest_zoomable_get_zoom (MODEST_ZOOMABLE (priv->msg_view));
1664 if (zoom_level >= 2.0) {
1665 modest_platform_system_banner (NULL, NULL,
1666 _CS("ckct_ib_max_zoom_level_reached"));
1668 } else if (zoom_level >= 1.5) {
1670 } else if (zoom_level >= 1.2) {
1672 } else if (zoom_level >= 1.0) {
1674 } else if (zoom_level >= 0.8) {
1676 } else if (zoom_level >= 0.5) {
1682 /* set zoom level */
1683 int_zoom = (gint) rint (zoom_level*100.0+0.1);
1684 banner_text = g_strdup_printf (_HL("wdgt_ib_zoom"), int_zoom);
1685 modest_platform_information_banner (GTK_WIDGET (window), NULL, banner_text);
1686 g_free (banner_text);
1687 modest_zoomable_set_zoom (MODEST_ZOOMABLE (priv->msg_view), zoom_level);
1693 modest_msg_view_window_zoom_minus (ModestWindow *window)
1696 ModestMsgViewWindowPrivate *priv;
1700 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), 1.0);
1701 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1703 zoom_level = modest_zoomable_get_zoom (MODEST_ZOOMABLE (priv->msg_view));
1705 if (zoom_level <= 0.5) {
1706 modest_platform_system_banner (NULL, NULL,
1707 _CS("ckct_ib_min_zoom_level_reached"));
1709 } else if (zoom_level <= 0.8) {
1711 } else if (zoom_level <= 1.0) {
1713 } else if (zoom_level <= 1.2) {
1715 } else if (zoom_level <= 1.5) {
1717 } else if (zoom_level <= 2.0) {
1723 /* set zoom level */
1724 int_zoom = (gint) rint (zoom_level*100.0+0.1);
1725 banner_text = g_strdup_printf (_HL("wdgt_ib_zoom"), int_zoom);
1726 modest_platform_information_banner (GTK_WIDGET (window), NULL, banner_text);
1727 g_free (banner_text);
1728 modest_zoomable_set_zoom (MODEST_ZOOMABLE (priv->msg_view), zoom_level);
1734 modest_msg_view_window_key_event (GtkWidget *window,
1740 focus = gtk_window_get_focus (GTK_WINDOW (window));
1742 /* for the isearch toolbar case */
1743 if (focus && GTK_IS_ENTRY (focus)) {
1744 if (event->keyval == GDK_BackSpace) {
1746 copy = gdk_event_copy ((GdkEvent *) event);
1747 gtk_widget_event (focus, copy);
1748 gdk_event_free (copy);
1758 modest_msg_view_window_last_message_selected (ModestMsgViewWindow *window)
1761 ModestMsgViewWindowPrivate *priv;
1762 GtkTreeIter tmp_iter;
1763 gboolean is_last_selected;
1765 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), TRUE);
1766 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1768 /*if no model (so no rows at all), then virtually we are the last*/
1769 if (!priv->header_model || !priv->row_reference)
1772 if (!gtk_tree_row_reference_valid (priv->row_reference))
1775 path = gtk_tree_row_reference_get_path (priv->row_reference);
1779 is_last_selected = TRUE;
1780 while (is_last_selected) {
1782 gtk_tree_path_next (path);
1783 if (!gtk_tree_model_get_iter (priv->header_model, &tmp_iter, path))
1785 gtk_tree_model_get (priv->header_model, &tmp_iter,
1786 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
1789 if (msg_is_visible (header, priv->is_outbox))
1790 is_last_selected = FALSE;
1791 g_object_unref(G_OBJECT(header));
1794 gtk_tree_path_free (path);
1795 return is_last_selected;
1799 modest_msg_view_window_has_headers_model (ModestMsgViewWindow *window)
1801 ModestMsgViewWindowPrivate *priv;
1803 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), TRUE);
1804 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1806 return priv->header_model != NULL;
1810 modest_msg_view_window_is_search_result (ModestMsgViewWindow *window)
1812 ModestMsgViewWindowPrivate *priv;
1814 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), TRUE);
1815 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1817 return priv->is_search_result;
1821 msg_is_visible (TnyHeader *header, gboolean check_outbox)
1823 if ((tny_header_get_flags(header) & TNY_HEADER_FLAG_DELETED))
1825 if (!check_outbox) {
1828 ModestTnySendQueueStatus status;
1829 status = modest_tny_all_send_queues_get_msg_status (header);
1830 return ((status != MODEST_TNY_SEND_QUEUE_FAILED) &&
1831 (status != MODEST_TNY_SEND_QUEUE_SENDING));
1836 modest_msg_view_window_first_message_selected (ModestMsgViewWindow *window)
1839 ModestMsgViewWindowPrivate *priv;
1840 gboolean is_first_selected;
1841 GtkTreeIter tmp_iter;
1843 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), TRUE);
1844 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1846 /*if no model (so no rows at all), then virtually we are the first*/
1847 if (!priv->header_model || !priv->row_reference)
1850 if (!gtk_tree_row_reference_valid (priv->row_reference))
1853 path = gtk_tree_row_reference_get_path (priv->row_reference);
1857 is_first_selected = TRUE;
1858 while (is_first_selected) {
1860 if(!gtk_tree_path_prev (path))
1862 /* Here the 'if' is needless for logic, but let make sure
1863 * iter is valid for gtk_tree_model_get. */
1864 if (!gtk_tree_model_get_iter (priv->header_model, &tmp_iter, path))
1866 gtk_tree_model_get (priv->header_model, &tmp_iter,
1867 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
1870 if (msg_is_visible (header, priv->is_outbox))
1871 is_first_selected = FALSE;
1872 g_object_unref(G_OBJECT(header));
1875 gtk_tree_path_free (path);
1876 return is_first_selected;
1883 GtkTreeRowReference *row_reference;
1887 message_reader_performer (gboolean canceled,
1889 GtkWindow *parent_window,
1890 TnyAccount *account,
1893 ModestMailOperation *mail_op = NULL;
1894 MsgReaderInfo *info;
1896 info = (MsgReaderInfo *) user_data;
1897 if (canceled || err) {
1898 update_window_title (MODEST_MSG_VIEW_WINDOW (parent_window));
1902 /* Register the header - it'll be unregistered in the callback */
1904 modest_window_mgr_register_header (modest_runtime_get_window_mgr (), info->header, NULL);
1906 /* New mail operation */
1907 mail_op = modest_mail_operation_new_with_error_handling (G_OBJECT(parent_window),
1908 modest_ui_actions_disk_operations_error_handler,
1911 modest_mail_operation_queue_add (modest_runtime_get_mail_operation_queue (), mail_op);
1913 modest_mail_operation_get_msg (mail_op, info->header, TRUE, view_msg_cb, info->row_reference);
1915 modest_mail_operation_find_msg (mail_op, info->folder, info->msg_uid, TRUE, view_msg_cb, NULL);
1916 g_object_unref (mail_op);
1918 /* Update dimming rules */
1919 modest_ui_actions_check_toolbar_dimming_rules (MODEST_WINDOW (parent_window));
1920 modest_ui_actions_check_menu_dimming_rules (MODEST_WINDOW (parent_window));
1923 /* Frees. The row_reference will be freed by the view_msg_cb callback */
1924 g_free (info->msg_uid);
1926 g_object_unref (info->folder);
1928 g_object_unref (info->header);
1929 g_slice_free (MsgReaderInfo, info);
1934 * Reads the message whose summary item is @header. It takes care of
1935 * several things, among others:
1937 * If the message was not previously downloaded then ask the user
1938 * before downloading. If there is no connection launch the connection
1939 * dialog. Update toolbar dimming rules.
1941 * Returns: TRUE if the mail operation was started, otherwise if the
1942 * user do not want to download the message, or if the user do not
1943 * want to connect, then the operation is not issued
1946 message_reader (ModestMsgViewWindow *window,
1947 ModestMsgViewWindowPrivate *priv,
1949 const gchar *msg_uid,
1951 GtkTreeRowReference *row_reference)
1953 ModestWindowMgr *mgr;
1954 TnyAccount *account = NULL;
1955 MsgReaderInfo *info;
1957 /* We set the header from model while we're loading */
1958 tny_header_view_set_header (TNY_HEADER_VIEW (priv->msg_view), header);
1959 modest_window_set_title (MODEST_WINDOW (window), _CS("ckdg_pb_updating"));
1965 g_object_ref (folder);
1967 mgr = modest_runtime_get_window_mgr ();
1968 /* Msg download completed */
1969 if (!header || !(tny_header_get_flags (header) & TNY_HEADER_FLAG_CACHED)) {
1971 /* Ask the user if he wants to download the message if
1973 if (!tny_device_is_online (modest_runtime_get_device())) {
1974 GtkResponseType response;
1976 response = modest_platform_run_confirmation_dialog (GTK_WINDOW (window),
1977 _("mcen_nc_get_msg"));
1978 if (response == GTK_RESPONSE_CANCEL) {
1979 update_window_title (window);
1984 folder = tny_header_get_folder (header);
1986 info = g_slice_new (MsgReaderInfo);
1987 info->msg_uid = g_strdup (msg_uid);
1989 info->header = g_object_ref (header);
1991 info->header = NULL;
1993 info->folder = g_object_ref (folder);
1995 info->folder = NULL;
1996 if (row_reference) {
1997 info->row_reference = gtk_tree_row_reference_copy (row_reference);
1999 info->row_reference = NULL;
2002 /* Offer the connection dialog if necessary */
2003 modest_platform_connect_if_remote_and_perform ((GtkWindow *) window,
2005 TNY_FOLDER_STORE (folder),
2006 message_reader_performer,
2009 g_object_unref (folder);
2015 folder = tny_header_get_folder (header);
2018 account = tny_folder_get_account (folder);
2020 info = g_slice_new (MsgReaderInfo);
2021 info->msg_uid = g_strdup (msg_uid);
2023 info->folder = g_object_ref (folder);
2025 info->folder = NULL;
2027 info->header = g_object_ref (header);
2029 info->header = NULL;
2031 info->row_reference = gtk_tree_row_reference_copy (row_reference);
2033 info->row_reference = NULL;
2035 message_reader_performer (FALSE, NULL, (GtkWindow *) window, account, info);
2037 g_object_unref (account);
2039 g_object_unref (folder);
2045 modest_msg_view_window_select_next_message (ModestMsgViewWindow *window)
2047 ModestMsgViewWindowPrivate *priv;
2048 GtkTreePath *path= NULL;
2049 GtkTreeIter tmp_iter;
2051 gboolean retval = TRUE;
2052 GtkTreeRowReference *row_reference = NULL;
2054 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), FALSE);
2055 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
2057 if (!priv->row_reference)
2060 /* Update the next row reference if it's not valid. This could
2061 happen if for example the header which it was pointing to,
2062 was deleted. The best place to do it is in the row-deleted
2063 handler but the tinymail model do not work like the glib
2064 tree models and reports the deletion when the row is still
2066 if (!gtk_tree_row_reference_valid (priv->next_row_reference)) {
2067 if (priv->next_row_reference) {
2068 gtk_tree_row_reference_free (priv->next_row_reference);
2070 if (gtk_tree_row_reference_valid (priv->row_reference)) {
2071 priv->next_row_reference = gtk_tree_row_reference_copy (priv->row_reference);
2072 select_next_valid_row (priv->header_model, &(priv->next_row_reference), FALSE, priv->is_outbox);
2074 priv->next_row_reference = NULL;
2077 if (priv->next_row_reference)
2078 path = gtk_tree_row_reference_get_path (priv->next_row_reference);
2082 row_reference = gtk_tree_row_reference_copy (priv->next_row_reference);
2084 gtk_tree_model_get_iter (priv->header_model,
2087 gtk_tree_path_free (path);
2089 gtk_tree_model_get (priv->header_model, &tmp_iter,
2090 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
2093 /* Read the message & show it */
2094 if (!message_reader (window, priv, header, NULL, NULL, row_reference)) {
2097 gtk_tree_row_reference_free (row_reference);
2100 g_object_unref (header);
2106 modest_msg_view_window_select_previous_message (ModestMsgViewWindow *window)
2108 ModestMsgViewWindowPrivate *priv = NULL;
2110 gboolean finished = FALSE;
2111 gboolean retval = FALSE;
2113 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), FALSE);
2114 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
2116 if (priv->row_reference && !gtk_tree_row_reference_valid (priv->row_reference)) {
2117 gtk_tree_row_reference_free (priv->row_reference);
2118 priv->row_reference = NULL;
2121 /* Return inmediatly if there is no header model */
2122 if (!priv->header_model || !priv->row_reference)
2125 path = gtk_tree_row_reference_get_path (priv->row_reference);
2126 while (!finished && gtk_tree_path_prev (path)) {
2130 gtk_tree_model_get_iter (priv->header_model, &iter, path);
2131 gtk_tree_model_get (priv->header_model, &iter,
2132 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
2136 if (msg_is_visible (header, priv->is_outbox)) {
2137 GtkTreeRowReference *row_reference;
2138 row_reference = gtk_tree_row_reference_new (priv->header_model, path);
2139 /* Read the message & show it */
2140 retval = message_reader (window, priv, header, NULL, NULL, row_reference);
2141 gtk_tree_row_reference_free (row_reference);
2145 g_object_unref (header);
2149 gtk_tree_path_free (path);
2154 view_msg_cb (ModestMailOperation *mail_op,
2161 ModestMsgViewWindow *self = NULL;
2162 ModestMsgViewWindowPrivate *priv = NULL;
2163 GtkTreeRowReference *row_reference = NULL;
2165 /* Unregister the header (it was registered before creating the mail operation) */
2166 modest_window_mgr_unregister_header (modest_runtime_get_window_mgr (), header);
2168 row_reference = (GtkTreeRowReference *) user_data;
2171 gtk_tree_row_reference_free (row_reference);
2172 self = (ModestMsgViewWindow *) modest_mail_operation_get_source (mail_op);
2174 /* Restore window title */
2175 update_window_title (self);
2176 modest_ui_actions_on_close_window (NULL, MODEST_WINDOW (self));
2177 g_object_unref (self);
2182 /* If there was any error */
2183 if (!modest_ui_actions_msg_retrieval_check (mail_op, header, msg)) {
2185 gtk_tree_row_reference_free (row_reference);
2186 self = (ModestMsgViewWindow *) modest_mail_operation_get_source (mail_op);
2188 /* Restore window title */
2189 update_window_title (self);
2190 modest_ui_actions_on_close_window (NULL, MODEST_WINDOW (self));
2191 g_object_unref (self);
2196 /* Get the window */
2197 self = (ModestMsgViewWindow *) modest_mail_operation_get_source (mail_op);
2198 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self));
2199 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
2201 /* Update the row reference */
2202 if (priv->row_reference != NULL) {
2203 gtk_tree_row_reference_free (priv->row_reference);
2204 priv->row_reference = (row_reference && gtk_tree_row_reference_valid (row_reference))?gtk_tree_row_reference_copy (row_reference):NULL;
2205 if (priv->next_row_reference != NULL) {
2206 gtk_tree_row_reference_free (priv->next_row_reference);
2208 if (priv->row_reference) {
2209 priv->next_row_reference = gtk_tree_row_reference_copy (priv->row_reference);
2210 select_next_valid_row (priv->header_model, &(priv->next_row_reference), TRUE, priv->is_outbox);
2212 priv->next_row_reference = NULL;
2216 /* Mark header as read */
2217 if (!(tny_header_get_flags (header) & TNY_HEADER_FLAG_SEEN))
2218 tny_header_set_flag (header, TNY_HEADER_FLAG_SEEN);
2220 /* Set new message */
2221 if (priv->msg_view != NULL && TNY_IS_MSG_VIEW (priv->msg_view)) {
2222 tny_msg_view_set_msg (TNY_MSG_VIEW (priv->msg_view), msg);
2223 modest_msg_view_window_update_priority (self);
2224 update_window_title (MODEST_MSG_VIEW_WINDOW (self));
2225 update_branding (MODEST_MSG_VIEW_WINDOW (self));
2226 modest_msg_view_grab_focus (MODEST_MSG_VIEW (priv->msg_view));
2229 /* Set the new message uid of the window */
2230 if (priv->msg_uid) {
2231 g_free (priv->msg_uid);
2232 priv->msg_uid = modest_tny_folder_get_header_unique_id (header);
2235 /* Notify the observers */
2236 g_signal_emit (G_OBJECT (self), signals[MSG_CHANGED_SIGNAL],
2237 0, priv->header_model, priv->row_reference);
2239 /* Sync the flags if the message is not opened from a header
2240 model, i.e, if it's opened from a notification */
2241 if (!priv->header_model)
2245 g_object_unref (self);
2247 gtk_tree_row_reference_free (row_reference);
2251 modest_msg_view_window_get_folder_type (ModestMsgViewWindow *window)
2253 ModestMsgViewWindowPrivate *priv;
2255 TnyFolderType folder_type;
2257 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
2259 folder_type = TNY_FOLDER_TYPE_UNKNOWN;
2261 msg = tny_msg_view_get_msg (TNY_MSG_VIEW (priv->msg_view));
2265 folder = tny_msg_get_folder (msg);
2267 folder_type = modest_tny_folder_guess_folder_type (folder);
2268 g_object_unref (folder);
2270 g_object_unref (msg);
2278 modest_msg_view_window_update_priority (ModestMsgViewWindow *window)
2280 ModestMsgViewWindowPrivate *priv;
2281 TnyHeader *header = NULL;
2282 TnyHeaderFlags flags = 0;
2284 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
2286 if (priv->header_model && priv->row_reference && gtk_tree_row_reference_valid (priv->row_reference)) {
2288 GtkTreePath *path = NULL;
2290 path = gtk_tree_row_reference_get_path (priv->row_reference);
2291 g_return_if_fail (path != NULL);
2292 gtk_tree_model_get_iter (priv->header_model,
2294 gtk_tree_row_reference_get_path (priv->row_reference));
2296 gtk_tree_model_get (priv->header_model, &iter, TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
2298 gtk_tree_path_free (path);
2301 msg = tny_msg_view_get_msg (TNY_MSG_VIEW (priv->msg_view));
2303 header = tny_msg_get_header (msg);
2304 g_object_unref (msg);
2309 flags = tny_header_get_flags (header);
2310 g_object_unref(G_OBJECT(header));
2313 modest_msg_view_set_priority (MODEST_MSG_VIEW(priv->msg_view), flags);
2318 toolbar_resize (ModestMsgViewWindow *self)
2320 ModestMsgViewWindowPrivate *priv = NULL;
2321 ModestWindowPrivate *parent_priv = NULL;
2323 gint static_button_size;
2324 ModestWindowMgr *mgr;
2326 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self));
2327 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
2328 parent_priv = MODEST_WINDOW_GET_PRIVATE(self);
2330 mgr = modest_runtime_get_window_mgr ();
2331 static_button_size = modest_window_mgr_get_fullscreen_mode (mgr)?120:120;
2333 if (parent_priv->toolbar) {
2334 /* Set expandable and homogeneous tool buttons */
2335 widget = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/ToolbarMessageReply");
2336 gtk_tool_item_set_expand (GTK_TOOL_ITEM (widget), TRUE);
2337 gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (widget), TRUE);
2338 widget = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/ToolbarMessageReplyAll");
2339 gtk_tool_item_set_expand (GTK_TOOL_ITEM (widget), TRUE);
2340 gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (widget), TRUE);
2341 widget = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/ToolbarMessageForward");
2342 gtk_tool_item_set_expand (GTK_TOOL_ITEM (widget), TRUE);
2343 gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (widget), TRUE);
2344 widget = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/ToolbarDeleteMessage");
2345 gtk_tool_item_set_expand (GTK_TOOL_ITEM (widget), TRUE);
2346 gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (widget), TRUE);
2347 widget = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/ToolbarDownloadExternalImages");
2348 gtk_tool_item_set_expand (GTK_TOOL_ITEM (widget), TRUE);
2349 gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (widget), TRUE);
2350 gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (priv->next_toolitem), TRUE);
2351 gtk_tool_item_set_expand (GTK_TOOL_ITEM (priv->next_toolitem), TRUE);
2352 gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (priv->prev_toolitem), TRUE);
2353 gtk_tool_item_set_expand (GTK_TOOL_ITEM (priv->prev_toolitem), TRUE);
2358 modest_msg_view_window_show_toolbar (ModestWindow *self,
2359 gboolean show_toolbar)
2361 ModestMsgViewWindowPrivate *priv = NULL;
2362 ModestWindowPrivate *parent_priv;
2364 parent_priv = MODEST_WINDOW_GET_PRIVATE(self);
2365 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(self);
2367 /* Set optimized view status */
2368 priv->optimized_view = !show_toolbar;
2370 if (!parent_priv->toolbar) {
2371 parent_priv->toolbar = gtk_ui_manager_get_widget (parent_priv->ui_manager,
2373 gtk_toolbar_set_icon_size (GTK_TOOLBAR (parent_priv->toolbar), HILDON_ICON_SIZE_FINGER);
2374 gtk_widget_set_no_show_all (parent_priv->toolbar, TRUE);
2376 priv->next_toolitem = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/ToolbarMessageNext");
2377 priv->prev_toolitem = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/ToolbarMessageBack");
2378 toolbar_resize (MODEST_MSG_VIEW_WINDOW (self));
2380 modest_window_add_toolbar (MODEST_WINDOW (self),
2381 GTK_TOOLBAR (parent_priv->toolbar));
2386 /* Quick hack: this prevents toolbar icons "dance" when progress bar show status is changed */
2387 /* TODO: resize mode migth be GTK_RESIZE_QUEUE, in order to avoid unneccesary shows */
2388 gtk_container_set_resize_mode (GTK_CONTAINER(parent_priv->toolbar), GTK_RESIZE_IMMEDIATE);
2390 gtk_widget_show (GTK_WIDGET (parent_priv->toolbar));
2391 if (modest_msg_view_window_transfer_mode_enabled (MODEST_MSG_VIEW_WINDOW (self)))
2392 set_progress_hint (MODEST_MSG_VIEW_WINDOW (self), TRUE);
2394 set_progress_hint (MODEST_MSG_VIEW_WINDOW (self), FALSE);
2397 gtk_widget_set_no_show_all (parent_priv->toolbar, TRUE);
2398 gtk_widget_hide (GTK_WIDGET (parent_priv->toolbar));
2403 modest_msg_view_window_clipboard_owner_change (GtkClipboard *clipboard,
2405 ModestMsgViewWindow *window)
2407 if (!GTK_WIDGET_VISIBLE (window))
2410 modest_window_check_dimming_rules_group (MODEST_WINDOW (window), MODEST_DIMMING_RULES_CLIPBOARD);
2414 modest_msg_view_window_transfer_mode_enabled (ModestMsgViewWindow *self)
2416 ModestMsgViewWindowPrivate *priv;
2418 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self), FALSE);
2419 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(self);
2421 return priv->progress_hint;
2425 observers_empty (ModestMsgViewWindow *self)
2428 ModestMsgViewWindowPrivate *priv;
2429 gboolean is_empty = TRUE;
2430 guint pending_ops = 0;
2432 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(self);
2433 tmp = priv->progress_widgets;
2435 /* Check all observers */
2436 while (tmp && is_empty) {
2437 pending_ops = modest_progress_object_num_pending_operations (MODEST_PROGRESS_OBJECT(tmp->data));
2438 is_empty = pending_ops == 0;
2440 tmp = g_slist_next(tmp);
2447 on_account_removed (TnyAccountStore *account_store,
2448 TnyAccount *account,
2451 /* Do nothing if it's a transport account, because we only
2452 show the messages of a store account */
2453 if (tny_account_get_account_type(account) == TNY_ACCOUNT_TYPE_STORE) {
2454 const gchar *parent_acc = NULL;
2455 const gchar *our_acc = NULL;
2457 our_acc = modest_window_get_active_account (MODEST_WINDOW (user_data));
2458 parent_acc = modest_tny_account_get_parent_modest_account_name_for_server_account (account);
2460 /* Close this window if I'm showing a message of the removed account */
2461 if (our_acc && parent_acc && strcmp (parent_acc, our_acc) == 0)
2462 modest_ui_actions_on_close_window (NULL, MODEST_WINDOW (user_data));
2467 on_mail_operation_started (ModestMailOperation *mail_op,
2470 ModestMsgViewWindow *self;
2471 ModestMailOperationTypeOperation op_type;
2473 ModestMsgViewWindowPrivate *priv;
2474 GObject *source = NULL;
2476 self = MODEST_MSG_VIEW_WINDOW (user_data);
2477 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
2478 op_type = modest_mail_operation_get_type_operation (mail_op);
2479 tmp = priv->progress_widgets;
2480 source = modest_mail_operation_get_source(mail_op);
2481 if (G_OBJECT (self) == source) {
2482 if (op_type == MODEST_MAIL_OPERATION_TYPE_RECEIVE ||
2483 op_type == MODEST_MAIL_OPERATION_TYPE_OPEN ||
2484 op_type == MODEST_MAIL_OPERATION_TYPE_DELETE) {
2485 set_progress_hint (self, TRUE);
2487 modest_progress_object_add_operation (
2488 MODEST_PROGRESS_OBJECT (tmp->data),
2490 tmp = g_slist_next (tmp);
2494 g_object_unref (source);
2496 /* Update dimming rules */
2497 check_dimming_rules_after_change (self);
2501 on_mail_operation_finished (ModestMailOperation *mail_op,
2504 ModestMsgViewWindow *self;
2505 ModestMailOperationTypeOperation op_type;
2507 ModestMsgViewWindowPrivate *priv;
2509 self = MODEST_MSG_VIEW_WINDOW (user_data);
2510 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
2511 op_type = modest_mail_operation_get_type_operation (mail_op);
2512 tmp = priv->progress_widgets;
2514 if (op_type == MODEST_MAIL_OPERATION_TYPE_RECEIVE ||
2515 op_type == MODEST_MAIL_OPERATION_TYPE_OPEN ||
2516 op_type == MODEST_MAIL_OPERATION_TYPE_DELETE) {
2518 modest_progress_object_remove_operation (MODEST_PROGRESS_OBJECT (tmp->data),
2520 tmp = g_slist_next (tmp);
2523 /* If no more operations are being observed, NORMAL mode is enabled again */
2524 if (observers_empty (self)) {
2525 set_progress_hint (self, FALSE);
2529 /* Update dimming rules. We have to do this right here
2530 and not in view_msg_cb because at that point the
2531 transfer mode is still enabled so the dimming rule
2532 won't let the user delete the message that has been
2533 readed for example */
2534 check_dimming_rules_after_change (self);
2538 on_queue_changed (ModestMailOperationQueue *queue,
2539 ModestMailOperation *mail_op,
2540 ModestMailOperationQueueNotification type,
2541 ModestMsgViewWindow *self)
2543 ModestMsgViewWindowPrivate *priv;
2545 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
2547 /* If this operations was created by another window, do nothing */
2548 if (!modest_mail_operation_is_mine (mail_op, G_OBJECT(self)))
2551 if (type == MODEST_MAIL_OPERATION_QUEUE_OPERATION_ADDED) {
2552 priv->sighandlers = modest_signal_mgr_connect (priv->sighandlers,
2554 "operation-started",
2555 G_CALLBACK (on_mail_operation_started),
2557 priv->sighandlers = modest_signal_mgr_connect (priv->sighandlers,
2559 "operation-finished",
2560 G_CALLBACK (on_mail_operation_finished),
2562 } else if (type == MODEST_MAIL_OPERATION_QUEUE_OPERATION_REMOVED) {
2563 priv->sighandlers = modest_signal_mgr_disconnect (priv->sighandlers,
2565 "operation-started");
2566 priv->sighandlers = modest_signal_mgr_disconnect (priv->sighandlers,
2568 "operation-finished");
2573 modest_msg_view_window_get_attachments (ModestMsgViewWindow *win)
2575 ModestMsgViewWindowPrivate *priv;
2576 TnyList *selected_attachments = NULL;
2578 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (win), NULL);
2579 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (win);
2581 /* In Hildon 2.2 as there's no selection we assume we have all attachments selected */
2582 selected_attachments = modest_msg_view_get_attachments (MODEST_MSG_VIEW (priv->msg_view));
2584 return selected_attachments;
2588 ModestMsgViewWindow *self;
2590 gchar *attachment_uid;
2591 } DecodeAsyncHelper;
2594 on_decode_to_stream_async_handler (TnyMimePart *mime_part,
2600 DecodeAsyncHelper *helper = (DecodeAsyncHelper *) user_data;
2601 const gchar *content_type;
2603 if (cancelled || err) {
2606 if ((err->domain == TNY_ERROR_DOMAIN) &&
2607 (err->code == TNY_IO_ERROR_WRITE) &&
2608 (errno == ENOSPC)) {
2609 msg = g_strdup_printf (_KR("cerm_device_memory_full"), "");
2611 msg = g_strdup (_("mail_ib_file_operation_failed"));
2613 modest_platform_information_banner (NULL, NULL, msg);
2619 /* It could happen that the window was closed. So we
2620 assume it is a cancelation */
2621 if (!GTK_WIDGET_VISIBLE (helper->self))
2624 /* Remove the progress hint */
2625 set_progress_hint (helper->self, FALSE);
2627 content_type = tny_mime_part_get_content_type (mime_part);
2628 if (g_str_has_prefix (content_type, "message/rfc822")) {
2629 ModestWindowMgr *mgr;
2630 ModestWindow *msg_win = NULL;
2633 const gchar *mailbox;
2634 TnyStream *file_stream;
2637 fd = g_open (helper->file_path, O_RDONLY, 0644);
2639 file_stream = tny_fs_stream_new (fd);
2641 mgr = modest_runtime_get_window_mgr ();
2643 account = g_strdup (modest_window_get_active_account (MODEST_WINDOW (helper->self)));
2644 mailbox = modest_window_get_active_mailbox (MODEST_WINDOW (helper->self));
2647 account = modest_account_mgr_get_default_account (modest_runtime_get_account_mgr ());
2649 msg = tny_camel_msg_new ();
2650 tny_camel_msg_parse (msg, file_stream);
2651 msg_win = modest_msg_view_window_new_for_attachment (TNY_MSG (msg), account, mailbox, helper->attachment_uid);
2652 modest_window_set_zoom (MODEST_WINDOW (msg_win),
2653 modest_window_get_zoom (MODEST_WINDOW (helper->self)));
2654 if (modest_window_mgr_register_window (mgr, msg_win, MODEST_WINDOW (helper->self)))
2655 gtk_widget_show_all (GTK_WIDGET (msg_win));
2657 gtk_widget_destroy (GTK_WIDGET (msg_win));
2658 g_object_unref (msg);
2659 g_object_unref (file_stream);
2661 modest_platform_information_banner (NULL, NULL, _("mail_ib_file_operation_failed"));
2666 /* make the file read-only */
2667 g_chmod(helper->file_path, 0444);
2669 /* Activate the file */
2670 modest_platform_activate_file (helper->file_path, tny_mime_part_get_content_type (mime_part));
2675 g_object_unref (helper->self);
2676 g_free (helper->file_path);
2677 g_free (helper->attachment_uid);
2678 g_slice_free (DecodeAsyncHelper, helper);
2682 modest_msg_view_window_view_attachment (ModestMsgViewWindow *window,
2683 TnyMimePart *mime_part)
2685 ModestMsgViewWindowPrivate *priv;
2686 const gchar *msg_uid;
2687 gchar *attachment_uid = NULL;
2688 gint attachment_index = 0;
2689 TnyList *attachments;
2691 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window));
2692 g_return_if_fail (TNY_IS_MIME_PART (mime_part) || (mime_part == NULL));
2693 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
2695 msg_uid = modest_msg_view_window_get_message_uid (MODEST_MSG_VIEW_WINDOW (window));
2696 attachments = modest_msg_view_get_attachments (MODEST_MSG_VIEW (priv->msg_view));
2697 attachment_index = modest_list_index (attachments, (GObject *) mime_part);
2698 g_object_unref (attachments);
2700 if (msg_uid && attachment_index >= 0) {
2701 attachment_uid = g_strdup_printf ("%s/%d", msg_uid, attachment_index);
2704 if (mime_part == NULL) {
2705 gboolean error = FALSE;
2706 TnyList *selected_attachments = modest_msg_view_get_selected_attachments (MODEST_MSG_VIEW (priv->msg_view));
2707 if (selected_attachments == NULL || tny_list_get_length (selected_attachments) == 0) {
2709 } else if (tny_list_get_length (selected_attachments) > 1) {
2710 modest_platform_system_banner (NULL, NULL, _("mcen_ib_unable_to_display_more"));
2714 iter = tny_list_create_iterator (selected_attachments);
2715 mime_part = (TnyMimePart *) tny_iterator_get_current (iter);
2716 g_object_unref (iter);
2718 if (selected_attachments)
2719 g_object_unref (selected_attachments);
2724 g_object_ref (mime_part);
2727 if (tny_mime_part_is_purged (mime_part))
2730 if (!modest_tny_mime_part_is_msg (mime_part) && tny_mime_part_get_filename (mime_part)) {
2731 gchar *filepath = NULL;
2732 const gchar *att_filename = tny_mime_part_get_filename (mime_part);
2733 gboolean show_error_banner = FALSE;
2734 TnyFsStream *temp_stream = NULL;
2735 temp_stream = modest_utils_create_temp_stream (att_filename, attachment_uid,
2738 if (temp_stream != NULL) {
2739 ModestAccountMgr *mgr;
2740 DecodeAsyncHelper *helper;
2741 gboolean decode_in_provider;
2742 ModestProtocol *protocol;
2743 const gchar *account;
2745 /* Activate progress hint */
2746 set_progress_hint (window, TRUE);
2748 helper = g_slice_new0 (DecodeAsyncHelper);
2749 helper->self = g_object_ref (window);
2750 helper->file_path = g_strdup (filepath);
2751 helper->attachment_uid = g_strdup (attachment_uid);
2753 decode_in_provider = FALSE;
2754 mgr = modest_runtime_get_account_mgr ();
2755 account = modest_window_get_active_account (MODEST_WINDOW (window));
2756 if (modest_account_mgr_account_is_multimailbox (mgr, account, &protocol)) {
2757 if (MODEST_IS_ACCOUNT_PROTOCOL (protocol)) {
2759 uri = g_strconcat ("file://", filepath, NULL);
2760 decode_in_provider =
2761 modest_account_protocol_decode_part_to_stream_async (
2762 MODEST_ACCOUNT_PROTOCOL (protocol),
2765 TNY_STREAM (temp_stream),
2766 on_decode_to_stream_async_handler,
2773 if (!decode_in_provider)
2774 tny_mime_part_decode_to_stream_async (mime_part, TNY_STREAM (temp_stream),
2775 on_decode_to_stream_async_handler,
2778 g_object_unref (temp_stream);
2779 /* NOTE: files in the temporary area will be automatically
2780 * cleaned after some time if they are no longer in use */
2783 const gchar *content_type;
2784 /* the file may already exist but it isn't writable,
2785 * let's try to open it anyway */
2786 content_type = tny_mime_part_get_content_type (mime_part);
2787 modest_platform_activate_file (filepath, content_type);
2789 g_warning ("%s: modest_utils_create_temp_stream failed", __FUNCTION__);
2790 show_error_banner = TRUE;
2795 if (show_error_banner)
2796 modest_platform_information_banner (NULL, NULL, _("mail_ib_file_operation_failed"));
2797 } else if (!modest_tny_mime_part_is_msg (mime_part)) {
2798 ModestWindowMgr *mgr;
2799 ModestWindow *msg_win = NULL;
2800 TnyMsg *current_msg;
2804 current_msg = modest_msg_view_window_get_message (MODEST_MSG_VIEW_WINDOW (window));
2805 mgr = modest_runtime_get_window_mgr ();
2806 header = tny_msg_get_header (TNY_MSG (current_msg));
2807 found = modest_window_mgr_find_registered_message_uid (mgr,
2812 g_debug ("window for this body is already being created");
2815 /* it's not found, so create a new window for it */
2816 modest_window_mgr_register_header (mgr, header, attachment_uid); /* register the uid before building the window */
2817 gchar *account = g_strdup (modest_window_get_active_account (MODEST_WINDOW (window)));
2818 const gchar *mailbox = modest_window_get_active_mailbox (MODEST_WINDOW (window));
2820 account = modest_account_mgr_get_default_account (modest_runtime_get_account_mgr ());
2822 msg_win = modest_msg_view_window_new_with_other_body (TNY_MSG (current_msg), TNY_MIME_PART (mime_part),
2823 account, mailbox, attachment_uid);
2825 modest_window_set_zoom (MODEST_WINDOW (msg_win),
2826 modest_window_get_zoom (MODEST_WINDOW (window)));
2827 if (modest_window_mgr_register_window (mgr, msg_win, MODEST_WINDOW (window)))
2828 gtk_widget_show_all (GTK_WIDGET (msg_win));
2830 gtk_widget_destroy (GTK_WIDGET (msg_win));
2832 g_object_unref (current_msg);
2834 /* message attachment */
2835 TnyHeader *header = NULL;
2836 ModestWindowMgr *mgr;
2837 ModestWindow *msg_win = NULL;
2840 header = tny_msg_get_header (TNY_MSG (mime_part));
2841 mgr = modest_runtime_get_window_mgr ();
2842 found = modest_window_mgr_find_registered_header (mgr, header, &msg_win);
2845 /* if it's found, but there is no msg_win, it's probably in the process of being created;
2846 * thus, we don't do anything */
2847 g_debug ("window for is already being created");
2849 /* it's not found, so create a new window for it */
2850 modest_window_mgr_register_header (mgr, header, attachment_uid); /* register the uid before building the window */
2851 gchar *account = g_strdup (modest_window_get_active_account (MODEST_WINDOW (window)));
2852 const gchar *mailbox = modest_window_get_active_mailbox (MODEST_WINDOW (window));
2854 account = modest_account_mgr_get_default_account (modest_runtime_get_account_mgr ());
2855 msg_win = modest_msg_view_window_new_for_attachment (TNY_MSG (mime_part), account,
2856 mailbox, attachment_uid);
2857 modest_window_set_zoom (MODEST_WINDOW (msg_win),
2858 modest_window_get_zoom (MODEST_WINDOW (window)));
2859 if (modest_window_mgr_register_window (mgr, msg_win, MODEST_WINDOW (window)))
2860 gtk_widget_show_all (GTK_WIDGET (msg_win));
2862 gtk_widget_destroy (GTK_WIDGET (msg_win));
2868 g_free (attachment_uid);
2870 g_object_unref (mime_part);
2882 GnomeVFSResult result;
2884 ModestMsgViewWindow *window;
2887 static void save_mime_part_info_free (SaveMimePartInfo *info, gboolean with_struct);
2888 static gboolean idle_save_mime_part_show_result (SaveMimePartInfo *info);
2889 static gpointer save_mime_part_to_file (SaveMimePartInfo *info);
2890 static void save_mime_parts_to_file_with_checks (GtkWindow *parent, SaveMimePartInfo *info);
2893 save_mime_part_info_free (SaveMimePartInfo *info, gboolean with_struct)
2897 for (node = info->pairs; node != NULL; node = g_list_next (node)) {
2898 SaveMimePartPair *pair = (SaveMimePartPair *) node->data;
2899 g_free (pair->filename);
2900 g_object_unref (pair->part);
2901 g_slice_free (SaveMimePartPair, pair);
2903 g_list_free (info->pairs);
2906 g_object_unref (info->window);
2907 info->window = NULL;
2909 g_slice_free (SaveMimePartInfo, info);
2914 idle_save_mime_part_show_result (SaveMimePartInfo *info)
2916 /* This is a GDK lock because we are an idle callback and
2917 * modest_platform_system_banner is or does Gtk+ code */
2919 gdk_threads_enter (); /* CHECKED */
2920 if (info->result == GNOME_VFS_OK) {
2921 modest_platform_system_banner (NULL, NULL, _CS("sfil_ib_saved"));
2922 } else if (info->result == GNOME_VFS_ERROR_NO_SPACE) {
2925 /* Check if the uri belongs to the external mmc */
2926 if (g_str_has_prefix (info->uri, g_getenv (MODEST_MMC1_VOLUMEPATH_ENV)))
2927 msg = g_strdup_printf (_KR("cerm_device_memory_full"), "");
2929 msg = g_strdup (_KR("cerm_memory_card_full"));
2930 modest_platform_information_banner (NULL, NULL, msg);
2933 modest_platform_system_banner (NULL, NULL, _("mail_ib_file_operation_failed"));
2935 save_mime_part_info_free (info, FALSE);
2936 gdk_threads_leave (); /* CHECKED */
2942 save_mime_part_to_file (SaveMimePartInfo *info)
2944 GnomeVFSHandle *handle;
2946 SaveMimePartPair *pair = (SaveMimePartPair *) info->pairs->data;
2948 info->result = gnome_vfs_create (&handle, pair->filename, GNOME_VFS_OPEN_WRITE, FALSE, 0644);
2949 if (info->result == GNOME_VFS_OK) {
2950 GError *error = NULL;
2951 gboolean decode_in_provider;
2953 ModestAccountMgr *mgr;
2954 const gchar *account;
2955 ModestProtocol *protocol = NULL;
2957 stream = tny_vfs_stream_new (handle);
2959 decode_in_provider = FALSE;
2960 mgr = modest_runtime_get_account_mgr ();
2961 account = modest_window_get_active_account (MODEST_WINDOW (info->window));
2962 if (modest_account_mgr_account_is_multimailbox (mgr, account, &protocol)) {
2963 if (MODEST_IS_ACCOUNT_PROTOCOL (protocol)) {
2964 decode_in_provider =
2965 modest_account_protocol_decode_part_to_stream (
2966 MODEST_ACCOUNT_PROTOCOL (protocol),
2974 if (!decode_in_provider)
2975 written = tny_mime_part_decode_to_stream (pair->part, stream, &error);
2978 g_warning ("modest: could not save attachment %s: %d (%s)\n", pair->filename, error?error->code:-1, error?error->message:"Unknown error");
2980 if ((error->domain == TNY_ERROR_DOMAIN) &&
2981 (error->code == TNY_IO_ERROR_WRITE) &&
2982 (errno == ENOSPC)) {
2983 info->result = GNOME_VFS_ERROR_NO_SPACE;
2985 info->result = GNOME_VFS_ERROR_IO;
2988 g_object_unref (G_OBJECT (stream));
2990 g_warning ("Could not create save attachment %s: %s\n",
2991 pair->filename, gnome_vfs_result_to_string (info->result));
2994 /* Go on saving remaining files */
2995 info->pairs = g_list_remove_link (info->pairs, info->pairs);
2996 if (info->pairs != NULL) {
2997 save_mime_part_to_file (info);
2999 g_idle_add ((GSourceFunc) idle_save_mime_part_show_result, info);
3006 save_mime_parts_to_file_with_checks (GtkWindow *parent,
3007 SaveMimePartInfo *info)
3009 gboolean is_ok = TRUE;
3010 gint replaced_files = 0;
3011 const GList *files = info->pairs;
3012 const GList *iter, *to_replace = NULL;
3014 for (iter = files; (iter != NULL) && (replaced_files < 2); iter = g_list_next(iter)) {
3015 SaveMimePartPair *pair = iter->data;
3016 gchar *unescaped = g_uri_unescape_string (pair->filename, NULL);
3018 if (modest_utils_file_exists (unescaped)) {
3020 if (replaced_files == 1)
3025 if (replaced_files) {
3028 if (replaced_files == 1) {
3029 SaveMimePartPair *pair = to_replace->data;
3030 const gchar *basename = strrchr (pair->filename, G_DIR_SEPARATOR) + 1;
3031 gchar *escaped_basename, *message;
3033 escaped_basename = g_uri_unescape_string (basename, NULL);
3034 message = g_strdup_printf ("%s\n%s",
3035 _FM("docm_nc_replace_file"),
3036 (escaped_basename) ? escaped_basename : "");
3037 response = modest_platform_run_confirmation_dialog (parent, message);
3039 g_free (escaped_basename);
3041 response = modest_platform_run_confirmation_dialog (parent,
3042 _FM("docm_nc_replace_multiple"));
3044 if (response != GTK_RESPONSE_OK)
3049 save_mime_part_info_free (info, TRUE);
3051 g_thread_create ((GThreadFunc)save_mime_part_to_file, info, FALSE, NULL);
3056 typedef struct _SaveAttachmentsInfo {
3057 TnyList *attachments_list;
3058 ModestMsgViewWindow *window;
3059 } SaveAttachmentsInfo;
3062 save_attachments_response (GtkDialog *dialog,
3066 TnyList *mime_parts;
3068 GList *files_to_save = NULL;
3069 gchar *current_folder;
3070 SaveAttachmentsInfo *sa_info = (SaveAttachmentsInfo *) user_data;
3072 mime_parts = TNY_LIST (sa_info->attachments_list);
3074 if (arg1 != GTK_RESPONSE_OK)
3077 chooser_uri = gtk_file_chooser_get_uri (GTK_FILE_CHOOSER (dialog));
3078 current_folder = gtk_file_chooser_get_current_folder_uri (GTK_FILE_CHOOSER (dialog));
3079 if (current_folder && *current_folder != '\0') {
3081 modest_conf_set_string (modest_runtime_get_conf (), MODEST_CONF_LATEST_SAVE_ATTACHMENT_PATH,
3082 current_folder,&err);
3084 g_debug ("Error storing latest used folder: %s", err->message);
3088 g_free (current_folder);
3090 if (!modest_utils_folder_writable (chooser_uri)) {
3091 const gchar *err_msg;
3093 #ifdef MODEST_PLATFORM_MAEMO
3094 if (modest_maemo_utils_in_usb_mode ()) {
3095 err_msg = dgettext ("hildon-status-bar-usb", "usbh_ib_mmc_usb_connected");
3097 err_msg = _FM("sfil_ib_readonly_location");
3100 err_msg = _FM("sfil_ib_readonly_location");
3102 modest_platform_system_banner (NULL, NULL, err_msg);
3106 iter = tny_list_create_iterator (mime_parts);
3107 while (!tny_iterator_is_done (iter)) {
3108 TnyMimePart *mime_part = (TnyMimePart *) tny_iterator_get_current (iter);
3110 if ((modest_tny_mime_part_is_attachment_for_modest (mime_part)) &&
3111 !tny_mime_part_is_purged (mime_part) &&
3112 (tny_mime_part_get_filename (mime_part) != NULL)) {
3113 SaveMimePartPair *pair;
3115 pair = g_slice_new0 (SaveMimePartPair);
3117 if (tny_list_get_length (mime_parts) > 1) {
3119 gnome_vfs_escape_slashes (tny_mime_part_get_filename (mime_part));
3120 pair->filename = g_build_filename (chooser_uri, escaped, NULL);
3123 pair->filename = g_strdup (chooser_uri);
3125 pair->part = mime_part;
3126 files_to_save = g_list_prepend (files_to_save, pair);
3128 tny_iterator_next (iter);
3130 g_object_unref (iter);
3133 if (files_to_save != NULL) {
3134 SaveMimePartInfo *info = g_slice_new0 (SaveMimePartInfo);
3135 info->pairs = files_to_save;
3136 info->result = TRUE;
3137 info->uri = g_strdup (chooser_uri);
3138 info->window = g_object_ref (sa_info->window);
3139 save_mime_parts_to_file_with_checks ((GtkWindow *) dialog, info);
3141 g_free (chooser_uri);
3144 /* Free and close the dialog */
3145 g_object_unref (mime_parts);
3146 g_object_unref (sa_info->window);
3147 g_slice_free (SaveAttachmentsInfo, sa_info);
3148 gtk_widget_destroy (GTK_WIDGET (dialog));
3152 modest_msg_view_window_save_attachments (ModestMsgViewWindow *window,
3153 TnyList *mime_parts)
3155 ModestMsgViewWindowPrivate *priv;
3156 GtkWidget *save_dialog = NULL;
3157 gchar *conf_folder = NULL;
3158 gchar *filename = NULL;
3159 gchar *save_multiple_str = NULL;
3160 const gchar *root_folder = "file:///";
3162 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window));
3163 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
3165 if (mime_parts == NULL) {
3166 /* In Hildon 2.2 save and delete operate over all the attachments as there's no
3167 * selection available */
3168 mime_parts = modest_msg_view_get_attachments (MODEST_MSG_VIEW (priv->msg_view));
3169 if (mime_parts && !modest_maemo_utils_select_attachments (GTK_WINDOW (window), mime_parts, FALSE)) {
3170 g_object_unref (mime_parts);
3173 if (mime_parts == NULL || tny_list_get_length (mime_parts) == 0) {
3175 g_object_unref (mime_parts);
3181 g_object_ref (mime_parts);
3184 /* prepare dialog */
3185 if (tny_list_get_length (mime_parts) == 1) {
3187 /* only one attachment selected */
3188 iter = tny_list_create_iterator (mime_parts);
3189 TnyMimePart *mime_part = (TnyMimePart *) tny_iterator_get_current (iter);
3190 g_object_unref (iter);
3191 if (!modest_tny_mime_part_is_msg (mime_part) &&
3192 modest_tny_mime_part_is_attachment_for_modest (mime_part) &&
3193 !tny_mime_part_is_purged (mime_part)) {
3194 filename = g_strdup (tny_mime_part_get_filename (mime_part));
3196 /* TODO: show any error? */
3197 g_warning ("%s: Tried to save a non-file attachment", __FUNCTION__);
3198 g_object_unref (mime_parts);
3201 g_object_unref (mime_part);
3203 gint num = tny_list_get_length (mime_parts);
3204 save_multiple_str = g_strdup_printf (dngettext("hildon-fm",
3205 "sfil_va_number_of_objects_attachment",
3206 "sfil_va_number_of_objects_attachments",
3210 /* Creation of hildon file chooser dialog for saving */
3211 save_dialog = modest_toolkit_factory_create_file_chooser_dialog (modest_runtime_get_toolkit_factory (),
3213 (GtkWindow *) window,
3214 GTK_FILE_CHOOSER_ACTION_SAVE);
3216 /* Get last used folder */
3217 conf_folder = modest_conf_get_string (modest_runtime_get_conf (),
3218 MODEST_CONF_LATEST_SAVE_ATTACHMENT_PATH, NULL);
3220 /* File chooser stops working if we select "file:///" as current folder */
3221 if (conf_folder && g_ascii_strcasecmp (root_folder, conf_folder) != 0) {
3222 g_free (conf_folder);
3226 if (conf_folder && conf_folder[0] != '\0') {
3227 gtk_file_chooser_set_current_folder_uri (GTK_FILE_CHOOSER (save_dialog), conf_folder);
3230 /* Set the default folder to documents folder */
3231 docs_folder = (gchar *) g_strdup(g_get_user_special_dir (G_USER_DIRECTORY_DOCUMENTS));
3234 docs_folder = g_build_filename (g_getenv (MYDOCS_ENV), DOCS_FOLDER, NULL);
3236 gtk_file_chooser_set_current_folder (GTK_FILE_CHOOSER (save_dialog), docs_folder);
3237 g_free (docs_folder);
3239 g_free (conf_folder);
3243 gtk_file_chooser_set_current_name (GTK_FILE_CHOOSER (save_dialog),
3248 /* if multiple, set multiple string */
3249 if (save_multiple_str) {
3250 g_object_set (G_OBJECT (save_dialog), "save-multiple", save_multiple_str, NULL);
3251 gtk_window_set_title (GTK_WINDOW (save_dialog), _FM("sfil_ti_save_objects_files"));
3252 g_free (save_multiple_str);
3255 /* We must run this asynchronously, because the hildon dialog
3256 performs a gtk_dialog_run by itself which leads to gdk
3258 SaveAttachmentsInfo *sa_info;
3259 sa_info = g_slice_new (SaveAttachmentsInfo);
3260 sa_info->attachments_list = mime_parts;
3261 sa_info->window = g_object_ref (window);
3262 g_signal_connect (save_dialog, "response",
3263 G_CALLBACK (save_attachments_response), sa_info);
3265 gtk_widget_show_all (save_dialog);
3269 show_remove_attachment_information (gpointer userdata)
3271 ModestMsgViewWindow *window = (ModestMsgViewWindow *) userdata;
3272 ModestMsgViewWindowPrivate *priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
3274 /* We're outside the main lock */
3275 gdk_threads_enter ();
3277 if (priv->remove_attachment_banner != NULL) {
3278 gtk_widget_destroy (priv->remove_attachment_banner);
3279 g_object_unref (priv->remove_attachment_banner);
3282 priv->remove_attachment_banner = g_object_ref (
3283 modest_platform_animation_banner (NULL, NULL, _("mcen_me_inbox_remove_attachments")));
3285 gdk_threads_leave ();
3291 modest_msg_view_window_remove_attachments (ModestMsgViewWindow *window, gboolean get_all)
3293 ModestMsgViewWindowPrivate *priv;
3294 TnyList *mime_parts = NULL, *tmp;
3295 gchar *confirmation_message;
3301 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window));
3302 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
3304 /* In hildon 2.2 we ignore the get_all flag as we always get all attachments. This is
3305 * because we don't have selection
3307 mime_parts = modest_msg_view_get_attachments (MODEST_MSG_VIEW (priv->msg_view));
3309 /* Remove already purged messages from mime parts list. We use
3310 a copy of the list to remove items in the original one */
3311 tmp = tny_list_copy (mime_parts);
3312 iter = tny_list_create_iterator (tmp);
3313 while (!tny_iterator_is_done (iter)) {
3314 TnyMimePart *part = TNY_MIME_PART (tny_iterator_get_current (iter));
3315 if (tny_mime_part_is_purged (part))
3316 tny_list_remove (mime_parts, (GObject *) part);
3318 g_object_unref (part);
3319 tny_iterator_next (iter);
3321 g_object_unref (tmp);
3322 g_object_unref (iter);
3324 if (!modest_maemo_utils_select_attachments (GTK_WINDOW (window), mime_parts, TRUE) ||
3325 tny_list_get_length (mime_parts) == 0) {
3326 g_object_unref (mime_parts);
3330 n_attachments = tny_list_get_length (mime_parts);
3331 if (n_attachments == 1) {
3335 iter = tny_list_create_iterator (mime_parts);
3336 part = (TnyMimePart *) tny_iterator_get_current (iter);
3337 g_object_unref (iter);
3338 if (modest_tny_mime_part_is_msg (part)) {
3340 header = tny_msg_get_header (TNY_MSG (part));
3341 filename = tny_header_dup_subject (header);
3342 g_object_unref (header);
3343 if (filename == NULL)
3344 filename = g_strdup (_("mail_va_no_subject"));
3346 filename = g_strdup (tny_mime_part_get_filename (TNY_MIME_PART (part)));
3348 confirmation_message = g_strdup_printf (_("mcen_nc_purge_file_text"), filename);
3350 g_object_unref (part);
3352 confirmation_message = g_strdup_printf (ngettext("mcen_nc_purge_file_text",
3353 "mcen_nc_purge_files_text",
3354 n_attachments), n_attachments);
3356 response = modest_platform_run_confirmation_dialog (GTK_WINDOW (window),
3357 confirmation_message);
3358 g_free (confirmation_message);
3360 if (response != GTK_RESPONSE_OK) {
3361 g_object_unref (mime_parts);
3365 priv->purge_timeout = g_timeout_add (2000, show_remove_attachment_information, window);
3367 iter = tny_list_create_iterator (mime_parts);
3368 while (!tny_iterator_is_done (iter)) {
3371 part = (TnyMimePart *) tny_iterator_get_current (iter);
3372 tny_mime_part_set_purged (TNY_MIME_PART (part));
3373 g_object_unref (part);
3374 tny_iterator_next (iter);
3376 g_object_unref (iter);
3378 msg = tny_msg_view_get_msg (TNY_MSG_VIEW (priv->msg_view));
3379 tny_msg_view_clear (TNY_MSG_VIEW (priv->msg_view));
3380 tny_msg_rewrite_cache (msg);
3381 tny_msg_view_set_msg (TNY_MSG_VIEW (priv->msg_view), msg);
3382 g_object_unref (msg);
3383 update_branding (MODEST_MSG_VIEW_WINDOW (window));
3385 g_object_unref (mime_parts);
3387 if (priv->purge_timeout > 0) {
3388 g_source_remove (priv->purge_timeout);
3389 priv->purge_timeout = 0;
3392 if (priv->remove_attachment_banner) {
3393 gtk_widget_destroy (priv->remove_attachment_banner);
3394 g_object_unref (priv->remove_attachment_banner);
3395 priv->remove_attachment_banner = NULL;
3401 update_window_title (ModestMsgViewWindow *window)
3403 ModestMsgViewWindowPrivate *priv;
3405 TnyHeader *header = NULL;
3406 gchar *subject = NULL;
3408 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
3410 /* Note that if the window is closed while we're retrieving
3411 the message, this widget could de deleted */
3412 if (!priv->msg_view)
3415 msg = tny_msg_view_get_msg (TNY_MSG_VIEW (priv->msg_view));
3417 if (priv->other_body) {
3420 description = modest_tny_mime_part_get_header_value (priv->other_body, "Content-Description");
3422 g_strstrip (description);
3423 subject = description;
3425 } else if (msg != NULL) {
3426 header = tny_msg_get_header (msg);
3427 subject = tny_header_dup_subject (header);
3428 g_object_unref (header);
3429 g_object_unref (msg);
3432 if ((subject == NULL)||(subject[0] == '\0')) {
3434 subject = g_strdup (_("mail_va_no_subject"));
3437 modest_window_set_title (MODEST_WINDOW (window), subject);
3442 on_move_focus (GtkWidget *widget,
3443 GtkDirectionType direction,
3446 g_signal_stop_emission_by_name (G_OBJECT (widget), "move-focus");
3450 fetch_image_open_stream (TnyStreamCache *self, gint64 *expected_size, gchar *uri)
3452 GnomeVFSResult result;
3453 GnomeVFSHandle *handle = NULL;
3454 GnomeVFSFileInfo *info = NULL;
3457 result = gnome_vfs_open (&handle, uri, GNOME_VFS_OPEN_READ);
3458 if (result != GNOME_VFS_OK) {
3463 info = gnome_vfs_file_info_new ();
3464 result = gnome_vfs_get_file_info_from_handle (handle, info, GNOME_VFS_FILE_INFO_DEFAULT);
3465 if (result != GNOME_VFS_OK || ! (info->valid_fields & GNOME_VFS_FILE_INFO_FIELDS_SIZE)) {
3466 /* We put a "safe" default size for going to cache */
3467 *expected_size = (300*1024);
3469 *expected_size = info->size;
3471 gnome_vfs_file_info_unref (info);
3473 stream = tny_vfs_stream_new (handle);
3482 TnyStream *output_stream;
3483 GtkWidget *msg_view;
3488 on_fetch_image_idle_refresh_view (gpointer userdata)
3491 FetchImageData *fidata = (FetchImageData *) userdata;
3493 gdk_threads_enter ();
3494 if (GTK_WIDGET_DRAWABLE (fidata->msg_view)) {
3495 ModestMsgViewWindowPrivate *priv;
3497 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (fidata->window);
3498 priv->fetching_images--;
3499 gtk_widget_queue_draw (fidata->msg_view);
3500 update_progress_hint (MODEST_MSG_VIEW_WINDOW (fidata->window));
3502 gdk_threads_leave ();
3504 g_object_unref (fidata->msg_view);
3505 g_object_unref (fidata->window);
3506 g_slice_free (FetchImageData, fidata);
3511 on_fetch_image_thread (gpointer userdata)
3513 FetchImageData *fidata = (FetchImageData *) userdata;
3514 TnyStreamCache *cache;
3515 TnyStream *cache_stream;
3517 cache = modest_runtime_get_images_cache ();
3519 tny_stream_cache_get_stream (cache,
3521 (TnyStreamCacheOpenStreamFetcher) fetch_image_open_stream,
3522 (gpointer) fidata->uri);
3523 g_free (fidata->cache_id);
3524 g_free (fidata->uri);
3526 if (cache_stream != NULL) {
3529 while (G_LIKELY (!tny_stream_is_eos (cache_stream))) {
3532 nb_read = tny_stream_read (cache_stream, buffer, sizeof (buffer));
3533 if (G_UNLIKELY (nb_read < 0)) {
3535 } else if (G_LIKELY (nb_read > 0)) {
3536 gssize nb_written = 0;
3538 while (G_UNLIKELY (nb_written < nb_read)) {
3541 len = tny_stream_write (fidata->output_stream, buffer + nb_written,
3542 nb_read - nb_written);
3543 if (G_UNLIKELY (len < 0))
3549 tny_stream_close (cache_stream);
3550 g_object_unref (cache_stream);
3553 tny_stream_close (fidata->output_stream);
3554 g_object_unref (fidata->output_stream);
3556 g_idle_add (on_fetch_image_idle_refresh_view, fidata);
3562 on_fetch_image (ModestMsgView *msgview,
3565 ModestMsgViewWindow *window)
3567 const gchar *current_account;
3568 ModestMsgViewWindowPrivate *priv;
3569 FetchImageData *fidata;
3571 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
3573 current_account = modest_window_get_active_account (MODEST_WINDOW (window));
3575 fidata = g_slice_new0 (FetchImageData);
3576 fidata->msg_view = g_object_ref (msgview);
3577 fidata->window = g_object_ref (window);
3578 fidata->uri = g_strdup (uri);
3579 fidata->cache_id = modest_images_cache_get_id (current_account, uri);
3580 fidata->output_stream = g_object_ref (stream);
3582 priv->fetching_images++;
3583 if (g_thread_create (on_fetch_image_thread, fidata, FALSE, NULL) == NULL) {
3584 g_object_unref (fidata->output_stream);
3585 g_free (fidata->cache_id);
3586 g_free (fidata->uri);
3587 g_object_unref (fidata->msg_view);
3588 g_slice_free (FetchImageData, fidata);
3589 tny_stream_close (stream);
3590 priv->fetching_images--;
3591 update_progress_hint (window);
3594 update_progress_hint (window);
3600 setup_menu (ModestMsgViewWindow *self)
3602 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW(self));
3604 /* Settings menu buttons */
3605 modest_window_add_to_menu (MODEST_WINDOW (self), _("mcen_me_viewer_find"), NULL,
3606 MODEST_WINDOW_MENU_CALLBACK (modest_msg_view_window_show_isearch_toolbar),
3607 MODEST_DIMMING_CALLBACK (modest_ui_dimming_rules_on_find_in_msg));
3609 modest_window_add_to_menu (MODEST_WINDOW (self),
3610 dngettext(GETTEXT_PACKAGE,
3611 "mcen_me_move_message",
3612 "mcen_me_move_messages",
3615 MODEST_WINDOW_MENU_CALLBACK (modest_ui_actions_on_move_to),
3616 MODEST_DIMMING_CALLBACK (modest_ui_dimming_rules_on_move_to));
3618 modest_window_add_to_menu (MODEST_WINDOW (self), _("mcen_me_inbox_mark_as_read"), NULL,
3619 MODEST_WINDOW_MENU_CALLBACK (modest_ui_actions_on_mark_as_read),
3620 MODEST_DIMMING_CALLBACK (modest_ui_dimming_rules_on_mark_as_read_msg_in_view));
3622 modest_window_add_to_menu (MODEST_WINDOW (self), _("mcen_me_inbox_mark_as_unread"), NULL,
3623 MODEST_WINDOW_MENU_CALLBACK (modest_ui_actions_on_mark_as_unread),
3624 MODEST_DIMMING_CALLBACK (modest_ui_dimming_rules_on_mark_as_unread_msg_in_view));
3626 modest_window_add_to_menu (MODEST_WINDOW (self), _("mcen_me_viewer_save_attachments"), NULL,
3627 MODEST_WINDOW_MENU_CALLBACK (modest_ui_actions_save_attachments),
3628 MODEST_DIMMING_CALLBACK (modest_ui_dimming_rules_on_save_attachments));
3629 modest_window_add_to_menu (MODEST_WINDOW (self), _("mcen_me_inbox_remove_attachments"), NULL,
3630 MODEST_WINDOW_MENU_CALLBACK (modest_ui_actions_remove_attachments),
3631 MODEST_DIMMING_CALLBACK (modest_ui_dimming_rules_on_remove_attachments));
3633 modest_window_add_to_menu (MODEST_WINDOW (self), _("mcen_me_new_message"), "<Control>n",
3634 MODEST_WINDOW_MENU_CALLBACK (modest_ui_actions_on_new_msg),
3635 MODEST_DIMMING_CALLBACK (modest_ui_dimming_rules_on_new_msg));
3636 modest_window_add_to_menu (MODEST_WINDOW (self), _("mcen_me_viewer_addtocontacts"), NULL,
3637 MODEST_WINDOW_MENU_CALLBACK (modest_ui_actions_add_to_contacts),
3638 MODEST_DIMMING_CALLBACK (modest_ui_dimming_rules_on_add_to_contacts));
3640 modest_window_add_to_menu (MODEST_WINDOW (self), _("mcen_ti_message_properties"), NULL,
3641 MODEST_WINDOW_MENU_CALLBACK (modest_ui_actions_on_details),
3642 MODEST_DIMMING_CALLBACK (modest_ui_dimming_rules_on_details));
3646 modest_msg_view_window_add_to_contacts (ModestMsgViewWindow *self)
3648 ModestMsgViewWindowPrivate *priv;
3649 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
3650 GSList *recipients = NULL;
3653 msg = tny_msg_view_get_msg (TNY_MSG_VIEW (priv->msg_view));
3657 header = modest_msg_view_window_get_header (self);
3660 recipients = modest_tny_msg_header_get_all_recipients_list (header);
3661 g_object_unref (header);
3663 recipients = modest_tny_msg_get_all_recipients_list (msg);
3664 g_object_unref (msg);
3668 /* Offer the user to add recipients to the address book */
3669 modest_address_book_add_address_list_with_selector (recipients, (GtkWindow *) self);
3670 g_slist_foreach (recipients, (GFunc) g_free, NULL); g_slist_free (recipients);
3675 _modest_msg_view_window_map_event (GtkWidget *widget,
3679 ModestMsgViewWindow *self = (ModestMsgViewWindow *) userdata;
3681 update_progress_hint (self);
3687 modest_msg_view_window_fetch_images (ModestMsgViewWindow *self)
3689 ModestMsgViewWindowPrivate *priv;
3690 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
3692 modest_msg_view_request_fetch_images (MODEST_MSG_VIEW (priv->msg_view));
3696 modest_msg_view_window_has_blocked_external_images (ModestMsgViewWindow *self)
3698 ModestMsgViewWindowPrivate *priv;
3699 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
3701 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self), FALSE);
3703 return modest_msg_view_has_blocked_external_images (MODEST_MSG_VIEW (priv->msg_view));
3707 modest_msg_view_window_reload (ModestMsgViewWindow *self)
3709 ModestMsgViewWindowPrivate *priv;
3710 const gchar *msg_uid;
3711 TnyHeader *header = NULL;
3712 TnyFolder *folder = NULL;
3714 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self));
3716 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
3718 header = modest_msg_view_window_get_header (MODEST_MSG_VIEW_WINDOW (self));
3722 folder = tny_header_get_folder (header);
3723 g_object_unref (header);
3728 msg_uid = modest_msg_view_window_get_message_uid (self);
3730 GtkTreeRowReference *row_reference;
3732 if (priv->row_reference && gtk_tree_row_reference_valid (priv->row_reference)) {
3733 row_reference = priv->row_reference;
3735 row_reference = NULL;
3737 if (!message_reader (self, priv, NULL, msg_uid, folder, row_reference))
3738 g_warning ("Shouldn't happen, trying to reload a message failed");
3741 g_object_unref (folder);
3745 update_branding (ModestMsgViewWindow *self)
3747 const gchar *account;
3748 const gchar *mailbox;
3749 ModestAccountMgr *mgr;
3750 ModestProtocol *protocol = NULL;
3751 gchar *service_name = NULL;
3752 const GdkPixbuf *service_icon = NULL;
3753 ModestMsgViewWindowPrivate *priv;
3755 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
3757 account = modest_window_get_active_account (MODEST_WINDOW (self));
3758 mailbox = modest_window_get_active_mailbox (MODEST_WINDOW (self));
3760 mgr = modest_runtime_get_account_mgr ();
3762 if (modest_account_mgr_account_is_multimailbox (mgr, account, &protocol)) {
3763 if (MODEST_IS_ACCOUNT_PROTOCOL (protocol)) {
3764 service_name = modest_account_protocol_get_service_name (MODEST_ACCOUNT_PROTOCOL (protocol),
3766 service_icon = modest_account_protocol_get_service_icon (MODEST_ACCOUNT_PROTOCOL (protocol),
3767 account, mailbox, MODEST_ICON_SIZE_SMALL);
3771 modest_msg_view_set_branding (MODEST_MSG_VIEW (priv->msg_view), service_name, service_icon);
3772 g_free (service_name);
3776 sync_flags (ModestMsgViewWindow *self)
3778 TnyHeader *header = NULL;
3780 header = modest_msg_view_window_get_header (self);
3782 TnyMsg *msg = modest_msg_view_window_get_message (self);
3784 header = tny_msg_get_header (msg);
3785 g_object_unref (msg);
3790 TnyFolder *folder = tny_header_get_folder (header);
3793 ModestMailOperation *mail_op;
3795 /* Sync folder, we need this to save the seen flag */
3796 mail_op = modest_mail_operation_new (NULL);
3797 modest_mail_operation_queue_add (modest_runtime_get_mail_operation_queue (),
3799 modest_mail_operation_sync_folder (mail_op, folder, FALSE, NULL, NULL);
3800 g_object_unref (mail_op);
3801 g_object_unref (folder);
3803 g_object_unref (header);