1 /* Copyright (c) 2006, Nokia Corporation
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions are
8 * * Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 * * Redistributions in binary form must reproduce the above copyright
11 * notice, this list of conditions and the following disclaimer in the
12 * documentation and/or other materials provided with the distribution.
13 * * Neither the name of the Nokia Corporation nor the names of its
14 * contributors may be used to endorse or promote products derived from
15 * this software without specific prior written permission.
17 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
18 * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
19 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
20 * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
21 * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
22 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
23 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
24 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
25 * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
26 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
27 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29 #include <glib/gi18n.h>
31 #include <tny-account-store.h>
32 #include <tny-simple-list.h>
34 #include <tny-mime-part.h>
35 #include <tny-vfs-stream.h>
36 #include <tny-error.h>
37 #include "modest-marshal.h"
38 #include "modest-platform.h"
39 #include <modest-utils.h>
40 #include <modest-toolkit-utils.h>
41 #include <modest-tny-msg.h>
42 #include <modest-msg-view-window.h>
43 #include "modest-msg-view-window-ui-dimming.h"
44 #include <modest-widget-memory.h>
45 #include <modest-progress-object.h>
46 #include <modest-runtime.h>
47 #include <modest-window-priv.h>
48 #include <modest-tny-folder.h>
49 #include <modest-text-utils.h>
50 #include <modest-account-mgr-helpers.h>
51 #include <modest-toolkit-factory.h>
52 #include <modest-scrollable.h>
53 #include <modest-isearch-toolbar.h>
54 #include "modest-defs.h"
55 #include "modest-ui-dimming-manager.h"
56 #include <gdk/gdkkeysyms.h>
57 #include <modest-tny-account.h>
58 #include <modest-mime-part-view.h>
59 #include <modest-isearch-view.h>
60 #include <modest-tny-mime-part.h>
61 #include <modest-address-book.h>
64 #include <glib/gstdio.h>
65 #include <modest-debug.h>
66 #include <modest-header-window.h>
67 #include <modest-account-protocol.h>
68 #include <modest-icon-names.h>
69 #include <modest-ui-actions.h>
70 #include <modest-window-mgr.h>
71 #include <tny-camel-msg.h>
72 #include <modest-icon-names.h>
74 #ifdef MODEST_PLATFORM_MAEMO
75 #include <modest-maemo-utils.h>
78 #ifdef MODEST_TOOLKIT_HILDON2
79 #include <hildon/hildon.h>
82 #define MYDOCS_ENV "MYDOCSDIR"
83 #define DOCS_FOLDER ".documents"
85 typedef struct _ModestMsgViewWindowPrivate ModestMsgViewWindowPrivate;
86 struct _ModestMsgViewWindowPrivate {
89 GtkWidget *main_scroll;
90 GtkWidget *isearch_toolbar;
93 /* Progress observers */
94 GSList *progress_widgets;
97 GtkWidget *prev_toolitem;
98 GtkWidget *next_toolitem;
99 gboolean progress_hint;
100 gint fetching_images;
102 /* Optimized view enabled */
103 gboolean optimized_view;
105 /* Whether this was created via the *_new_for_search_result() function. */
106 gboolean is_search_result;
108 /* Whether the message is in outbox */
111 /* A reference to the @model of the header view
112 * to allow selecting previous/next messages,
113 * if the message is currently selected in the header view.
115 const gchar *header_folder_id;
116 GtkTreeModel *header_model;
117 GtkTreeRowReference *row_reference;
118 GtkTreeRowReference *next_row_reference;
120 gulong clipboard_change_handler;
121 gulong queue_change_handler;
122 gulong account_removed_handler;
123 gulong row_changed_handler;
124 gulong row_deleted_handler;
125 gulong row_inserted_handler;
126 gulong rows_reordered_handler;
129 GtkWidget *remove_attachment_banner;
132 TnyMimePart *other_body;
137 static void modest_msg_view_window_class_init (ModestMsgViewWindowClass *klass);
138 static void modest_msg_view_window_init (ModestMsgViewWindow *obj);
139 static void modest_header_view_observer_init (ModestHeaderViewObserverIface *iface_class);
140 static void modest_msg_view_window_finalize (GObject *obj);
141 static void modest_msg_view_window_show_isearch_toolbar (GtkWidget *obj, gpointer data);
142 static void modest_msg_view_window_isearch_toolbar_close (GtkWidget *widget,
143 ModestMsgViewWindow *obj);
144 static void modest_msg_view_window_isearch_toolbar_search (GtkWidget *widget,
145 ModestMsgViewWindow *obj);
146 static void modest_msg_view_window_toggle_isearch_toolbar (GtkWidget *obj,
148 static void modest_msg_view_window_disconnect_signals (ModestWindow *self);
150 static gdouble modest_msg_view_window_get_zoom (ModestWindow *window);
151 static void modest_msg_view_window_set_zoom (ModestWindow *window,
153 static gboolean modest_msg_view_window_zoom_minus (ModestWindow *window);
154 static gboolean modest_msg_view_window_zoom_plus (ModestWindow *window);
155 static gboolean modest_msg_view_window_key_event (GtkWidget *window,
158 static void modest_msg_view_window_update_priority (ModestMsgViewWindow *window);
160 static void modest_msg_view_window_show_toolbar (ModestWindow *window,
161 gboolean show_toolbar);
163 static void modest_msg_view_window_clipboard_owner_change (GtkClipboard *clipboard,
165 ModestMsgViewWindow *window);
167 static void modest_msg_view_window_on_row_changed (GtkTreeModel *header_model,
170 ModestMsgViewWindow *window);
172 static void modest_msg_view_window_on_row_deleted (GtkTreeModel *header_model,
174 ModestMsgViewWindow *window);
176 static void modest_msg_view_window_on_row_inserted (GtkTreeModel *header_model,
177 GtkTreePath *tree_path,
178 GtkTreeIter *tree_iter,
179 ModestMsgViewWindow *window);
181 static void modest_msg_view_window_on_row_reordered (GtkTreeModel *header_model,
185 ModestMsgViewWindow *window);
187 static void modest_msg_view_window_update_model_replaced (ModestHeaderViewObserver *window,
189 const gchar *tny_folder_id);
191 static void on_queue_changed (ModestMailOperationQueue *queue,
192 ModestMailOperation *mail_op,
193 ModestMailOperationQueueNotification type,
194 ModestMsgViewWindow *self);
196 static void on_account_removed (TnyAccountStore *account_store,
200 static void on_move_focus (GtkWidget *widget,
201 GtkDirectionType direction,
204 static void view_msg_cb (ModestMailOperation *mail_op,
211 static void set_progress_hint (ModestMsgViewWindow *self,
214 static void update_window_title (ModestMsgViewWindow *window);
216 static void init_window (ModestMsgViewWindow *obj);
218 static gboolean msg_is_visible (TnyHeader *header, gboolean check_outbox);
220 static void check_dimming_rules_after_change (ModestMsgViewWindow *window);
222 static gboolean on_fetch_image (ModestMsgView *msgview,
225 ModestMsgViewWindow *window);
227 static gboolean modest_msg_view_window_scroll_child (ModestMsgViewWindow *self,
228 GtkScrollType scroll_type,
231 static gboolean message_reader (ModestMsgViewWindow *window,
232 ModestMsgViewWindowPrivate *priv,
234 const gchar *msg_uid,
236 GtkTreeRowReference *row_reference);
238 static void setup_menu (ModestMsgViewWindow *self);
239 static gboolean _modest_msg_view_window_map_event (GtkWidget *widget,
242 static void update_branding (ModestMsgViewWindow *self);
243 static void sync_flags (ModestMsgViewWindow *self);
245 /* list my signals */
252 static const GtkActionEntry msg_view_toolbar_action_entries [] = {
255 { "ToolbarMessageReply", MODEST_STOCK_REPLY, N_("mcen_me_inbox_reply"), "<CTRL>R", NULL, G_CALLBACK (modest_ui_actions_on_reply) },
256 { "ToolbarMessageReplyAll", MODEST_STOCK_REPLY_ALL, N_("mcen_me_inbox_replytoall"), NULL, NULL, G_CALLBACK (modest_ui_actions_on_reply_all) },
257 { "ToolbarMessageForward", MODEST_STOCK_FORWARD, N_("mcen_me_inbox_forward"), NULL, NULL, G_CALLBACK (modest_ui_actions_on_forward) },
258 { "ToolbarDeleteMessage", MODEST_STOCK_DELETE, N_("qgn_toolb_gene_deletebutton"), NULL, NULL, G_CALLBACK (modest_ui_actions_on_delete_message_or_folder) },
259 { "ToolbarMessageBack", MODEST_TOOLBAR_ICON_PREV, N_("qgn_toolb_gene_back"), NULL, NULL, G_CALLBACK (modest_ui_actions_on_prev) },
260 { "ToolbarMessageNext", MODEST_TOOLBAR_ICON_NEXT, N_("qgn_toolb_gene_forward"), NULL, NULL, G_CALLBACK (modest_ui_actions_on_next) },
261 { "ToolbarDownloadExternalImages", MODEST_TOOLBAR_ICON_DOWNLOAD_IMAGES, N_("mail_bd_external_images"), NULL, NULL, G_CALLBACK (modest_ui_actions_on_fetch_images) },
264 static const GtkToggleActionEntry msg_view_toggle_action_entries [] = {
265 { "FindInMessage", MODEST_TOOLBAR_ICON_FIND, N_("qgn_toolb_gene_find"), "<CTRL>F", NULL, G_CALLBACK (modest_msg_view_window_toggle_isearch_toolbar), FALSE },
268 #define MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE((o), \
269 MODEST_TYPE_MSG_VIEW_WINDOW, \
270 ModestMsgViewWindowPrivate))
272 static GtkWindowClass *parent_class = NULL;
274 /* uncomment the following if you have defined any signals */
275 static guint signals[LAST_SIGNAL] = {0};
278 modest_msg_view_window_get_type (void)
280 static GType my_type = 0;
282 static const GTypeInfo my_info = {
283 sizeof(ModestMsgViewWindowClass),
284 NULL, /* base init */
285 NULL, /* base finalize */
286 (GClassInitFunc) modest_msg_view_window_class_init,
287 NULL, /* class finalize */
288 NULL, /* class data */
289 sizeof(ModestMsgViewWindow),
291 (GInstanceInitFunc) modest_msg_view_window_init,
294 #ifndef MODEST_TOOLKIT_HILDON2
295 my_type = g_type_register_static (MODEST_TYPE_SHELL_WINDOW,
296 "ModestMsgViewWindow",
299 my_type = g_type_register_static (MODEST_TYPE_HILDON2_WINDOW,
300 "ModestMsgViewWindow",
304 static const GInterfaceInfo modest_header_view_observer_info =
306 (GInterfaceInitFunc) modest_header_view_observer_init,
307 NULL, /* interface_finalize */
308 NULL /* interface_data */
311 g_type_add_interface_static (my_type,
312 MODEST_TYPE_HEADER_VIEW_OBSERVER,
313 &modest_header_view_observer_info);
319 save_state (ModestWindow *self)
321 modest_widget_memory_save (modest_runtime_get_conf (),
323 MODEST_CONF_MSG_VIEW_WINDOW_KEY);
327 modest_msg_view_window_scroll_child (ModestMsgViewWindow *self,
328 GtkScrollType scroll_type,
332 ModestMsgViewWindowPrivate *priv;
335 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(self);
337 switch (scroll_type) {
338 case GTK_SCROLL_STEP_UP:
341 case GTK_SCROLL_STEP_DOWN:
344 case GTK_SCROLL_PAGE_UP:
347 case GTK_SCROLL_PAGE_DOWN:
350 case GTK_SCROLL_START:
361 modest_scrollable_scroll ((ModestScrollable *) priv->main_scroll, 0, step);
363 return (gboolean) step;
367 add_scroll_binding (GtkBindingSet *binding_set,
369 GtkScrollType scroll)
371 guint keypad_keyval = keyval - GDK_Left + GDK_KP_Left;
373 gtk_binding_entry_add_signal (binding_set, keyval, 0,
375 GTK_TYPE_SCROLL_TYPE, scroll,
376 G_TYPE_BOOLEAN, FALSE);
377 gtk_binding_entry_add_signal (binding_set, keypad_keyval, 0,
379 GTK_TYPE_SCROLL_TYPE, scroll,
380 G_TYPE_BOOLEAN, FALSE);
384 modest_msg_view_window_class_init (ModestMsgViewWindowClass *klass)
386 GObjectClass *gobject_class;
387 ModestWindowClass *modest_window_class;
388 GtkBindingSet *binding_set;
390 gobject_class = (GObjectClass*) klass;
391 modest_window_class = (ModestWindowClass *) klass;
393 parent_class = g_type_class_peek_parent (klass);
394 gobject_class->finalize = modest_msg_view_window_finalize;
396 modest_window_class->set_zoom_func = modest_msg_view_window_set_zoom;
397 modest_window_class->get_zoom_func = modest_msg_view_window_get_zoom;
398 modest_window_class->zoom_plus_func = modest_msg_view_window_zoom_plus;
399 modest_window_class->zoom_minus_func = modest_msg_view_window_zoom_minus;
400 modest_window_class->show_toolbar_func = modest_msg_view_window_show_toolbar;
401 modest_window_class->disconnect_signals_func = modest_msg_view_window_disconnect_signals;
403 modest_window_class->save_state_func = save_state;
405 klass->scroll_child = modest_msg_view_window_scroll_child;
407 signals[MSG_CHANGED_SIGNAL] =
408 g_signal_new ("msg-changed",
409 G_TYPE_FROM_CLASS (gobject_class),
411 G_STRUCT_OFFSET (ModestMsgViewWindowClass, msg_changed),
413 modest_marshal_VOID__POINTER_POINTER,
414 G_TYPE_NONE, 2, G_TYPE_POINTER, G_TYPE_POINTER);
416 signals[SCROLL_CHILD_SIGNAL] =
417 g_signal_new ("scroll-child",
418 G_TYPE_FROM_CLASS (gobject_class),
419 G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
420 G_STRUCT_OFFSET (ModestMsgViewWindowClass, scroll_child),
422 modest_marshal_BOOLEAN__ENUM_BOOLEAN,
423 G_TYPE_BOOLEAN, 2, GTK_TYPE_SCROLL_TYPE, G_TYPE_BOOLEAN);
425 binding_set = gtk_binding_set_by_class (klass);
426 add_scroll_binding (binding_set, GDK_Up, GTK_SCROLL_STEP_UP);
427 add_scroll_binding (binding_set, GDK_Down, GTK_SCROLL_STEP_DOWN);
428 add_scroll_binding (binding_set, GDK_Page_Up, GTK_SCROLL_PAGE_UP);
429 add_scroll_binding (binding_set, GDK_Page_Down, GTK_SCROLL_PAGE_DOWN);
430 add_scroll_binding (binding_set, GDK_Home, GTK_SCROLL_START);
431 add_scroll_binding (binding_set, GDK_End, GTK_SCROLL_END);
433 g_type_class_add_private (gobject_class, sizeof(ModestMsgViewWindowPrivate));
437 static void modest_header_view_observer_init(
438 ModestHeaderViewObserverIface *iface_class)
440 iface_class->update_func = modest_msg_view_window_update_model_replaced;
444 modest_msg_view_window_init (ModestMsgViewWindow *obj)
446 ModestMsgViewWindowPrivate *priv;
447 ModestWindowPrivate *parent_priv = NULL;
448 GtkActionGroup *action_group = NULL;
449 GError *error = NULL;
451 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(obj);
452 parent_priv = MODEST_WINDOW_GET_PRIVATE(obj);
453 parent_priv->ui_manager = gtk_ui_manager_new();
455 action_group = gtk_action_group_new ("ModestMsgViewWindowActions");
456 gtk_action_group_set_translation_domain (action_group, GETTEXT_PACKAGE);
458 /* Add common actions */
459 gtk_action_group_add_actions (action_group,
460 msg_view_toolbar_action_entries,
461 G_N_ELEMENTS (msg_view_toolbar_action_entries),
463 gtk_action_group_add_toggle_actions (action_group,
464 msg_view_toggle_action_entries,
465 G_N_ELEMENTS (msg_view_toggle_action_entries),
468 gtk_ui_manager_insert_action_group (parent_priv->ui_manager, action_group, 0);
469 g_object_unref (action_group);
471 /* Load the UI definition */
472 gtk_ui_manager_add_ui_from_file (parent_priv->ui_manager, MODEST_UIDIR "modest-msg-view-window-ui.xml",
475 g_printerr ("modest: could not merge modest-msg-view-window-ui.xml: %s\n", error->message);
476 g_error_free (error);
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_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_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_IB_FIND_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_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_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_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_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));
1899 modest_ui_actions_on_close_window (NULL, MODEST_WINDOW (parent_window));
1903 /* Register the header - it'll be unregistered in the callback */
1905 modest_window_mgr_register_header (modest_runtime_get_window_mgr (), info->header, NULL);
1907 /* New mail operation */
1908 mail_op = modest_mail_operation_new_with_error_handling (G_OBJECT(parent_window),
1909 modest_ui_actions_disk_operations_error_handler,
1912 modest_mail_operation_queue_add (modest_runtime_get_mail_operation_queue (), mail_op);
1914 modest_mail_operation_get_msg (mail_op, info->header, TRUE, view_msg_cb, info->row_reference);
1916 modest_mail_operation_find_msg (mail_op, info->folder, info->msg_uid, TRUE, view_msg_cb, NULL);
1917 g_object_unref (mail_op);
1919 /* Update dimming rules */
1920 modest_ui_actions_check_toolbar_dimming_rules (MODEST_WINDOW (parent_window));
1921 modest_ui_actions_check_menu_dimming_rules (MODEST_WINDOW (parent_window));
1924 /* Frees. The row_reference will be freed by the view_msg_cb callback */
1925 g_free (info->msg_uid);
1927 g_object_unref (info->folder);
1929 g_object_unref (info->header);
1930 g_slice_free (MsgReaderInfo, info);
1935 * Reads the message whose summary item is @header. It takes care of
1936 * several things, among others:
1938 * If the message was not previously downloaded then ask the user
1939 * before downloading. If there is no connection launch the connection
1940 * dialog. Update toolbar dimming rules.
1942 * Returns: TRUE if the mail operation was started, otherwise if the
1943 * user do not want to download the message, or if the user do not
1944 * want to connect, then the operation is not issued
1947 message_reader (ModestMsgViewWindow *window,
1948 ModestMsgViewWindowPrivate *priv,
1950 const gchar *msg_uid,
1952 GtkTreeRowReference *row_reference)
1954 ModestWindowMgr *mgr;
1955 TnyAccount *account = NULL;
1956 MsgReaderInfo *info;
1958 /* We set the header from model while we're loading */
1959 tny_header_view_set_header (TNY_HEADER_VIEW (priv->msg_view), header);
1960 modest_window_set_title (MODEST_WINDOW (window), _CS_UPDATING);
1966 g_object_ref (folder);
1968 mgr = modest_runtime_get_window_mgr ();
1969 /* Msg download completed */
1970 if (!header || !(tny_header_get_flags (header) & TNY_HEADER_FLAG_CACHED)) {
1972 /* Ask the user if he wants to download the message if
1974 if (!tny_device_is_online (modest_runtime_get_device())) {
1975 GtkResponseType response;
1977 response = modest_platform_run_confirmation_dialog (GTK_WINDOW (window),
1978 _("mcen_nc_get_msg"));
1979 if (response == GTK_RESPONSE_CANCEL) {
1980 update_window_title (window);
1985 folder = tny_header_get_folder (header);
1987 info = g_slice_new (MsgReaderInfo);
1988 info->msg_uid = g_strdup (msg_uid);
1990 info->header = g_object_ref (header);
1992 info->header = NULL;
1994 info->folder = g_object_ref (folder);
1996 info->folder = NULL;
1997 if (row_reference) {
1998 info->row_reference = gtk_tree_row_reference_copy (row_reference);
2000 info->row_reference = NULL;
2003 /* Offer the connection dialog if necessary */
2004 modest_platform_connect_if_remote_and_perform ((GtkWindow *) window,
2006 TNY_FOLDER_STORE (folder),
2007 message_reader_performer,
2010 g_object_unref (folder);
2016 folder = tny_header_get_folder (header);
2019 account = tny_folder_get_account (folder);
2021 info = g_slice_new (MsgReaderInfo);
2022 info->msg_uid = g_strdup (msg_uid);
2024 info->folder = g_object_ref (folder);
2026 info->folder = NULL;
2028 info->header = g_object_ref (header);
2030 info->header = NULL;
2032 info->row_reference = gtk_tree_row_reference_copy (row_reference);
2034 info->row_reference = NULL;
2036 message_reader_performer (FALSE, NULL, (GtkWindow *) window, account, info);
2038 g_object_unref (account);
2040 g_object_unref (folder);
2046 modest_msg_view_window_select_next_message (ModestMsgViewWindow *window)
2048 ModestMsgViewWindowPrivate *priv;
2049 GtkTreePath *path= NULL;
2050 GtkTreeIter tmp_iter;
2052 gboolean retval = TRUE;
2053 GtkTreeRowReference *row_reference = NULL;
2055 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), FALSE);
2056 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
2058 if (!priv->row_reference)
2061 /* Update the next row reference if it's not valid. This could
2062 happen if for example the header which it was pointing to,
2063 was deleted. The best place to do it is in the row-deleted
2064 handler but the tinymail model do not work like the glib
2065 tree models and reports the deletion when the row is still
2067 if (!gtk_tree_row_reference_valid (priv->next_row_reference)) {
2068 if (priv->next_row_reference) {
2069 gtk_tree_row_reference_free (priv->next_row_reference);
2071 if (gtk_tree_row_reference_valid (priv->row_reference)) {
2072 priv->next_row_reference = gtk_tree_row_reference_copy (priv->row_reference);
2073 select_next_valid_row (priv->header_model, &(priv->next_row_reference), FALSE, priv->is_outbox);
2075 priv->next_row_reference = NULL;
2078 if (priv->next_row_reference)
2079 path = gtk_tree_row_reference_get_path (priv->next_row_reference);
2083 row_reference = gtk_tree_row_reference_copy (priv->next_row_reference);
2085 gtk_tree_model_get_iter (priv->header_model,
2088 gtk_tree_path_free (path);
2090 gtk_tree_model_get (priv->header_model, &tmp_iter,
2091 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
2094 /* Read the message & show it */
2095 if (!message_reader (window, priv, header, NULL, NULL, row_reference)) {
2098 gtk_tree_row_reference_free (row_reference);
2101 g_object_unref (header);
2107 modest_msg_view_window_select_previous_message (ModestMsgViewWindow *window)
2109 ModestMsgViewWindowPrivate *priv = NULL;
2111 gboolean finished = FALSE;
2112 gboolean retval = FALSE;
2114 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), FALSE);
2115 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
2117 if (priv->row_reference && !gtk_tree_row_reference_valid (priv->row_reference)) {
2118 gtk_tree_row_reference_free (priv->row_reference);
2119 priv->row_reference = NULL;
2122 /* Return inmediatly if there is no header model */
2123 if (!priv->header_model || !priv->row_reference)
2126 path = gtk_tree_row_reference_get_path (priv->row_reference);
2127 while (!finished && gtk_tree_path_prev (path)) {
2131 gtk_tree_model_get_iter (priv->header_model, &iter, path);
2132 gtk_tree_model_get (priv->header_model, &iter,
2133 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
2137 if (msg_is_visible (header, priv->is_outbox)) {
2138 GtkTreeRowReference *row_reference;
2139 row_reference = gtk_tree_row_reference_new (priv->header_model, path);
2140 /* Read the message & show it */
2141 retval = message_reader (window, priv, header, NULL, NULL, row_reference);
2142 gtk_tree_row_reference_free (row_reference);
2146 g_object_unref (header);
2150 gtk_tree_path_free (path);
2155 view_msg_cb (ModestMailOperation *mail_op,
2162 ModestMsgViewWindow *self = NULL;
2163 ModestMsgViewWindowPrivate *priv = NULL;
2164 GtkTreeRowReference *row_reference = NULL;
2166 /* Unregister the header (it was registered before creating the mail operation) */
2167 modest_window_mgr_unregister_header (modest_runtime_get_window_mgr (), header);
2169 row_reference = (GtkTreeRowReference *) user_data;
2172 gtk_tree_row_reference_free (row_reference);
2173 self = (ModestMsgViewWindow *) modest_mail_operation_get_source (mail_op);
2175 /* Restore window title */
2176 update_window_title (self);
2177 modest_ui_actions_on_close_window (NULL, MODEST_WINDOW (self));
2178 g_object_unref (self);
2183 /* If there was any error */
2184 if (!modest_ui_actions_msg_retrieval_check (mail_op, header, msg)) {
2186 gtk_tree_row_reference_free (row_reference);
2187 self = (ModestMsgViewWindow *) modest_mail_operation_get_source (mail_op);
2189 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
2190 /* First we check if the parent is a folder window */
2191 if (priv->msg_uid && !modest_window_mgr_get_folder_window (MODEST_WINDOW_MGR (modest_runtime_get_window_mgr ()))) {
2193 TnyAccount *account = NULL;
2194 GtkWidget *header_window = NULL;
2196 is_merge = g_str_has_prefix (priv->msg_uid, "merge:");
2198 /* Get the account */
2200 account = tny_account_store_find_account (TNY_ACCOUNT_STORE (modest_runtime_get_account_store ()),
2203 if (is_merge || account) {
2204 TnyFolder *folder = NULL;
2206 /* Try to get the message, if it's already downloaded
2207 we don't need to connect */
2209 folder = modest_tny_folder_store_find_folder_from_uri (TNY_FOLDER_STORE (account),
2212 ModestTnyAccountStore *account_store;
2213 ModestTnyLocalFoldersAccount *local_folders_account;
2215 account_store = modest_runtime_get_account_store ();
2216 local_folders_account = MODEST_TNY_LOCAL_FOLDERS_ACCOUNT (
2217 modest_tny_account_store_get_local_folders_account (account_store));
2218 folder = modest_tny_local_folders_account_get_merged_outbox (local_folders_account);
2219 g_object_unref (local_folders_account);
2221 if (account) g_object_unref (account);
2224 header_window = (GtkWidget *)
2225 modest_header_window_new (
2227 modest_window_get_active_account (MODEST_WINDOW (self)),
2228 modest_window_get_active_mailbox (MODEST_WINDOW (self)));
2229 if (!modest_window_mgr_register_window (modest_runtime_get_window_mgr (),
2230 MODEST_WINDOW (header_window),
2232 gtk_widget_destroy (GTK_WIDGET (header_window));
2234 gtk_widget_show_all (GTK_WIDGET (header_window));
2236 g_object_unref (folder);
2242 /* Restore window title */
2243 update_window_title (self);
2244 modest_ui_actions_on_close_window (NULL, MODEST_WINDOW (self));
2245 g_object_unref (self);
2250 /* Get the window */
2251 self = (ModestMsgViewWindow *) modest_mail_operation_get_source (mail_op);
2252 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self));
2253 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
2255 /* Update the row reference */
2256 if (priv->row_reference != NULL) {
2257 gtk_tree_row_reference_free (priv->row_reference);
2258 priv->row_reference = (row_reference && gtk_tree_row_reference_valid (row_reference))?gtk_tree_row_reference_copy (row_reference):NULL;
2259 if (priv->next_row_reference != NULL) {
2260 gtk_tree_row_reference_free (priv->next_row_reference);
2262 if (priv->row_reference) {
2263 priv->next_row_reference = gtk_tree_row_reference_copy (priv->row_reference);
2264 select_next_valid_row (priv->header_model, &(priv->next_row_reference), TRUE, priv->is_outbox);
2266 priv->next_row_reference = NULL;
2270 /* Mark header as read */
2271 if (!(tny_header_get_flags (header) & TNY_HEADER_FLAG_SEEN))
2272 tny_header_set_flag (header, TNY_HEADER_FLAG_SEEN);
2274 /* Set new message */
2275 if (priv->msg_view != NULL && TNY_IS_MSG_VIEW (priv->msg_view)) {
2276 tny_msg_view_set_msg (TNY_MSG_VIEW (priv->msg_view), msg);
2277 modest_msg_view_window_update_priority (self);
2278 update_window_title (MODEST_MSG_VIEW_WINDOW (self));
2279 update_branding (MODEST_MSG_VIEW_WINDOW (self));
2280 modest_msg_view_grab_focus (MODEST_MSG_VIEW (priv->msg_view));
2283 /* Set the new message uid of the window */
2284 if (priv->msg_uid) {
2285 g_free (priv->msg_uid);
2286 priv->msg_uid = modest_tny_folder_get_header_unique_id (header);
2289 /* Notify the observers */
2290 g_signal_emit (G_OBJECT (self), signals[MSG_CHANGED_SIGNAL],
2291 0, priv->header_model, priv->row_reference);
2293 /* Sync the flags if the message is not opened from a header
2294 model, i.e, if it's opened from a notification */
2295 if (!priv->header_model)
2299 g_object_unref (self);
2301 gtk_tree_row_reference_free (row_reference);
2305 modest_msg_view_window_get_folder_type (ModestMsgViewWindow *window)
2307 ModestMsgViewWindowPrivate *priv;
2309 TnyFolderType folder_type;
2311 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
2313 folder_type = TNY_FOLDER_TYPE_UNKNOWN;
2315 msg = tny_msg_view_get_msg (TNY_MSG_VIEW (priv->msg_view));
2319 folder = tny_msg_get_folder (msg);
2321 folder_type = modest_tny_folder_guess_folder_type (folder);
2322 g_object_unref (folder);
2324 g_object_unref (msg);
2332 modest_msg_view_window_update_priority (ModestMsgViewWindow *window)
2334 ModestMsgViewWindowPrivate *priv;
2335 TnyHeader *header = NULL;
2336 TnyHeaderFlags flags = 0;
2338 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
2340 if (priv->header_model && priv->row_reference && gtk_tree_row_reference_valid (priv->row_reference)) {
2342 GtkTreePath *path = NULL;
2344 path = gtk_tree_row_reference_get_path (priv->row_reference);
2345 g_return_if_fail (path != NULL);
2346 gtk_tree_model_get_iter (priv->header_model,
2348 gtk_tree_row_reference_get_path (priv->row_reference));
2350 gtk_tree_model_get (priv->header_model, &iter, TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
2352 gtk_tree_path_free (path);
2355 msg = tny_msg_view_get_msg (TNY_MSG_VIEW (priv->msg_view));
2357 header = tny_msg_get_header (msg);
2358 g_object_unref (msg);
2363 flags = tny_header_get_flags (header);
2364 g_object_unref(G_OBJECT(header));
2367 modest_msg_view_set_priority (MODEST_MSG_VIEW(priv->msg_view), flags);
2372 toolbar_resize (ModestMsgViewWindow *self)
2374 ModestMsgViewWindowPrivate *priv = NULL;
2375 ModestWindowPrivate *parent_priv = NULL;
2377 gint static_button_size;
2378 ModestWindowMgr *mgr;
2380 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self));
2381 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
2382 parent_priv = MODEST_WINDOW_GET_PRIVATE(self);
2384 mgr = modest_runtime_get_window_mgr ();
2385 static_button_size = modest_window_mgr_get_fullscreen_mode (mgr)?120:120;
2387 if (parent_priv->toolbar) {
2388 /* Set expandable and homogeneous tool buttons */
2389 widget = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/ToolbarMessageReply");
2390 gtk_tool_item_set_expand (GTK_TOOL_ITEM (widget), TRUE);
2391 gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (widget), TRUE);
2392 widget = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/ToolbarMessageReplyAll");
2393 gtk_tool_item_set_expand (GTK_TOOL_ITEM (widget), TRUE);
2394 gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (widget), TRUE);
2395 widget = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/ToolbarMessageForward");
2396 gtk_tool_item_set_expand (GTK_TOOL_ITEM (widget), TRUE);
2397 gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (widget), TRUE);
2398 widget = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/ToolbarDeleteMessage");
2399 gtk_tool_item_set_expand (GTK_TOOL_ITEM (widget), TRUE);
2400 gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (widget), TRUE);
2401 widget = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/ToolbarDownloadExternalImages");
2402 gtk_tool_item_set_expand (GTK_TOOL_ITEM (widget), TRUE);
2403 gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (widget), TRUE);
2404 gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (priv->next_toolitem), TRUE);
2405 gtk_tool_item_set_expand (GTK_TOOL_ITEM (priv->next_toolitem), TRUE);
2406 gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (priv->prev_toolitem), TRUE);
2407 gtk_tool_item_set_expand (GTK_TOOL_ITEM (priv->prev_toolitem), TRUE);
2412 modest_msg_view_window_show_toolbar (ModestWindow *self,
2413 gboolean show_toolbar)
2415 ModestMsgViewWindowPrivate *priv = NULL;
2416 ModestWindowPrivate *parent_priv;
2418 parent_priv = MODEST_WINDOW_GET_PRIVATE(self);
2419 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(self);
2421 /* Set optimized view status */
2422 priv->optimized_view = !show_toolbar;
2424 if (!parent_priv->toolbar) {
2425 parent_priv->toolbar = gtk_ui_manager_get_widget (parent_priv->ui_manager,
2428 #ifdef MODEST_TOOLKIT_HILDON2
2429 gtk_toolbar_set_icon_size (GTK_TOOLBAR (parent_priv->toolbar), HILDON_ICON_SIZE_FINGER);
2431 gtk_toolbar_set_icon_size (GTK_TOOLBAR (parent_priv->toolbar), GTK_ICON_SIZE_LARGE_TOOLBAR);
2433 gtk_widget_set_no_show_all (parent_priv->toolbar, TRUE);
2435 priv->next_toolitem = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/ToolbarMessageNext");
2436 priv->prev_toolitem = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/ToolbarMessageBack");
2437 toolbar_resize (MODEST_MSG_VIEW_WINDOW (self));
2439 modest_window_add_toolbar (MODEST_WINDOW (self),
2440 GTK_TOOLBAR (parent_priv->toolbar));
2445 /* Quick hack: this prevents toolbar icons "dance" when progress bar show status is changed */
2446 /* TODO: resize mode migth be GTK_RESIZE_QUEUE, in order to avoid unneccesary shows */
2447 gtk_container_set_resize_mode (GTK_CONTAINER(parent_priv->toolbar), GTK_RESIZE_IMMEDIATE);
2449 gtk_widget_show (GTK_WIDGET (parent_priv->toolbar));
2450 if (modest_msg_view_window_transfer_mode_enabled (MODEST_MSG_VIEW_WINDOW (self)))
2451 set_progress_hint (MODEST_MSG_VIEW_WINDOW (self), TRUE);
2453 set_progress_hint (MODEST_MSG_VIEW_WINDOW (self), FALSE);
2456 gtk_widget_set_no_show_all (parent_priv->toolbar, TRUE);
2457 gtk_widget_hide (GTK_WIDGET (parent_priv->toolbar));
2462 modest_msg_view_window_clipboard_owner_change (GtkClipboard *clipboard,
2464 ModestMsgViewWindow *window)
2466 if (!GTK_WIDGET_VISIBLE (window))
2469 modest_window_check_dimming_rules_group (MODEST_WINDOW (window), MODEST_DIMMING_RULES_CLIPBOARD);
2473 modest_msg_view_window_transfer_mode_enabled (ModestMsgViewWindow *self)
2475 ModestMsgViewWindowPrivate *priv;
2477 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self), FALSE);
2478 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(self);
2480 return priv->progress_hint;
2484 observers_empty (ModestMsgViewWindow *self)
2487 ModestMsgViewWindowPrivate *priv;
2488 gboolean is_empty = TRUE;
2489 guint pending_ops = 0;
2491 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(self);
2492 tmp = priv->progress_widgets;
2494 /* Check all observers */
2495 while (tmp && is_empty) {
2496 pending_ops = modest_progress_object_num_pending_operations (MODEST_PROGRESS_OBJECT(tmp->data));
2497 is_empty = pending_ops == 0;
2499 tmp = g_slist_next(tmp);
2506 on_account_removed (TnyAccountStore *account_store,
2507 TnyAccount *account,
2510 /* Do nothing if it's a transport account, because we only
2511 show the messages of a store account */
2512 if (tny_account_get_account_type(account) == TNY_ACCOUNT_TYPE_STORE) {
2513 const gchar *parent_acc = NULL;
2514 const gchar *our_acc = NULL;
2516 our_acc = modest_window_get_active_account (MODEST_WINDOW (user_data));
2517 parent_acc = modest_tny_account_get_parent_modest_account_name_for_server_account (account);
2519 /* Close this window if I'm showing a message of the removed account */
2520 if (our_acc && parent_acc && strcmp (parent_acc, our_acc) == 0)
2521 modest_ui_actions_on_close_window (NULL, MODEST_WINDOW (user_data));
2526 on_mail_operation_started (ModestMailOperation *mail_op,
2529 ModestMsgViewWindow *self;
2530 ModestMailOperationTypeOperation op_type;
2532 ModestMsgViewWindowPrivate *priv;
2533 GObject *source = NULL;
2535 self = MODEST_MSG_VIEW_WINDOW (user_data);
2536 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
2537 op_type = modest_mail_operation_get_type_operation (mail_op);
2538 tmp = priv->progress_widgets;
2539 source = modest_mail_operation_get_source(mail_op);
2540 if (G_OBJECT (self) == source) {
2541 if (op_type == MODEST_MAIL_OPERATION_TYPE_RECEIVE ||
2542 op_type == MODEST_MAIL_OPERATION_TYPE_OPEN ||
2543 op_type == MODEST_MAIL_OPERATION_TYPE_DELETE) {
2544 set_progress_hint (self, TRUE);
2546 modest_progress_object_add_operation (
2547 MODEST_PROGRESS_OBJECT (tmp->data),
2549 tmp = g_slist_next (tmp);
2553 g_object_unref (source);
2555 /* Update dimming rules */
2556 check_dimming_rules_after_change (self);
2560 on_mail_operation_finished (ModestMailOperation *mail_op,
2563 ModestMsgViewWindow *self;
2564 ModestMailOperationTypeOperation op_type;
2566 ModestMsgViewWindowPrivate *priv;
2568 self = MODEST_MSG_VIEW_WINDOW (user_data);
2569 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
2570 op_type = modest_mail_operation_get_type_operation (mail_op);
2571 tmp = priv->progress_widgets;
2573 if (op_type == MODEST_MAIL_OPERATION_TYPE_RECEIVE ||
2574 op_type == MODEST_MAIL_OPERATION_TYPE_OPEN ||
2575 op_type == MODEST_MAIL_OPERATION_TYPE_DELETE) {
2577 modest_progress_object_remove_operation (MODEST_PROGRESS_OBJECT (tmp->data),
2579 tmp = g_slist_next (tmp);
2582 /* If no more operations are being observed, NORMAL mode is enabled again */
2583 if (observers_empty (self)) {
2584 set_progress_hint (self, FALSE);
2588 /* Update dimming rules. We have to do this right here
2589 and not in view_msg_cb because at that point the
2590 transfer mode is still enabled so the dimming rule
2591 won't let the user delete the message that has been
2592 readed for example */
2593 check_dimming_rules_after_change (self);
2597 on_queue_changed (ModestMailOperationQueue *queue,
2598 ModestMailOperation *mail_op,
2599 ModestMailOperationQueueNotification type,
2600 ModestMsgViewWindow *self)
2602 ModestMsgViewWindowPrivate *priv;
2604 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
2606 /* If this operations was created by another window, do nothing */
2607 if (!modest_mail_operation_is_mine (mail_op, G_OBJECT(self)))
2610 if (type == MODEST_MAIL_OPERATION_QUEUE_OPERATION_ADDED) {
2611 priv->sighandlers = modest_signal_mgr_connect (priv->sighandlers,
2613 "operation-started",
2614 G_CALLBACK (on_mail_operation_started),
2616 priv->sighandlers = modest_signal_mgr_connect (priv->sighandlers,
2618 "operation-finished",
2619 G_CALLBACK (on_mail_operation_finished),
2621 } else if (type == MODEST_MAIL_OPERATION_QUEUE_OPERATION_REMOVED) {
2622 priv->sighandlers = modest_signal_mgr_disconnect (priv->sighandlers,
2624 "operation-started");
2625 priv->sighandlers = modest_signal_mgr_disconnect (priv->sighandlers,
2627 "operation-finished");
2632 modest_msg_view_window_get_attachments (ModestMsgViewWindow *win)
2634 ModestMsgViewWindowPrivate *priv;
2635 TnyList *selected_attachments = NULL;
2637 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (win), NULL);
2638 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (win);
2640 /* In Hildon 2.2 as there's no selection we assume we have all attachments selected */
2641 selected_attachments = modest_msg_view_get_attachments (MODEST_MSG_VIEW (priv->msg_view));
2643 return selected_attachments;
2647 ModestMsgViewWindow *self;
2649 gchar *attachment_uid;
2650 } DecodeAsyncHelper;
2653 on_decode_to_stream_async_handler (TnyMimePart *mime_part,
2659 DecodeAsyncHelper *helper = (DecodeAsyncHelper *) user_data;
2660 const gchar *content_type;
2662 if (cancelled || err) {
2665 if ((err->domain == TNY_ERROR_DOMAIN) &&
2666 (err->code == TNY_IO_ERROR_WRITE) &&
2667 (errno == ENOSPC)) {
2668 msg = g_strdup_printf (_KR("cerm_device_memory_full"), "");
2670 msg = g_strdup (_("mail_ib_file_operation_failed"));
2672 modest_platform_information_banner (NULL, NULL, msg);
2678 /* It could happen that the window was closed. So we
2679 assume it is a cancelation */
2680 if (!GTK_WIDGET_VISIBLE (helper->self))
2683 /* Remove the progress hint */
2684 set_progress_hint (helper->self, FALSE);
2686 content_type = tny_mime_part_get_content_type (mime_part);
2687 if (g_str_has_prefix (content_type, "message/rfc822")) {
2688 ModestWindowMgr *mgr;
2689 ModestWindow *msg_win = NULL;
2692 const gchar *mailbox;
2693 TnyStream *file_stream;
2696 fd = g_open (helper->file_path, O_RDONLY, 0644);
2698 file_stream = tny_fs_stream_new (fd);
2700 mgr = modest_runtime_get_window_mgr ();
2702 account = g_strdup (modest_window_get_active_account (MODEST_WINDOW (helper->self)));
2703 mailbox = modest_window_get_active_mailbox (MODEST_WINDOW (helper->self));
2706 account = modest_account_mgr_get_default_account (modest_runtime_get_account_mgr ());
2708 msg = tny_camel_msg_new ();
2709 tny_camel_msg_parse (msg, file_stream);
2710 msg_win = modest_msg_view_window_new_for_attachment (TNY_MSG (msg), account, mailbox, helper->attachment_uid);
2711 modest_window_set_zoom (MODEST_WINDOW (msg_win),
2712 modest_window_get_zoom (MODEST_WINDOW (helper->self)));
2713 if (modest_window_mgr_register_window (mgr, msg_win, MODEST_WINDOW (helper->self)))
2714 gtk_widget_show_all (GTK_WIDGET (msg_win));
2716 gtk_widget_destroy (GTK_WIDGET (msg_win));
2717 g_object_unref (msg);
2718 g_object_unref (file_stream);
2720 modest_platform_information_banner (NULL, NULL, _("mail_ib_file_operation_failed"));
2725 /* make the file read-only */
2726 g_chmod(helper->file_path, 0444);
2728 /* Activate the file */
2729 modest_platform_activate_file (helper->file_path, tny_mime_part_get_content_type (mime_part));
2734 g_object_unref (helper->self);
2735 g_free (helper->file_path);
2736 g_free (helper->attachment_uid);
2737 g_slice_free (DecodeAsyncHelper, helper);
2741 modest_msg_view_window_view_attachment (ModestMsgViewWindow *window,
2742 TnyMimePart *mime_part)
2744 ModestMsgViewWindowPrivate *priv;
2745 const gchar *msg_uid;
2746 gchar *attachment_uid = NULL;
2747 gint attachment_index = 0;
2748 TnyList *attachments;
2750 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window));
2751 g_return_if_fail (TNY_IS_MIME_PART (mime_part) || (mime_part == NULL));
2752 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
2754 msg_uid = modest_msg_view_window_get_message_uid (MODEST_MSG_VIEW_WINDOW (window));
2755 attachments = modest_msg_view_get_attachments (MODEST_MSG_VIEW (priv->msg_view));
2756 attachment_index = modest_list_index (attachments, (GObject *) mime_part);
2757 g_object_unref (attachments);
2759 if (msg_uid && attachment_index >= 0) {
2760 attachment_uid = g_strdup_printf ("%s/%d", msg_uid, attachment_index);
2763 if (mime_part == NULL) {
2764 gboolean error = FALSE;
2765 TnyList *selected_attachments = modest_msg_view_get_selected_attachments (MODEST_MSG_VIEW (priv->msg_view));
2766 if (selected_attachments == NULL || tny_list_get_length (selected_attachments) == 0) {
2768 } else if (tny_list_get_length (selected_attachments) > 1) {
2769 modest_platform_system_banner (NULL, NULL, _("mcen_ib_unable_to_display_more"));
2773 iter = tny_list_create_iterator (selected_attachments);
2774 mime_part = (TnyMimePart *) tny_iterator_get_current (iter);
2775 g_object_unref (iter);
2777 if (selected_attachments)
2778 g_object_unref (selected_attachments);
2783 g_object_ref (mime_part);
2786 if (tny_mime_part_is_purged (mime_part))
2789 if (!modest_tny_mime_part_is_msg (mime_part) && tny_mime_part_get_filename (mime_part)) {
2790 gchar *filepath = NULL;
2791 const gchar *att_filename = tny_mime_part_get_filename (mime_part);
2792 gboolean show_error_banner = FALSE;
2793 TnyFsStream *temp_stream = NULL;
2794 temp_stream = modest_utils_create_temp_stream (att_filename, attachment_uid,
2797 if (temp_stream != NULL) {
2798 ModestAccountMgr *mgr;
2799 DecodeAsyncHelper *helper;
2800 gboolean decode_in_provider;
2801 ModestProtocol *protocol;
2802 const gchar *account;
2804 /* Activate progress hint */
2805 set_progress_hint (window, TRUE);
2807 helper = g_slice_new0 (DecodeAsyncHelper);
2808 helper->self = g_object_ref (window);
2809 helper->file_path = g_strdup (filepath);
2810 helper->attachment_uid = g_strdup (attachment_uid);
2812 decode_in_provider = FALSE;
2813 mgr = modest_runtime_get_account_mgr ();
2814 account = modest_window_get_active_account (MODEST_WINDOW (window));
2815 if (modest_account_mgr_account_is_multimailbox (mgr, account, &protocol)) {
2816 if (MODEST_IS_ACCOUNT_PROTOCOL (protocol)) {
2818 uri = g_strconcat ("file://", filepath, NULL);
2819 decode_in_provider =
2820 modest_account_protocol_decode_part_to_stream_async (
2821 MODEST_ACCOUNT_PROTOCOL (protocol),
2824 TNY_STREAM (temp_stream),
2825 on_decode_to_stream_async_handler,
2832 if (!decode_in_provider)
2833 tny_mime_part_decode_to_stream_async (mime_part, TNY_STREAM (temp_stream),
2834 on_decode_to_stream_async_handler,
2837 g_object_unref (temp_stream);
2838 /* NOTE: files in the temporary area will be automatically
2839 * cleaned after some time if they are no longer in use */
2842 const gchar *content_type;
2843 /* the file may already exist but it isn't writable,
2844 * let's try to open it anyway */
2845 content_type = tny_mime_part_get_content_type (mime_part);
2846 modest_platform_activate_file (filepath, content_type);
2848 g_warning ("%s: modest_utils_create_temp_stream failed", __FUNCTION__);
2849 show_error_banner = TRUE;
2854 if (show_error_banner)
2855 modest_platform_information_banner (NULL, NULL, _("mail_ib_file_operation_failed"));
2856 } else if (!modest_tny_mime_part_is_msg (mime_part)) {
2857 ModestWindowMgr *mgr;
2858 ModestWindow *msg_win = NULL;
2859 TnyMsg *current_msg;
2863 current_msg = modest_msg_view_window_get_message (MODEST_MSG_VIEW_WINDOW (window));
2864 mgr = modest_runtime_get_window_mgr ();
2865 header = tny_msg_get_header (TNY_MSG (current_msg));
2866 found = modest_window_mgr_find_registered_message_uid (mgr,
2871 g_debug ("window for this body is already being created");
2874 /* it's not found, so create a new window for it */
2875 modest_window_mgr_register_header (mgr, header, attachment_uid); /* register the uid before building the window */
2876 gchar *account = g_strdup (modest_window_get_active_account (MODEST_WINDOW (window)));
2877 const gchar *mailbox = modest_window_get_active_mailbox (MODEST_WINDOW (window));
2879 account = modest_account_mgr_get_default_account (modest_runtime_get_account_mgr ());
2881 msg_win = modest_msg_view_window_new_with_other_body (TNY_MSG (current_msg), TNY_MIME_PART (mime_part),
2882 account, mailbox, attachment_uid);
2884 modest_window_set_zoom (MODEST_WINDOW (msg_win),
2885 modest_window_get_zoom (MODEST_WINDOW (window)));
2886 if (modest_window_mgr_register_window (mgr, msg_win, MODEST_WINDOW (window)))
2887 gtk_widget_show_all (GTK_WIDGET (msg_win));
2889 gtk_widget_destroy (GTK_WIDGET (msg_win));
2891 g_object_unref (current_msg);
2893 /* message attachment */
2894 TnyHeader *header = NULL;
2895 ModestWindowMgr *mgr;
2896 ModestWindow *msg_win = NULL;
2899 header = tny_msg_get_header (TNY_MSG (mime_part));
2900 mgr = modest_runtime_get_window_mgr ();
2901 found = modest_window_mgr_find_registered_header (mgr, header, &msg_win);
2904 /* if it's found, but there is no msg_win, it's probably in the process of being created;
2905 * thus, we don't do anything */
2906 g_debug ("window for is already being created");
2908 /* it's not found, so create a new window for it */
2909 modest_window_mgr_register_header (mgr, header, attachment_uid); /* register the uid before building the window */
2910 gchar *account = g_strdup (modest_window_get_active_account (MODEST_WINDOW (window)));
2911 const gchar *mailbox = modest_window_get_active_mailbox (MODEST_WINDOW (window));
2913 account = modest_account_mgr_get_default_account (modest_runtime_get_account_mgr ());
2914 msg_win = modest_msg_view_window_new_for_attachment (TNY_MSG (mime_part), account,
2915 mailbox, attachment_uid);
2916 modest_window_set_zoom (MODEST_WINDOW (msg_win),
2917 modest_window_get_zoom (MODEST_WINDOW (window)));
2918 if (modest_window_mgr_register_window (mgr, msg_win, MODEST_WINDOW (window)))
2919 gtk_widget_show_all (GTK_WIDGET (msg_win));
2921 gtk_widget_destroy (GTK_WIDGET (msg_win));
2927 g_free (attachment_uid);
2929 g_object_unref (mime_part);
2941 GnomeVFSResult result;
2943 ModestMsgViewWindow *window;
2946 static void save_mime_part_info_free (SaveMimePartInfo *info, gboolean with_struct);
2947 static gboolean idle_save_mime_part_show_result (SaveMimePartInfo *info);
2948 static gpointer save_mime_part_to_file (SaveMimePartInfo *info);
2949 static void save_mime_parts_to_file_with_checks (GtkWindow *parent, SaveMimePartInfo *info);
2952 save_mime_part_info_free (SaveMimePartInfo *info, gboolean with_struct)
2956 for (node = info->pairs; node != NULL; node = g_list_next (node)) {
2957 SaveMimePartPair *pair = (SaveMimePartPair *) node->data;
2958 g_free (pair->filename);
2959 g_object_unref (pair->part);
2960 g_slice_free (SaveMimePartPair, pair);
2962 g_list_free (info->pairs);
2965 g_object_unref (info->window);
2966 info->window = NULL;
2968 g_slice_free (SaveMimePartInfo, info);
2973 idle_save_mime_part_show_result (SaveMimePartInfo *info)
2975 /* This is a GDK lock because we are an idle callback and
2976 * modest_platform_system_banner is or does Gtk+ code */
2978 gdk_threads_enter (); /* CHECKED */
2979 if (info->result == GNOME_VFS_OK) {
2980 modest_platform_system_banner (NULL, NULL, _CS_SAVED);
2981 } else if (info->result == GNOME_VFS_ERROR_NO_SPACE) {
2984 /* Check if the uri belongs to the external mmc */
2985 if (g_str_has_prefix (info->uri, g_getenv (MODEST_MMC1_VOLUMEPATH_ENV)))
2986 msg = g_strdup_printf (_KR("cerm_device_memory_full"), "");
2988 msg = g_strdup (_KR("cerm_memory_card_full"));
2989 modest_platform_information_banner (NULL, NULL, msg);
2992 modest_platform_system_banner (NULL, NULL, _("mail_ib_file_operation_failed"));
2994 save_mime_part_info_free (info, FALSE);
2995 gdk_threads_leave (); /* CHECKED */
3001 save_mime_part_to_file (SaveMimePartInfo *info)
3003 GnomeVFSHandle *handle;
3005 SaveMimePartPair *pair = (SaveMimePartPair *) info->pairs->data;
3007 info->result = gnome_vfs_create (&handle, pair->filename, GNOME_VFS_OPEN_WRITE, FALSE, 0644);
3008 if (info->result == GNOME_VFS_OK) {
3009 GError *error = NULL;
3010 gboolean decode_in_provider;
3012 ModestAccountMgr *mgr;
3013 const gchar *account;
3014 ModestProtocol *protocol = NULL;
3016 stream = tny_vfs_stream_new (handle);
3018 decode_in_provider = FALSE;
3019 mgr = modest_runtime_get_account_mgr ();
3020 account = modest_window_get_active_account (MODEST_WINDOW (info->window));
3021 if (modest_account_mgr_account_is_multimailbox (mgr, account, &protocol)) {
3022 if (MODEST_IS_ACCOUNT_PROTOCOL (protocol)) {
3023 decode_in_provider =
3024 modest_account_protocol_decode_part_to_stream (
3025 MODEST_ACCOUNT_PROTOCOL (protocol),
3033 if (!decode_in_provider)
3034 written = tny_mime_part_decode_to_stream (pair->part, stream, &error);
3037 g_warning ("modest: could not save attachment %s: %d (%s)\n", pair->filename, error?error->code:-1, error?error->message:"Unknown error");
3039 if ((error->domain == TNY_ERROR_DOMAIN) &&
3040 (error->code == TNY_IO_ERROR_WRITE) &&
3041 (errno == ENOSPC)) {
3042 info->result = GNOME_VFS_ERROR_NO_SPACE;
3044 info->result = GNOME_VFS_ERROR_IO;
3047 g_object_unref (G_OBJECT (stream));
3049 g_warning ("Could not create save attachment %s: %s\n",
3050 pair->filename, gnome_vfs_result_to_string (info->result));
3053 /* Go on saving remaining files */
3054 info->pairs = g_list_remove_link (info->pairs, info->pairs);
3055 if (info->pairs != NULL) {
3056 save_mime_part_to_file (info);
3058 g_idle_add ((GSourceFunc) idle_save_mime_part_show_result, info);
3065 save_mime_parts_to_file_with_checks (GtkWindow *parent,
3066 SaveMimePartInfo *info)
3068 gboolean is_ok = TRUE;
3069 gint replaced_files = 0;
3070 const GList *files = info->pairs;
3071 const GList *iter, *to_replace = NULL;
3073 for (iter = files; (iter != NULL) && (replaced_files < 2); iter = g_list_next(iter)) {
3074 SaveMimePartPair *pair = iter->data;
3075 gchar *unescaped = g_uri_unescape_string (pair->filename, NULL);
3077 if (modest_utils_file_exists (unescaped)) {
3079 if (replaced_files == 1)
3084 if (replaced_files) {
3087 if (replaced_files == 1) {
3088 SaveMimePartPair *pair = to_replace->data;
3089 const gchar *basename = strrchr (pair->filename, G_DIR_SEPARATOR) + 1;
3090 gchar *escaped_basename, *message;
3092 escaped_basename = g_uri_unescape_string (basename, NULL);
3093 message = g_strdup_printf ("%s\n%s",
3095 (escaped_basename) ? escaped_basename : "");
3096 response = modest_platform_run_confirmation_dialog (parent, message);
3098 g_free (escaped_basename);
3100 response = modest_platform_run_confirmation_dialog (parent,
3101 _FM_REPLACE_MULTIPLE);
3103 if (response != GTK_RESPONSE_OK)
3108 save_mime_part_info_free (info, TRUE);
3110 g_thread_create ((GThreadFunc)save_mime_part_to_file, info, FALSE, NULL);
3115 typedef struct _SaveAttachmentsInfo {
3116 TnyList *attachments_list;
3117 ModestMsgViewWindow *window;
3118 } SaveAttachmentsInfo;
3121 save_attachments_response (GtkDialog *dialog,
3125 TnyList *mime_parts;
3127 GList *files_to_save = NULL;
3128 gchar *current_folder;
3129 SaveAttachmentsInfo *sa_info = (SaveAttachmentsInfo *) user_data;
3131 mime_parts = TNY_LIST (sa_info->attachments_list);
3133 if (arg1 != GTK_RESPONSE_OK)
3136 chooser_uri = gtk_file_chooser_get_uri (GTK_FILE_CHOOSER (dialog));
3137 current_folder = gtk_file_chooser_get_current_folder_uri (GTK_FILE_CHOOSER (dialog));
3138 if (current_folder && *current_folder != '\0') {
3140 modest_conf_set_string (modest_runtime_get_conf (), MODEST_CONF_LATEST_SAVE_ATTACHMENT_PATH,
3141 current_folder,&err);
3143 g_debug ("Error storing latest used folder: %s", err->message);
3147 g_free (current_folder);
3149 if (!modest_utils_folder_writable (chooser_uri)) {
3150 const gchar *err_msg;
3152 #ifdef MODEST_PLATFORM_MAEMO
3153 if (modest_maemo_utils_in_usb_mode ()) {
3154 err_msg = dgettext ("hildon-status-bar-usb", "usbh_ib_mmc_usb_connected");
3156 err_msg = _FM_READ_ONLY_LOCATION;
3159 err_msg = _FM_READ_ONLY_LOCATION;
3161 modest_platform_system_banner (NULL, NULL, err_msg);
3165 iter = tny_list_create_iterator (mime_parts);
3166 while (!tny_iterator_is_done (iter)) {
3167 TnyMimePart *mime_part = (TnyMimePart *) tny_iterator_get_current (iter);
3169 if ((modest_tny_mime_part_is_attachment_for_modest (mime_part)) &&
3170 !tny_mime_part_is_purged (mime_part) &&
3171 (tny_mime_part_get_filename (mime_part) != NULL)) {
3172 SaveMimePartPair *pair;
3174 pair = g_slice_new0 (SaveMimePartPair);
3176 if (tny_list_get_length (mime_parts) > 1) {
3178 gnome_vfs_escape_slashes (tny_mime_part_get_filename (mime_part));
3179 pair->filename = g_build_filename (chooser_uri, escaped, NULL);
3182 pair->filename = g_strdup (chooser_uri);
3184 pair->part = mime_part;
3185 files_to_save = g_list_prepend (files_to_save, pair);
3187 tny_iterator_next (iter);
3189 g_object_unref (iter);
3192 if (files_to_save != NULL) {
3193 SaveMimePartInfo *info = g_slice_new0 (SaveMimePartInfo);
3194 info->pairs = files_to_save;
3195 info->result = TRUE;
3196 info->uri = g_strdup (chooser_uri);
3197 info->window = g_object_ref (sa_info->window);
3198 save_mime_parts_to_file_with_checks ((GtkWindow *) dialog, info);
3200 g_free (chooser_uri);
3203 /* Free and close the dialog */
3204 g_object_unref (mime_parts);
3205 g_object_unref (sa_info->window);
3206 g_slice_free (SaveAttachmentsInfo, sa_info);
3207 gtk_widget_destroy (GTK_WIDGET (dialog));
3211 modest_msg_view_window_save_attachments (ModestMsgViewWindow *window,
3212 TnyList *mime_parts)
3214 ModestMsgViewWindowPrivate *priv;
3215 GtkWidget *save_dialog = NULL;
3216 gchar *conf_folder = NULL;
3217 gchar *filename = NULL;
3218 gchar *save_multiple_str = NULL;
3219 const gchar *root_folder = "file:///";
3221 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window));
3222 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
3224 if (mime_parts == NULL) {
3225 /* In Hildon 2.2 save and delete operate over all the attachments as there's no
3226 * selection available */
3227 mime_parts = modest_msg_view_get_attachments (MODEST_MSG_VIEW (priv->msg_view));
3228 if (mime_parts && !modest_toolkit_utils_select_attachments (GTK_WINDOW (gtk_widget_get_toplevel (window)), mime_parts, FALSE)) {
3229 g_object_unref (mime_parts);
3232 if (mime_parts == NULL || tny_list_get_length (mime_parts) == 0) {
3234 g_object_unref (mime_parts);
3240 g_object_ref (mime_parts);
3243 /* prepare dialog */
3244 if (tny_list_get_length (mime_parts) == 1) {
3246 /* only one attachment selected */
3247 iter = tny_list_create_iterator (mime_parts);
3248 TnyMimePart *mime_part = (TnyMimePart *) tny_iterator_get_current (iter);
3249 g_object_unref (iter);
3250 if (!modest_tny_mime_part_is_msg (mime_part) &&
3251 modest_tny_mime_part_is_attachment_for_modest (mime_part) &&
3252 !tny_mime_part_is_purged (mime_part)) {
3253 filename = g_strdup (tny_mime_part_get_filename (mime_part));
3255 /* TODO: show any error? */
3256 g_warning ("%s: Tried to save a non-file attachment", __FUNCTION__);
3257 g_object_unref (mime_parts);
3260 g_object_unref (mime_part);
3262 gint num = tny_list_get_length (mime_parts);
3263 save_multiple_str = g_strdup_printf (dngettext("hildon-fm",
3264 "sfil_va_number_of_objects_attachment",
3265 "sfil_va_number_of_objects_attachments",
3269 /* Creation of hildon file chooser dialog for saving */
3270 save_dialog = modest_toolkit_factory_create_file_chooser_dialog (modest_runtime_get_toolkit_factory (),
3272 (GtkWindow *) window,
3273 GTK_FILE_CHOOSER_ACTION_SAVE);
3275 /* Get last used folder */
3276 conf_folder = modest_conf_get_string (modest_runtime_get_conf (),
3277 MODEST_CONF_LATEST_SAVE_ATTACHMENT_PATH, NULL);
3279 /* File chooser stops working if we select "file:///" as current folder */
3280 if (conf_folder && g_ascii_strcasecmp (root_folder, conf_folder) != 0) {
3281 g_free (conf_folder);
3285 if (conf_folder && conf_folder[0] != '\0') {
3286 gtk_file_chooser_set_current_folder_uri (GTK_FILE_CHOOSER (save_dialog), conf_folder);
3289 /* Set the default folder to documents folder */
3290 docs_folder = (gchar *) g_strdup(g_get_user_special_dir (G_USER_DIRECTORY_DOCUMENTS));
3293 docs_folder = g_build_filename (g_getenv (MYDOCS_ENV), DOCS_FOLDER, NULL);
3295 gtk_file_chooser_set_current_folder (GTK_FILE_CHOOSER (save_dialog), docs_folder);
3296 g_free (docs_folder);
3298 g_free (conf_folder);
3302 gtk_file_chooser_set_current_name (GTK_FILE_CHOOSER (save_dialog),
3307 /* if multiple, set multiple string */
3308 if (save_multiple_str) {
3309 g_object_set (G_OBJECT (save_dialog), "save-multiple", save_multiple_str, NULL);
3310 gtk_window_set_title (GTK_WINDOW (save_dialog), _FM_SAVE_OBJECT_FILES);
3311 g_free (save_multiple_str);
3314 /* We must run this asynchronously, because the hildon dialog
3315 performs a gtk_dialog_run by itself which leads to gdk
3317 SaveAttachmentsInfo *sa_info;
3318 sa_info = g_slice_new (SaveAttachmentsInfo);
3319 sa_info->attachments_list = mime_parts;
3320 sa_info->window = g_object_ref (window);
3321 g_signal_connect (save_dialog, "response",
3322 G_CALLBACK (save_attachments_response), sa_info);
3324 gtk_widget_show_all (save_dialog);
3328 show_remove_attachment_information (gpointer userdata)
3330 ModestMsgViewWindow *window = (ModestMsgViewWindow *) userdata;
3331 ModestMsgViewWindowPrivate *priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
3333 /* We're outside the main lock */
3334 gdk_threads_enter ();
3336 if (priv->remove_attachment_banner != NULL) {
3337 gtk_widget_destroy (priv->remove_attachment_banner);
3338 g_object_unref (priv->remove_attachment_banner);
3341 priv->remove_attachment_banner = g_object_ref (
3342 modest_platform_animation_banner (NULL, NULL, _("mcen_me_inbox_remove_attachments")));
3344 gdk_threads_leave ();
3350 modest_msg_view_window_remove_attachments (ModestMsgViewWindow *window, gboolean get_all)
3352 ModestMsgViewWindowPrivate *priv;
3353 TnyList *mime_parts = NULL, *tmp;
3354 gchar *confirmation_message;
3360 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window));
3361 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
3363 /* In hildon 2.2 we ignore the get_all flag as we always get all attachments. This is
3364 * because we don't have selection
3366 mime_parts = modest_msg_view_get_attachments (MODEST_MSG_VIEW (priv->msg_view));
3368 /* Remove already purged messages from mime parts list. We use
3369 a copy of the list to remove items in the original one */
3370 tmp = tny_list_copy (mime_parts);
3371 iter = tny_list_create_iterator (tmp);
3372 while (!tny_iterator_is_done (iter)) {
3373 TnyMimePart *part = TNY_MIME_PART (tny_iterator_get_current (iter));
3374 if (tny_mime_part_is_purged (part))
3375 tny_list_remove (mime_parts, (GObject *) part);
3377 g_object_unref (part);
3378 tny_iterator_next (iter);
3380 g_object_unref (tmp);
3381 g_object_unref (iter);
3383 if (!modest_toolkit_utils_select_attachments (GTK_WINDOW (window), mime_parts, TRUE) ||
3384 tny_list_get_length (mime_parts) == 0) {
3385 g_object_unref (mime_parts);
3389 n_attachments = tny_list_get_length (mime_parts);
3390 if (n_attachments == 1) {
3394 iter = tny_list_create_iterator (mime_parts);
3395 part = (TnyMimePart *) tny_iterator_get_current (iter);
3396 g_object_unref (iter);
3397 if (modest_tny_mime_part_is_msg (part)) {
3399 header = tny_msg_get_header (TNY_MSG (part));
3400 filename = tny_header_dup_subject (header);
3401 g_object_unref (header);
3402 if (filename == NULL)
3403 filename = g_strdup (_("mail_va_no_subject"));
3405 filename = g_strdup (tny_mime_part_get_filename (TNY_MIME_PART (part)));
3407 confirmation_message = g_strdup_printf (_("mcen_nc_purge_file_text"), filename);
3409 g_object_unref (part);
3411 confirmation_message = g_strdup_printf (ngettext("mcen_nc_purge_file_text",
3412 "mcen_nc_purge_files_text",
3413 n_attachments), n_attachments);
3415 response = modest_platform_run_confirmation_dialog (GTK_WINDOW (window),
3416 confirmation_message);
3417 g_free (confirmation_message);
3419 if (response != GTK_RESPONSE_OK) {
3420 g_object_unref (mime_parts);
3424 priv->purge_timeout = g_timeout_add (2000, show_remove_attachment_information, window);
3426 iter = tny_list_create_iterator (mime_parts);
3427 while (!tny_iterator_is_done (iter)) {
3430 part = (TnyMimePart *) tny_iterator_get_current (iter);
3431 tny_mime_part_set_purged (TNY_MIME_PART (part));
3432 g_object_unref (part);
3433 tny_iterator_next (iter);
3435 g_object_unref (iter);
3437 msg = tny_msg_view_get_msg (TNY_MSG_VIEW (priv->msg_view));
3438 tny_msg_view_clear (TNY_MSG_VIEW (priv->msg_view));
3439 tny_msg_rewrite_cache (msg);
3440 tny_msg_view_set_msg (TNY_MSG_VIEW (priv->msg_view), msg);
3441 g_object_unref (msg);
3442 update_branding (MODEST_MSG_VIEW_WINDOW (window));
3444 g_object_unref (mime_parts);
3446 if (priv->purge_timeout > 0) {
3447 g_source_remove (priv->purge_timeout);
3448 priv->purge_timeout = 0;
3451 if (priv->remove_attachment_banner) {
3452 gtk_widget_destroy (priv->remove_attachment_banner);
3453 g_object_unref (priv->remove_attachment_banner);
3454 priv->remove_attachment_banner = NULL;
3460 update_window_title (ModestMsgViewWindow *window)
3462 ModestMsgViewWindowPrivate *priv;
3464 TnyHeader *header = NULL;
3465 gchar *subject = NULL;
3467 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
3469 /* Note that if the window is closed while we're retrieving
3470 the message, this widget could de deleted */
3471 if (!priv->msg_view)
3474 msg = tny_msg_view_get_msg (TNY_MSG_VIEW (priv->msg_view));
3476 if (priv->other_body) {
3479 description = modest_tny_mime_part_get_header_value (priv->other_body, "Content-Description");
3481 g_strstrip (description);
3482 subject = description;
3484 } else if (msg != NULL) {
3485 header = tny_msg_get_header (msg);
3486 subject = tny_header_dup_subject (header);
3487 g_object_unref (header);
3488 g_object_unref (msg);
3491 if ((subject == NULL)||(subject[0] == '\0')) {
3493 subject = g_strdup (_("mail_va_no_subject"));
3496 modest_window_set_title (MODEST_WINDOW (window), subject);
3501 on_move_focus (GtkWidget *widget,
3502 GtkDirectionType direction,
3505 g_signal_stop_emission_by_name (G_OBJECT (widget), "move-focus");
3509 fetch_image_open_stream (TnyStreamCache *self, gint64 *expected_size, gchar *uri)
3511 GnomeVFSResult result;
3512 GnomeVFSHandle *handle = NULL;
3513 GnomeVFSFileInfo *info = NULL;
3516 result = gnome_vfs_open (&handle, uri, GNOME_VFS_OPEN_READ);
3517 if (result != GNOME_VFS_OK) {
3522 info = gnome_vfs_file_info_new ();
3523 result = gnome_vfs_get_file_info_from_handle (handle, info, GNOME_VFS_FILE_INFO_DEFAULT);
3524 if (result != GNOME_VFS_OK || ! (info->valid_fields & GNOME_VFS_FILE_INFO_FIELDS_SIZE)) {
3525 /* We put a "safe" default size for going to cache */
3526 *expected_size = (300*1024);
3528 *expected_size = info->size;
3530 gnome_vfs_file_info_unref (info);
3532 stream = tny_vfs_stream_new (handle);
3541 TnyStream *output_stream;
3542 GtkWidget *msg_view;
3547 on_fetch_image_idle_refresh_view (gpointer userdata)
3550 FetchImageData *fidata = (FetchImageData *) userdata;
3552 gdk_threads_enter ();
3553 if (GTK_WIDGET_DRAWABLE (fidata->msg_view)) {
3554 ModestMsgViewWindowPrivate *priv;
3556 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (fidata->window);
3557 priv->fetching_images--;
3558 gtk_widget_queue_draw (fidata->msg_view);
3559 update_progress_hint (MODEST_MSG_VIEW_WINDOW (fidata->window));
3561 gdk_threads_leave ();
3563 g_object_unref (fidata->msg_view);
3564 g_object_unref (fidata->window);
3565 g_slice_free (FetchImageData, fidata);
3570 on_fetch_image_thread (gpointer userdata)
3572 FetchImageData *fidata = (FetchImageData *) userdata;
3573 TnyStreamCache *cache;
3574 TnyStream *cache_stream;
3576 cache = modest_runtime_get_images_cache ();
3578 tny_stream_cache_get_stream (cache,
3580 (TnyStreamCacheOpenStreamFetcher) fetch_image_open_stream,
3581 (gpointer) fidata->uri);
3582 g_free (fidata->cache_id);
3583 g_free (fidata->uri);
3585 if (cache_stream != NULL) {
3588 while (G_LIKELY (!tny_stream_is_eos (cache_stream))) {
3591 nb_read = tny_stream_read (cache_stream, buffer, sizeof (buffer));
3592 if (G_UNLIKELY (nb_read < 0)) {
3594 } else if (G_LIKELY (nb_read > 0)) {
3595 gssize nb_written = 0;
3597 while (G_UNLIKELY (nb_written < nb_read)) {
3600 len = tny_stream_write (fidata->output_stream, buffer + nb_written,
3601 nb_read - nb_written);
3602 if (G_UNLIKELY (len < 0))
3608 tny_stream_close (cache_stream);
3609 g_object_unref (cache_stream);
3612 tny_stream_close (fidata->output_stream);
3613 g_object_unref (fidata->output_stream);
3615 g_idle_add (on_fetch_image_idle_refresh_view, fidata);
3621 on_fetch_image (ModestMsgView *msgview,
3624 ModestMsgViewWindow *window)
3626 const gchar *current_account;
3627 ModestMsgViewWindowPrivate *priv;
3628 FetchImageData *fidata;
3630 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
3632 current_account = modest_window_get_active_account (MODEST_WINDOW (window));
3634 fidata = g_slice_new0 (FetchImageData);
3635 fidata->msg_view = g_object_ref (msgview);
3636 fidata->window = g_object_ref (window);
3637 fidata->uri = g_strdup (uri);
3638 fidata->cache_id = modest_images_cache_get_id (current_account, uri);
3639 fidata->output_stream = g_object_ref (stream);
3641 priv->fetching_images++;
3642 if (g_thread_create (on_fetch_image_thread, fidata, FALSE, NULL) == NULL) {
3643 g_object_unref (fidata->output_stream);
3644 g_free (fidata->cache_id);
3645 g_free (fidata->uri);
3646 g_object_unref (fidata->msg_view);
3647 g_slice_free (FetchImageData, fidata);
3648 tny_stream_close (stream);
3649 priv->fetching_images--;
3650 update_progress_hint (window);
3653 update_progress_hint (window);
3659 setup_menu (ModestMsgViewWindow *self)
3661 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW(self));
3663 /* Settings menu buttons */
3664 modest_window_add_to_menu (MODEST_WINDOW (self), _("mcen_me_viewer_find"), NULL,
3665 MODEST_WINDOW_MENU_CALLBACK (modest_msg_view_window_show_isearch_toolbar),
3666 MODEST_DIMMING_CALLBACK (modest_ui_dimming_rules_on_find_in_msg));
3668 modest_window_add_to_menu (MODEST_WINDOW (self),
3669 dngettext(GETTEXT_PACKAGE,
3670 "mcen_me_move_message",
3671 "mcen_me_move_messages",
3674 MODEST_WINDOW_MENU_CALLBACK (modest_ui_actions_on_move_to),
3675 MODEST_DIMMING_CALLBACK (modest_ui_dimming_rules_on_move_to));
3677 modest_window_add_to_menu (MODEST_WINDOW (self), _("mcen_me_inbox_mark_as_read"), NULL,
3678 MODEST_WINDOW_MENU_CALLBACK (modest_ui_actions_on_mark_as_read),
3679 MODEST_DIMMING_CALLBACK (modest_ui_dimming_rules_on_mark_as_read_msg_in_view));
3681 modest_window_add_to_menu (MODEST_WINDOW (self), _("mcen_me_inbox_mark_as_unread"), NULL,
3682 MODEST_WINDOW_MENU_CALLBACK (modest_ui_actions_on_mark_as_unread),
3683 MODEST_DIMMING_CALLBACK (modest_ui_dimming_rules_on_mark_as_unread_msg_in_view));
3685 modest_window_add_to_menu (MODEST_WINDOW (self), _("mcen_me_viewer_save_attachments"), NULL,
3686 MODEST_WINDOW_MENU_CALLBACK (modest_ui_actions_save_attachments),
3687 MODEST_DIMMING_CALLBACK (modest_ui_dimming_rules_on_save_attachments));
3688 modest_window_add_to_menu (MODEST_WINDOW (self), _("mcen_me_inbox_remove_attachments"), NULL,
3689 MODEST_WINDOW_MENU_CALLBACK (modest_ui_actions_remove_attachments),
3690 MODEST_DIMMING_CALLBACK (modest_ui_dimming_rules_on_remove_attachments));
3692 modest_window_add_to_menu (MODEST_WINDOW (self), _("mcen_me_new_message"), "<Control>n",
3693 MODEST_WINDOW_MENU_CALLBACK (modest_ui_actions_on_new_msg),
3694 MODEST_DIMMING_CALLBACK (modest_ui_dimming_rules_on_new_msg));
3695 modest_window_add_to_menu (MODEST_WINDOW (self), _("mcen_me_viewer_addtocontacts"), NULL,
3696 MODEST_WINDOW_MENU_CALLBACK (modest_ui_actions_add_to_contacts),
3697 MODEST_DIMMING_CALLBACK (modest_ui_dimming_rules_on_add_to_contacts));
3699 modest_window_add_to_menu (MODEST_WINDOW (self), _("mcen_ti_message_properties"), NULL,
3700 MODEST_WINDOW_MENU_CALLBACK (modest_ui_actions_on_details),
3701 MODEST_DIMMING_CALLBACK (modest_ui_dimming_rules_on_details));
3705 modest_msg_view_window_add_to_contacts (ModestMsgViewWindow *self)
3707 ModestMsgViewWindowPrivate *priv;
3708 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
3709 GSList *recipients = NULL;
3712 msg = tny_msg_view_get_msg (TNY_MSG_VIEW (priv->msg_view));
3716 header = modest_msg_view_window_get_header (self);
3719 recipients = modest_tny_msg_header_get_all_recipients_list (header);
3720 g_object_unref (header);
3722 recipients = modest_tny_msg_get_all_recipients_list (msg);
3723 g_object_unref (msg);
3727 /* Offer the user to add recipients to the address book */
3728 modest_address_book_add_address_list_with_selector (recipients, (GtkWindow *) self);
3729 g_slist_foreach (recipients, (GFunc) g_free, NULL); g_slist_free (recipients);
3734 _modest_msg_view_window_map_event (GtkWidget *widget,
3738 ModestMsgViewWindow *self = (ModestMsgViewWindow *) userdata;
3740 update_progress_hint (self);
3746 modest_msg_view_window_fetch_images (ModestMsgViewWindow *self)
3748 ModestMsgViewWindowPrivate *priv;
3749 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
3751 modest_msg_view_request_fetch_images (MODEST_MSG_VIEW (priv->msg_view));
3755 modest_msg_view_window_has_blocked_external_images (ModestMsgViewWindow *self)
3757 ModestMsgViewWindowPrivate *priv;
3758 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
3760 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self), FALSE);
3762 return modest_msg_view_has_blocked_external_images (MODEST_MSG_VIEW (priv->msg_view));
3766 modest_msg_view_window_reload (ModestMsgViewWindow *self)
3768 ModestMsgViewWindowPrivate *priv;
3769 const gchar *msg_uid;
3770 TnyHeader *header = NULL;
3771 TnyFolder *folder = NULL;
3773 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self));
3775 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
3777 header = modest_msg_view_window_get_header (MODEST_MSG_VIEW_WINDOW (self));
3781 folder = tny_header_get_folder (header);
3782 g_object_unref (header);
3787 msg_uid = modest_msg_view_window_get_message_uid (self);
3789 GtkTreeRowReference *row_reference;
3791 if (priv->row_reference && gtk_tree_row_reference_valid (priv->row_reference)) {
3792 row_reference = priv->row_reference;
3794 row_reference = NULL;
3796 if (!message_reader (self, priv, NULL, msg_uid, folder, row_reference))
3797 g_warning ("Shouldn't happen, trying to reload a message failed");
3800 g_object_unref (folder);
3804 update_branding (ModestMsgViewWindow *self)
3806 const gchar *account;
3807 const gchar *mailbox;
3808 ModestAccountMgr *mgr;
3809 ModestProtocol *protocol = NULL;
3810 gchar *service_name = NULL;
3811 const GdkPixbuf *service_icon = NULL;
3812 ModestMsgViewWindowPrivate *priv;
3814 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
3816 account = modest_window_get_active_account (MODEST_WINDOW (self));
3817 mailbox = modest_window_get_active_mailbox (MODEST_WINDOW (self));
3819 mgr = modest_runtime_get_account_mgr ();
3821 if (modest_account_mgr_account_is_multimailbox (mgr, account, &protocol)) {
3822 if (MODEST_IS_ACCOUNT_PROTOCOL (protocol)) {
3823 service_name = modest_account_protocol_get_service_name (MODEST_ACCOUNT_PROTOCOL (protocol),
3825 service_icon = modest_account_protocol_get_service_icon (MODEST_ACCOUNT_PROTOCOL (protocol),
3826 account, mailbox, MODEST_ICON_SIZE_SMALL);
3830 modest_msg_view_set_branding (MODEST_MSG_VIEW (priv->msg_view), service_name, service_icon);
3831 g_free (service_name);
3835 sync_flags (ModestMsgViewWindow *self)
3837 TnyHeader *header = NULL;
3839 header = modest_msg_view_window_get_header (self);
3841 TnyMsg *msg = modest_msg_view_window_get_message (self);
3843 header = tny_msg_get_header (msg);
3844 g_object_unref (msg);
3849 TnyFolder *folder = tny_header_get_folder (header);
3852 ModestMailOperation *mail_op;
3854 /* Sync folder, we need this to save the seen flag */
3855 mail_op = modest_mail_operation_new (NULL);
3856 modest_mail_operation_queue_add (modest_runtime_get_mail_operation_queue (),
3858 modest_mail_operation_sync_folder (mail_op, folder, FALSE, NULL, NULL);
3859 g_object_unref (mail_op);
3860 g_object_unref (folder);
3862 g_object_unref (header);