1 /* Copyright (c) 2006, Nokia Corporation
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions are
8 * * Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 * * Redistributions in binary form must reproduce the above copyright
11 * notice, this list of conditions and the following disclaimer in the
12 * documentation and/or other materials provided with the distribution.
13 * * Neither the name of the Nokia Corporation nor the names of its
14 * contributors may be used to endorse or promote products derived from
15 * this software without specific prior written permission.
17 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
18 * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
19 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
20 * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
21 * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
22 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
23 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
24 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
25 * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
26 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
27 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29 #include <glib/gi18n.h>
31 #include <tny-account-store.h>
32 #include <tny-simple-list.h>
34 #include <tny-mime-part.h>
35 #include <tny-vfs-stream.h>
36 #include <tny-error.h>
37 #include "modest-marshal.h"
38 #include "modest-platform.h"
39 #include <modest-utils.h>
40 #include <modest-toolkit-utils.h>
41 #include <modest-tny-msg.h>
42 #include <modest-msg-view-window.h>
43 #include "modest-msg-view-window-ui-dimming.h"
44 #include <modest-widget-memory.h>
45 #include <modest-progress-object.h>
46 #include <modest-runtime.h>
47 #include <modest-window-priv.h>
48 #include <modest-tny-folder.h>
49 #include <modest-text-utils.h>
50 #include <modest-account-mgr-helpers.h>
51 #include <modest-toolkit-factory.h>
52 #include <modest-scrollable.h>
53 #include <modest-isearch-toolbar.h>
54 #include "modest-defs.h"
55 #include "modest-ui-dimming-manager.h"
56 #include <gdk/gdkkeysyms.h>
57 #include <modest-tny-account.h>
58 #include <modest-mime-part-view.h>
59 #include <modest-isearch-view.h>
60 #include <modest-tny-mime-part.h>
61 #include <modest-address-book.h>
64 #include <glib/gstdio.h>
65 #include <modest-debug.h>
66 #include <modest-header-window.h>
67 #include <modest-account-protocol.h>
68 #include <modest-icon-names.h>
69 #include <modest-ui-actions.h>
70 #include <modest-window-mgr.h>
71 #include <tny-camel-msg.h>
72 #include <modest-icon-names.h>
74 #ifdef MODEST_PLATFORM_MAEMO
75 #include <modest-maemo-utils.h>
78 #ifdef MODEST_TOOLKIT_HILDON2
79 #include <hildon/hildon.h>
81 #include <tny-camel-bs-mime-part.h>
82 #include <tny-camel-bs-msg.h>
84 #define MYDOCS_ENV "MYDOCSDIR"
85 #define DOCS_FOLDER ".documents"
87 typedef struct _ModestMsgViewWindowPrivate ModestMsgViewWindowPrivate;
88 struct _ModestMsgViewWindowPrivate {
91 GtkWidget *main_scroll;
92 GtkWidget *isearch_toolbar;
95 /* Progress observers */
96 GSList *progress_widgets;
99 GtkWidget *prev_toolitem;
100 GtkWidget *next_toolitem;
101 gboolean progress_hint;
102 gint fetching_images;
104 /* Optimized view enabled */
105 gboolean optimized_view;
107 /* Whether this was created via the *_new_for_search_result() function. */
108 gboolean is_search_result;
110 /* Whether the message is in outbox */
113 /* A reference to the @model of the header view
114 * to allow selecting previous/next messages,
115 * if the message is currently selected in the header view.
117 const gchar *header_folder_id;
118 GtkTreeModel *header_model;
119 GtkTreeRowReference *row_reference;
120 GtkTreeRowReference *next_row_reference;
122 gulong clipboard_change_handler;
123 gulong queue_change_handler;
124 gulong account_removed_handler;
125 gulong row_changed_handler;
126 gulong row_deleted_handler;
127 gulong row_inserted_handler;
128 gulong rows_reordered_handler;
129 gulong fetch_image_redraw_handler;
132 GtkWidget *remove_attachment_banner;
135 TnyMimePart *other_body;
140 static void modest_msg_view_window_class_init (ModestMsgViewWindowClass *klass);
141 static void modest_msg_view_window_init (ModestMsgViewWindow *obj);
142 static void modest_header_view_observer_init (ModestHeaderViewObserverIface *iface_class);
143 static void modest_msg_view_window_finalize (GObject *obj);
144 static void modest_msg_view_window_show_isearch_toolbar (GtkWidget *obj, gpointer data);
145 static void modest_msg_view_window_isearch_toolbar_close (GtkWidget *widget,
146 ModestMsgViewWindow *obj);
147 static void modest_msg_view_window_isearch_toolbar_search (GtkWidget *widget,
148 ModestMsgViewWindow *obj);
149 static void modest_msg_view_window_toggle_isearch_toolbar (GtkWidget *obj,
151 static void modest_msg_view_window_disconnect_signals (ModestWindow *self);
153 static gdouble modest_msg_view_window_get_zoom (ModestWindow *window);
154 static void modest_msg_view_window_set_zoom (ModestWindow *window,
156 static gboolean modest_msg_view_window_zoom_minus (ModestWindow *window);
157 static gboolean modest_msg_view_window_zoom_plus (ModestWindow *window);
158 static gboolean modest_msg_view_window_key_event (GtkWidget *window,
161 static void modest_msg_view_window_update_priority (ModestMsgViewWindow *window);
163 static void modest_msg_view_window_show_toolbar (ModestWindow *window,
164 gboolean show_toolbar);
166 static void modest_msg_view_window_clipboard_owner_change (GtkClipboard *clipboard,
168 ModestMsgViewWindow *window);
170 static void modest_msg_view_window_on_row_changed (GtkTreeModel *header_model,
173 ModestMsgViewWindow *window);
175 static void modest_msg_view_window_on_row_deleted (GtkTreeModel *header_model,
177 ModestMsgViewWindow *window);
179 static void modest_msg_view_window_on_row_inserted (GtkTreeModel *header_model,
180 GtkTreePath *tree_path,
181 GtkTreeIter *tree_iter,
182 ModestMsgViewWindow *window);
184 static void modest_msg_view_window_on_row_reordered (GtkTreeModel *header_model,
188 ModestMsgViewWindow *window);
190 static void modest_msg_view_window_update_model_replaced (ModestHeaderViewObserver *window,
192 const gchar *tny_folder_id);
194 static void on_queue_changed (ModestMailOperationQueue *queue,
195 ModestMailOperation *mail_op,
196 ModestMailOperationQueueNotification type,
197 ModestMsgViewWindow *self);
199 static void on_account_removed (TnyAccountStore *account_store,
203 static void on_move_focus (GtkWidget *widget,
204 GtkDirectionType direction,
207 static void view_msg_cb (ModestMailOperation *mail_op,
214 static void set_progress_hint (ModestMsgViewWindow *self,
217 static void update_window_title (ModestMsgViewWindow *window);
219 static void init_window (ModestMsgViewWindow *obj);
221 static gboolean msg_is_visible (TnyHeader *header, gboolean check_outbox);
223 static void check_dimming_rules_after_change (ModestMsgViewWindow *window);
225 static gboolean on_fetch_image (ModestMsgView *msgview,
228 ModestMsgViewWindow *window);
230 static gboolean modest_msg_view_window_scroll_child (ModestMsgViewWindow *self,
231 GtkScrollType scroll_type,
234 static gboolean message_reader (ModestMsgViewWindow *window,
235 ModestMsgViewWindowPrivate *priv,
237 const gchar *msg_uid,
239 GtkTreeRowReference *row_reference);
241 static void setup_menu (ModestMsgViewWindow *self);
242 static gboolean _modest_msg_view_window_map_event (GtkWidget *widget,
245 static void update_branding (ModestMsgViewWindow *self);
246 static void sync_flags (ModestMsgViewWindow *self);
248 /* list my signals */
255 static const GtkActionEntry msg_view_toolbar_action_entries [] = {
258 { "ToolbarMessageReply", MODEST_STOCK_REPLY, N_("mcen_me_inbox_reply"), "<CTRL>R", NULL, G_CALLBACK (modest_ui_actions_on_reply) },
259 { "ToolbarMessageReplyAll", MODEST_STOCK_REPLY_ALL, N_("mcen_me_inbox_replytoall"), NULL, NULL, G_CALLBACK (modest_ui_actions_on_reply_all) },
260 { "ToolbarMessageForward", MODEST_STOCK_FORWARD, N_("mcen_me_inbox_forward"), NULL, NULL, G_CALLBACK (modest_ui_actions_on_forward) },
261 { "ToolbarDeleteMessage", MODEST_STOCK_DELETE, N_("qgn_toolb_gene_deletebutton"), NULL, NULL, G_CALLBACK (modest_ui_actions_on_delete_message_or_folder) },
262 { "ToolbarMessageBack", MODEST_TOOLBAR_ICON_PREV, N_("qgn_toolb_gene_back"), NULL, NULL, G_CALLBACK (modest_ui_actions_on_prev) },
263 { "ToolbarMessageNext", MODEST_TOOLBAR_ICON_NEXT, N_("qgn_toolb_gene_forward"), NULL, NULL, G_CALLBACK (modest_ui_actions_on_next) },
264 { "ToolbarDownloadExternalImages", MODEST_TOOLBAR_ICON_DOWNLOAD_IMAGES, N_("mail_bd_external_images"), NULL, NULL, G_CALLBACK (modest_ui_actions_on_fetch_images) },
267 static const GtkToggleActionEntry msg_view_toggle_action_entries [] = {
268 { "FindInMessage", MODEST_TOOLBAR_ICON_FIND, N_("qgn_toolb_gene_find"), "<CTRL>F", NULL, G_CALLBACK (modest_msg_view_window_toggle_isearch_toolbar), FALSE },
271 #define MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE((o), \
272 MODEST_TYPE_MSG_VIEW_WINDOW, \
273 ModestMsgViewWindowPrivate))
275 static GtkWindowClass *parent_class = NULL;
277 /* uncomment the following if you have defined any signals */
278 static guint signals[LAST_SIGNAL] = {0};
281 modest_msg_view_window_get_type (void)
283 static GType my_type = 0;
285 static const GTypeInfo my_info = {
286 sizeof(ModestMsgViewWindowClass),
287 NULL, /* base init */
288 NULL, /* base finalize */
289 (GClassInitFunc) modest_msg_view_window_class_init,
290 NULL, /* class finalize */
291 NULL, /* class data */
292 sizeof(ModestMsgViewWindow),
294 (GInstanceInitFunc) modest_msg_view_window_init,
297 #ifndef MODEST_TOOLKIT_HILDON2
298 my_type = g_type_register_static (MODEST_TYPE_SHELL_WINDOW,
299 "ModestMsgViewWindow",
302 my_type = g_type_register_static (MODEST_TYPE_HILDON2_WINDOW,
303 "ModestMsgViewWindow",
307 static const GInterfaceInfo modest_header_view_observer_info =
309 (GInterfaceInitFunc) modest_header_view_observer_init,
310 NULL, /* interface_finalize */
311 NULL /* interface_data */
314 g_type_add_interface_static (my_type,
315 MODEST_TYPE_HEADER_VIEW_OBSERVER,
316 &modest_header_view_observer_info);
322 save_state (ModestWindow *self)
324 modest_widget_memory_save (modest_runtime_get_conf (),
326 MODEST_CONF_MSG_VIEW_WINDOW_KEY);
330 modest_msg_view_window_scroll_child (ModestMsgViewWindow *self,
331 GtkScrollType scroll_type,
335 ModestMsgViewWindowPrivate *priv;
338 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(self);
340 switch (scroll_type) {
341 case GTK_SCROLL_STEP_UP:
344 case GTK_SCROLL_STEP_DOWN:
347 case GTK_SCROLL_PAGE_UP:
350 case GTK_SCROLL_PAGE_DOWN:
353 case GTK_SCROLL_START:
364 modest_scrollable_scroll ((ModestScrollable *) priv->main_scroll, 0, step);
366 return (gboolean) step;
370 add_scroll_binding (GtkBindingSet *binding_set,
372 GtkScrollType scroll)
374 guint keypad_keyval = keyval - GDK_Left + GDK_KP_Left;
376 gtk_binding_entry_add_signal (binding_set, keyval, 0,
378 GTK_TYPE_SCROLL_TYPE, scroll,
379 G_TYPE_BOOLEAN, FALSE);
380 gtk_binding_entry_add_signal (binding_set, keypad_keyval, 0,
382 GTK_TYPE_SCROLL_TYPE, scroll,
383 G_TYPE_BOOLEAN, FALSE);
387 modest_msg_view_window_class_init (ModestMsgViewWindowClass *klass)
389 GObjectClass *gobject_class;
390 ModestWindowClass *modest_window_class;
391 GtkBindingSet *binding_set;
393 gobject_class = (GObjectClass*) klass;
394 modest_window_class = (ModestWindowClass *) klass;
396 parent_class = g_type_class_peek_parent (klass);
397 gobject_class->finalize = modest_msg_view_window_finalize;
399 modest_window_class->set_zoom_func = modest_msg_view_window_set_zoom;
400 modest_window_class->get_zoom_func = modest_msg_view_window_get_zoom;
401 modest_window_class->zoom_plus_func = modest_msg_view_window_zoom_plus;
402 modest_window_class->zoom_minus_func = modest_msg_view_window_zoom_minus;
403 modest_window_class->show_toolbar_func = modest_msg_view_window_show_toolbar;
404 modest_window_class->disconnect_signals_func = modest_msg_view_window_disconnect_signals;
406 modest_window_class->save_state_func = save_state;
408 klass->scroll_child = modest_msg_view_window_scroll_child;
410 signals[MSG_CHANGED_SIGNAL] =
411 g_signal_new ("msg-changed",
412 G_TYPE_FROM_CLASS (gobject_class),
414 G_STRUCT_OFFSET (ModestMsgViewWindowClass, msg_changed),
416 modest_marshal_VOID__POINTER_POINTER,
417 G_TYPE_NONE, 2, G_TYPE_POINTER, G_TYPE_POINTER);
419 signals[SCROLL_CHILD_SIGNAL] =
420 g_signal_new ("scroll-child",
421 G_TYPE_FROM_CLASS (gobject_class),
422 G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
423 G_STRUCT_OFFSET (ModestMsgViewWindowClass, scroll_child),
425 modest_marshal_BOOLEAN__ENUM_BOOLEAN,
426 G_TYPE_BOOLEAN, 2, GTK_TYPE_SCROLL_TYPE, G_TYPE_BOOLEAN);
428 binding_set = gtk_binding_set_by_class (klass);
429 add_scroll_binding (binding_set, GDK_Up, GTK_SCROLL_STEP_UP);
430 add_scroll_binding (binding_set, GDK_Down, GTK_SCROLL_STEP_DOWN);
431 add_scroll_binding (binding_set, GDK_Page_Up, GTK_SCROLL_PAGE_UP);
432 add_scroll_binding (binding_set, GDK_Page_Down, GTK_SCROLL_PAGE_DOWN);
433 add_scroll_binding (binding_set, GDK_Home, GTK_SCROLL_START);
434 add_scroll_binding (binding_set, GDK_End, GTK_SCROLL_END);
436 g_type_class_add_private (gobject_class, sizeof(ModestMsgViewWindowPrivate));
440 static void modest_header_view_observer_init(
441 ModestHeaderViewObserverIface *iface_class)
443 iface_class->update_func = modest_msg_view_window_update_model_replaced;
447 modest_msg_view_window_init (ModestMsgViewWindow *obj)
449 ModestMsgViewWindowPrivate *priv;
450 ModestWindowPrivate *parent_priv = NULL;
451 GtkActionGroup *action_group = NULL;
452 GError *error = NULL;
454 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(obj);
455 parent_priv = MODEST_WINDOW_GET_PRIVATE(obj);
456 parent_priv->ui_manager = gtk_ui_manager_new();
458 action_group = gtk_action_group_new ("ModestMsgViewWindowActions");
459 gtk_action_group_set_translation_domain (action_group, GETTEXT_PACKAGE);
461 /* Add common actions */
462 gtk_action_group_add_actions (action_group,
463 msg_view_toolbar_action_entries,
464 G_N_ELEMENTS (msg_view_toolbar_action_entries),
466 gtk_action_group_add_toggle_actions (action_group,
467 msg_view_toggle_action_entries,
468 G_N_ELEMENTS (msg_view_toggle_action_entries),
471 gtk_ui_manager_insert_action_group (parent_priv->ui_manager, action_group, 0);
472 g_object_unref (action_group);
474 /* Load the UI definition */
475 gtk_ui_manager_add_ui_from_file (parent_priv->ui_manager, MODEST_UIDIR "modest-msg-view-window-ui.xml",
478 g_printerr ("modest: could not merge modest-msg-view-window-ui.xml: %s\n", error->message);
479 g_error_free (error);
483 priv->is_search_result = FALSE;
484 priv->is_outbox = FALSE;
486 priv->msg_view = NULL;
487 priv->header_model = NULL;
488 priv->header_folder_id = NULL;
489 priv->clipboard_change_handler = 0;
490 priv->queue_change_handler = 0;
491 priv->account_removed_handler = 0;
492 priv->row_changed_handler = 0;
493 priv->row_deleted_handler = 0;
494 priv->row_inserted_handler = 0;
495 priv->rows_reordered_handler = 0;
496 priv->fetch_image_redraw_handler = 0;
497 priv->progress_hint = FALSE;
498 priv->fetching_images = 0;
500 priv->optimized_view = FALSE;
501 priv->purge_timeout = 0;
502 priv->remove_attachment_banner = NULL;
503 priv->msg_uid = NULL;
504 priv->other_body = NULL;
506 priv->sighandlers = NULL;
509 init_window (MODEST_MSG_VIEW_WINDOW(obj));
514 update_progress_hint (ModestMsgViewWindow *self)
516 ModestMsgViewWindowPrivate *priv;
517 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(self);
519 if (GTK_WIDGET_VISIBLE (self)) {
520 modest_window_show_progress (MODEST_WINDOW (self),
521 (priv->progress_hint || (priv->fetching_images > 0))?1:0);
526 set_progress_hint (ModestMsgViewWindow *self,
529 ModestWindowPrivate *parent_priv;
530 ModestMsgViewWindowPrivate *priv;
532 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self));
534 parent_priv = MODEST_WINDOW_GET_PRIVATE(self);
535 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(self);
537 /* Sets current progress hint */
538 priv->progress_hint = enabled;
540 update_progress_hint (self);
546 init_window (ModestMsgViewWindow *obj)
548 GtkWidget *main_vbox;
549 ModestMsgViewWindowPrivate *priv;
551 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(obj);
553 priv->msg_view = GTK_WIDGET (tny_platform_factory_new_msg_view (modest_tny_platform_factory_get_instance ()));
554 modest_msg_view_set_shadow_type (MODEST_MSG_VIEW (priv->msg_view), GTK_SHADOW_NONE);
555 main_vbox = gtk_vbox_new (FALSE, 6);
557 priv->main_scroll = modest_toolkit_factory_create_scrollable (modest_runtime_get_toolkit_factory ());
558 modest_scrollable_set_horizontal_policy (MODEST_SCROLLABLE (priv->main_scroll), GTK_POLICY_AUTOMATIC);
559 g_object_set (G_OBJECT (priv->main_scroll),
560 "movement-mode", MODEST_MOVEMENT_MODE_BOTH,
561 "horizontal-max-overshoot", 0,
563 gtk_container_add (GTK_CONTAINER (priv->main_scroll), priv->msg_view);
564 gtk_box_pack_start (GTK_BOX(main_vbox), priv->main_scroll, TRUE, TRUE, 0);
565 gtk_container_add (GTK_CONTAINER(obj), main_vbox);
567 /* NULL-ize fields if the window is destroyed */
568 g_signal_connect (priv->msg_view, "destroy", G_CALLBACK (gtk_widget_destroyed), &(priv->msg_view));
570 gtk_widget_show_all (GTK_WIDGET(main_vbox));
574 modest_msg_view_window_disconnect_signals (ModestWindow *self)
576 ModestMsgViewWindowPrivate *priv;
577 GtkWidget *header_view = NULL;
578 GtkWindow *parent_window = NULL;
580 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
582 if (gtk_clipboard_get (GDK_SELECTION_PRIMARY) &&
583 g_signal_handler_is_connected (gtk_clipboard_get (GDK_SELECTION_PRIMARY),
584 priv->clipboard_change_handler))
585 g_signal_handler_disconnect (gtk_clipboard_get (GDK_SELECTION_PRIMARY),
586 priv->clipboard_change_handler);
588 if (g_signal_handler_is_connected (G_OBJECT (modest_runtime_get_mail_operation_queue ()),
589 priv->queue_change_handler))
590 g_signal_handler_disconnect (G_OBJECT (modest_runtime_get_mail_operation_queue ()),
591 priv->queue_change_handler);
593 if (g_signal_handler_is_connected (G_OBJECT (modest_runtime_get_account_store ()),
594 priv->account_removed_handler))
595 g_signal_handler_disconnect (G_OBJECT (modest_runtime_get_account_store ()),
596 priv->account_removed_handler);
598 if (priv->header_model) {
599 if (g_signal_handler_is_connected(G_OBJECT (priv->header_model),
600 priv->row_changed_handler))
601 g_signal_handler_disconnect(G_OBJECT (priv->header_model),
602 priv->row_changed_handler);
604 if (g_signal_handler_is_connected(G_OBJECT (priv->header_model),
605 priv->row_deleted_handler))
606 g_signal_handler_disconnect(G_OBJECT (priv->header_model),
607 priv->row_deleted_handler);
609 if (g_signal_handler_is_connected(G_OBJECT (priv->header_model),
610 priv->row_inserted_handler))
611 g_signal_handler_disconnect(G_OBJECT (priv->header_model),
612 priv->row_inserted_handler);
614 if (g_signal_handler_is_connected(G_OBJECT (priv->header_model),
615 priv->rows_reordered_handler))
616 g_signal_handler_disconnect(G_OBJECT (priv->header_model),
617 priv->rows_reordered_handler);
620 modest_signal_mgr_disconnect_all_and_destroy (priv->sighandlers);
621 priv->sighandlers = NULL;
623 parent_window = gtk_window_get_transient_for (GTK_WINDOW (self));
624 if (parent_window && MODEST_IS_HEADER_WINDOW (parent_window)) {
625 header_view = GTK_WIDGET (modest_header_window_get_header_view (MODEST_HEADER_WINDOW (parent_window)));
627 modest_header_view_remove_observer(MODEST_HEADER_VIEW (header_view),
628 MODEST_HEADER_VIEW_OBSERVER(self));
634 modest_msg_view_window_finalize (GObject *obj)
636 ModestMsgViewWindowPrivate *priv;
638 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (obj);
640 /* Sanity check: shouldn't be needed, the window mgr should
641 call this function before */
642 modest_msg_view_window_disconnect_signals (MODEST_WINDOW (obj));
644 if (priv->fetch_image_redraw_handler > 0) {
645 g_source_remove (priv->fetch_image_redraw_handler);
646 priv->fetch_image_redraw_handler = 0;
649 if (priv->other_body != NULL) {
650 g_object_unref (priv->other_body);
651 priv->other_body = NULL;
654 if (priv->header_model != NULL) {
655 g_object_unref (priv->header_model);
656 priv->header_model = NULL;
659 if (priv->remove_attachment_banner) {
660 gtk_widget_destroy (priv->remove_attachment_banner);
661 g_object_unref (priv->remove_attachment_banner);
662 priv->remove_attachment_banner = NULL;
665 if (priv->purge_timeout > 0) {
666 g_source_remove (priv->purge_timeout);
667 priv->purge_timeout = 0;
670 if (priv->row_reference) {
671 gtk_tree_row_reference_free (priv->row_reference);
672 priv->row_reference = NULL;
675 if (priv->next_row_reference) {
676 gtk_tree_row_reference_free (priv->next_row_reference);
677 priv->next_row_reference = NULL;
681 g_free (priv->msg_uid);
682 priv->msg_uid = NULL;
685 G_OBJECT_CLASS(parent_class)->finalize (obj);
689 select_next_valid_row (GtkTreeModel *model,
690 GtkTreeRowReference **row_reference,
694 GtkTreeIter tmp_iter;
696 GtkTreePath *next = NULL;
697 gboolean retval = FALSE, finished;
699 g_return_val_if_fail (gtk_tree_row_reference_valid (*row_reference), FALSE);
701 path = gtk_tree_row_reference_get_path (*row_reference);
702 gtk_tree_model_get_iter (model, &tmp_iter, path);
703 gtk_tree_row_reference_free (*row_reference);
704 *row_reference = NULL;
708 TnyHeader *header = NULL;
710 if (gtk_tree_model_iter_next (model, &tmp_iter)) {
711 gtk_tree_model_get (model, &tmp_iter,
712 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
716 if (msg_is_visible (header, is_outbox)) {
717 next = gtk_tree_model_get_path (model, &tmp_iter);
718 *row_reference = gtk_tree_row_reference_new (model, next);
719 gtk_tree_path_free (next);
723 g_object_unref (header);
726 } else if (cycle && gtk_tree_model_get_iter_first (model, &tmp_iter)) {
727 next = gtk_tree_model_get_path (model, &tmp_iter);
729 /* Ensure that we are not selecting the same */
730 if (gtk_tree_path_compare (path, next) != 0) {
731 gtk_tree_model_get (model, &tmp_iter,
732 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
735 if (msg_is_visible (header, is_outbox)) {
736 *row_reference = gtk_tree_row_reference_new (model, next);
740 g_object_unref (header);
744 /* If we ended up in the same message
745 then there is no valid next
749 gtk_tree_path_free (next);
751 /* If there are no more messages and we don't
752 want to start again in the first one then
753 there is no valid next message */
759 gtk_tree_path_free (path);
764 /* TODO: This should be in _init(), with the parameters as properties. */
766 modest_msg_view_window_construct (ModestMsgViewWindow *self,
767 const gchar *modest_account_name,
768 const gchar *mailbox,
769 const gchar *msg_uid)
772 ModestMsgViewWindowPrivate *priv = NULL;
773 ModestWindowPrivate *parent_priv = NULL;
774 ModestDimmingRulesGroup *toolbar_rules_group = NULL;
775 ModestDimmingRulesGroup *clipboard_rules_group = NULL;
777 obj = G_OBJECT (self);
778 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(obj);
779 parent_priv = MODEST_WINDOW_GET_PRIVATE(obj);
781 priv->msg_uid = g_strdup (msg_uid);
784 parent_priv->menubar = NULL;
786 toolbar_rules_group = modest_dimming_rules_group_new (MODEST_DIMMING_RULES_TOOLBAR, TRUE);
787 clipboard_rules_group = modest_dimming_rules_group_new (MODEST_DIMMING_RULES_CLIPBOARD, FALSE);
790 /* Add common dimming rules */
791 modest_dimming_rules_group_add_rules (toolbar_rules_group,
792 modest_msg_view_toolbar_dimming_entries,
793 G_N_ELEMENTS (modest_msg_view_toolbar_dimming_entries),
794 MODEST_WINDOW (self));
795 modest_dimming_rules_group_add_rules (clipboard_rules_group,
796 modest_msg_view_clipboard_dimming_entries,
797 G_N_ELEMENTS (modest_msg_view_clipboard_dimming_entries),
798 MODEST_WINDOW (self));
800 /* Insert dimming rules group for this window */
801 modest_ui_dimming_manager_insert_rules_group (parent_priv->ui_dimming_manager, toolbar_rules_group);
802 modest_ui_dimming_manager_insert_rules_group (parent_priv->ui_dimming_manager, clipboard_rules_group);
803 g_object_unref (toolbar_rules_group);
804 g_object_unref (clipboard_rules_group);
806 /* g_signal_connect (G_OBJECT(obj), "delete-event", G_CALLBACK(on_delete_event), obj); */
808 priv->clipboard_change_handler = g_signal_connect (G_OBJECT (gtk_clipboard_get (GDK_SELECTION_PRIMARY)), "owner-change", G_CALLBACK (modest_msg_view_window_clipboard_owner_change), obj);
809 g_signal_connect (G_OBJECT(priv->msg_view), "activate_link",
810 G_CALLBACK (modest_ui_actions_on_msg_link_clicked), obj);
811 g_signal_connect (G_OBJECT(priv->msg_view), "link_hover",
812 G_CALLBACK (modest_ui_actions_on_msg_link_hover), obj);
813 g_signal_connect (G_OBJECT(priv->msg_view), "attachment_clicked",
814 G_CALLBACK (modest_ui_actions_on_msg_attachment_clicked), obj);
815 g_signal_connect (G_OBJECT(priv->msg_view), "recpt_activated",
816 G_CALLBACK (modest_ui_actions_on_msg_recpt_activated), obj);
817 g_signal_connect (G_OBJECT(priv->msg_view), "show_details",
818 G_CALLBACK (modest_ui_actions_on_details), obj);
819 g_signal_connect (G_OBJECT(priv->msg_view), "link_contextual",
820 G_CALLBACK (modest_ui_actions_on_msg_link_contextual), obj);
821 g_signal_connect (G_OBJECT(priv->msg_view), "limit_error",
822 G_CALLBACK (modest_ui_actions_on_limit_error), obj);
823 g_signal_connect (G_OBJECT (priv->msg_view), "fetch_image",
824 G_CALLBACK (on_fetch_image), obj);
826 g_signal_connect (G_OBJECT (obj), "key-release-event",
827 G_CALLBACK (modest_msg_view_window_key_event),
830 g_signal_connect (G_OBJECT (obj), "key-press-event",
831 G_CALLBACK (modest_msg_view_window_key_event),
834 g_signal_connect (G_OBJECT (obj), "move-focus",
835 G_CALLBACK (on_move_focus), obj);
837 g_signal_connect (G_OBJECT (obj), "map-event",
838 G_CALLBACK (_modest_msg_view_window_map_event),
841 /* Mail Operation Queue */
842 priv->queue_change_handler = g_signal_connect (G_OBJECT (modest_runtime_get_mail_operation_queue ()),
844 G_CALLBACK (on_queue_changed),
847 /* Account manager */
848 priv->account_removed_handler = g_signal_connect (G_OBJECT (modest_runtime_get_account_store ()),
850 G_CALLBACK(on_account_removed),
853 modest_window_set_active_account (MODEST_WINDOW(obj), modest_account_name);
854 modest_window_set_active_mailbox (MODEST_WINDOW(obj), mailbox);
856 /* First add out toolbar ... */
857 modest_msg_view_window_show_toolbar (MODEST_WINDOW (obj), TRUE);
859 priv->isearch_toolbar = modest_toolkit_factory_create_isearch_toolbar (modest_runtime_get_toolkit_factory (),
861 modest_window_add_toolbar (MODEST_WINDOW (obj), GTK_TOOLBAR (priv->isearch_toolbar));
862 gtk_widget_set_no_show_all (priv->isearch_toolbar, TRUE);
863 g_signal_connect (G_OBJECT (priv->isearch_toolbar), "isearch-close",
864 G_CALLBACK (modest_msg_view_window_isearch_toolbar_close), obj);
865 g_signal_connect (G_OBJECT (priv->isearch_toolbar), "isearch-search",
866 G_CALLBACK (modest_msg_view_window_isearch_toolbar_search), obj);
867 priv->last_search = NULL;
869 /* Init the clipboard actions dim status */
870 modest_msg_view_grab_focus(MODEST_MSG_VIEW (priv->msg_view));
872 update_window_title (MODEST_MSG_VIEW_WINDOW (obj));
877 /* FIXME: parameter checks */
879 modest_msg_view_window_new_with_header_model (TnyMsg *msg,
880 const gchar *modest_account_name,
881 const gchar *mailbox,
882 const gchar *msg_uid,
884 GtkTreeRowReference *row_reference)
886 ModestMsgViewWindow *window = NULL;
887 ModestMsgViewWindowPrivate *priv = NULL;
888 TnyFolder *header_folder = NULL;
889 ModestHeaderView *header_view = NULL;
890 ModestWindowMgr *mgr = NULL;
893 modest_tny_mime_part_to_string (TNY_MIME_PART (msg), 0);
896 mgr = modest_runtime_get_window_mgr ();
897 window = MODEST_MSG_VIEW_WINDOW (modest_window_mgr_get_msg_view_window (mgr));
898 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), NULL);
900 modest_msg_view_window_construct (window, modest_account_name, mailbox, msg_uid);
902 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
904 /* Remember the message list's TreeModel so we can detect changes
905 * and change the list selection when necessary: */
906 header_folder = modest_header_view_get_folder (header_view);
908 priv->is_outbox = (modest_tny_folder_guess_folder_type (header_folder) ==
909 TNY_FOLDER_TYPE_OUTBOX);
910 priv->header_folder_id = tny_folder_get_id (header_folder);
911 g_object_unref(header_folder);
914 /* Setup row references and connect signals */
915 priv->header_model = g_object_ref (model);
917 if (row_reference && gtk_tree_row_reference_valid (row_reference)) {
918 priv->row_reference = gtk_tree_row_reference_copy (row_reference);
919 priv->next_row_reference = gtk_tree_row_reference_copy (row_reference);
920 select_next_valid_row (model, &(priv->next_row_reference), TRUE, priv->is_outbox);
922 priv->row_reference = NULL;
923 priv->next_row_reference = NULL;
926 /* Connect signals */
927 priv->row_changed_handler =
928 g_signal_connect (GTK_TREE_MODEL(model), "row-changed",
929 G_CALLBACK(modest_msg_view_window_on_row_changed),
931 priv->row_deleted_handler =
932 g_signal_connect (GTK_TREE_MODEL(model), "row-deleted",
933 G_CALLBACK(modest_msg_view_window_on_row_deleted),
935 priv->row_inserted_handler =
936 g_signal_connect (GTK_TREE_MODEL(model), "row-inserted",
937 G_CALLBACK(modest_msg_view_window_on_row_inserted),
939 priv->rows_reordered_handler =
940 g_signal_connect(GTK_TREE_MODEL(model), "rows-reordered",
941 G_CALLBACK(modest_msg_view_window_on_row_reordered),
944 if (header_view != NULL){
945 modest_header_view_add_observer(header_view,
946 MODEST_HEADER_VIEW_OBSERVER(window));
949 tny_msg_view_set_msg (TNY_MSG_VIEW (priv->msg_view), msg);
950 update_window_title (MODEST_MSG_VIEW_WINDOW (window));
951 update_branding (MODEST_MSG_VIEW_WINDOW (window));
953 /* gtk_widget_show_all (GTK_WIDGET (window)); */
954 modest_msg_view_window_update_priority (window);
955 /* Check dimming rules */
956 modest_ui_actions_check_toolbar_dimming_rules (MODEST_WINDOW (window));
957 modest_ui_actions_check_menu_dimming_rules (MODEST_WINDOW (window));
958 modest_window_check_dimming_rules_group (MODEST_WINDOW (window), MODEST_DIMMING_RULES_CLIPBOARD);
960 return MODEST_WINDOW(window);
964 modest_msg_view_window_new_from_uid (const gchar *modest_account_name,
965 const gchar *mailbox,
966 const gchar *msg_uid)
968 ModestMsgViewWindow *window = NULL;
969 ModestMsgViewWindowPrivate *priv = NULL;
970 ModestWindowMgr *mgr = NULL;
972 TnyAccount *account = NULL;
974 mgr = modest_runtime_get_window_mgr ();
975 window = MODEST_MSG_VIEW_WINDOW (modest_window_mgr_get_msg_view_window (mgr));
976 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), NULL);
978 modest_msg_view_window_construct (window, modest_account_name, mailbox, msg_uid);
980 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
982 is_merge = g_str_has_prefix (msg_uid, "merge:");
984 /* Get the account */
986 account = tny_account_store_find_account (TNY_ACCOUNT_STORE (modest_runtime_get_account_store ()),
989 if (is_merge || account) {
990 TnyFolder *folder = NULL;
992 /* Try to get the message, if it's already downloaded
993 we don't need to connect */
995 folder = modest_tny_folder_store_find_folder_from_uri (TNY_FOLDER_STORE (account), msg_uid);
997 ModestTnyAccountStore *account_store;
998 ModestTnyLocalFoldersAccount *local_folders_account;
1000 account_store = modest_runtime_get_account_store ();
1001 local_folders_account = MODEST_TNY_LOCAL_FOLDERS_ACCOUNT (
1002 modest_tny_account_store_get_local_folders_account (account_store));
1003 folder = modest_tny_local_folders_account_get_merged_outbox (local_folders_account);
1004 g_object_unref (local_folders_account);
1008 gboolean device_online;
1010 device = modest_runtime_get_device();
1011 device_online = tny_device_is_online (device);
1012 if (device_online) {
1013 message_reader (window, priv, NULL, msg_uid, folder, NULL);
1015 TnyMsg *msg = tny_folder_find_msg (folder, msg_uid, NULL);
1017 tny_msg_view_set_msg (TNY_MSG_VIEW (priv->msg_view), msg);
1018 update_window_title (MODEST_MSG_VIEW_WINDOW (window));
1019 update_branding (MODEST_MSG_VIEW_WINDOW (window));
1020 g_object_unref (msg);
1021 /* Sync flags to server */
1022 sync_flags (MODEST_MSG_VIEW_WINDOW (window));
1024 message_reader (window, priv, NULL, msg_uid, folder, NULL);
1027 g_object_unref (folder);
1032 /* Check dimming rules */
1033 modest_ui_actions_check_toolbar_dimming_rules (MODEST_WINDOW (window));
1034 modest_ui_actions_check_menu_dimming_rules (MODEST_WINDOW (window));
1035 modest_window_check_dimming_rules_group (MODEST_WINDOW (window), MODEST_DIMMING_RULES_CLIPBOARD);
1037 return MODEST_WINDOW(window);
1041 modest_msg_view_window_new_from_header_view (ModestHeaderView *header_view,
1042 const gchar *modest_account_name,
1043 const gchar *mailbox,
1044 const gchar *msg_uid,
1045 GtkTreeRowReference *row_reference)
1047 ModestMsgViewWindow *window = NULL;
1048 ModestMsgViewWindowPrivate *priv = NULL;
1049 TnyFolder *header_folder = NULL;
1050 ModestWindowMgr *mgr = NULL;
1054 mgr = modest_runtime_get_window_mgr ();
1055 window = MODEST_MSG_VIEW_WINDOW (modest_window_mgr_get_msg_view_window (mgr));
1056 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), NULL);
1058 modest_msg_view_window_construct (window, modest_account_name, mailbox, msg_uid);
1060 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1062 /* Remember the message list's TreeModel so we can detect changes
1063 * and change the list selection when necessary: */
1065 if (header_view != NULL){
1066 header_folder = modest_header_view_get_folder(header_view);
1067 /* This could happen if the header folder was
1068 unseleted before opening this msg window (for
1069 example if the user selects an account in the
1070 folder view of the main window */
1071 if (header_folder) {
1072 priv->is_outbox = (modest_tny_folder_guess_folder_type (header_folder) ==
1073 TNY_FOLDER_TYPE_OUTBOX);
1074 priv->header_folder_id = tny_folder_get_id(header_folder);
1075 g_object_unref(header_folder);
1079 /* Setup row references and connect signals */
1080 priv->header_model = gtk_tree_view_get_model (GTK_TREE_VIEW (header_view));
1081 g_object_ref (priv->header_model);
1083 if (row_reference && gtk_tree_row_reference_valid (row_reference)) {
1084 priv->row_reference = gtk_tree_row_reference_copy (row_reference);
1085 priv->next_row_reference = gtk_tree_row_reference_copy (row_reference);
1086 select_next_valid_row (priv->header_model, &(priv->next_row_reference), TRUE, priv->is_outbox);
1088 priv->row_reference = NULL;
1089 priv->next_row_reference = NULL;
1092 /* Connect signals */
1093 priv->row_changed_handler =
1094 g_signal_connect (GTK_TREE_MODEL(priv->header_model), "row-changed",
1095 G_CALLBACK(modest_msg_view_window_on_row_changed),
1097 priv->row_deleted_handler =
1098 g_signal_connect (GTK_TREE_MODEL(priv->header_model), "row-deleted",
1099 G_CALLBACK(modest_msg_view_window_on_row_deleted),
1101 priv->row_inserted_handler =
1102 g_signal_connect (GTK_TREE_MODEL(priv->header_model), "row-inserted",
1103 G_CALLBACK(modest_msg_view_window_on_row_inserted),
1105 priv->rows_reordered_handler =
1106 g_signal_connect(GTK_TREE_MODEL(priv->header_model), "rows-reordered",
1107 G_CALLBACK(modest_msg_view_window_on_row_reordered),
1110 if (header_view != NULL){
1111 modest_header_view_add_observer(header_view,
1112 MODEST_HEADER_VIEW_OBSERVER(window));
1115 tny_msg_view_set_msg (TNY_MSG_VIEW (priv->msg_view), NULL);
1116 update_branding (MODEST_MSG_VIEW_WINDOW (window));
1118 if (priv->row_reference) {
1119 path = gtk_tree_row_reference_get_path (priv->row_reference);
1120 if (gtk_tree_model_get_iter (priv->header_model, &iter, path)) {
1122 gtk_tree_model_get (priv->header_model, &iter,
1123 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
1125 message_reader (window, priv, header, NULL, NULL, priv->row_reference);
1126 g_object_unref (header);
1128 gtk_tree_path_free (path);
1130 /* Check dimming rules */
1131 modest_ui_actions_check_toolbar_dimming_rules (MODEST_WINDOW (window));
1132 modest_ui_actions_check_menu_dimming_rules (MODEST_WINDOW (window));
1133 modest_window_check_dimming_rules_group (MODEST_WINDOW (window), MODEST_DIMMING_RULES_CLIPBOARD);
1135 return MODEST_WINDOW(window);
1139 modest_msg_view_window_new_for_search_result (TnyMsg *msg,
1140 const gchar *modest_account_name,
1141 const gchar *mailbox,
1142 const gchar *msg_uid)
1144 ModestMsgViewWindow *window = NULL;
1145 ModestMsgViewWindowPrivate *priv = NULL;
1146 ModestWindowMgr *mgr = NULL;
1148 mgr = modest_runtime_get_window_mgr ();
1149 window = MODEST_MSG_VIEW_WINDOW (modest_window_mgr_get_msg_view_window (mgr));
1150 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), NULL);
1151 modest_msg_view_window_construct (window, modest_account_name, mailbox, msg_uid);
1153 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1155 /* Remember that this is a search result,
1156 * so we can disable some UI appropriately: */
1157 priv->is_search_result = TRUE;
1159 tny_msg_view_set_msg (TNY_MSG_VIEW (priv->msg_view), msg);
1160 update_branding (MODEST_MSG_VIEW_WINDOW (window));
1162 update_window_title (window);
1163 /* gtk_widget_show_all (GTK_WIDGET (window));*/
1164 modest_msg_view_window_update_priority (window);
1166 /* Check dimming rules */
1167 modest_ui_actions_check_toolbar_dimming_rules (MODEST_WINDOW (window));
1168 modest_ui_actions_check_menu_dimming_rules (MODEST_WINDOW (window));
1169 modest_window_check_dimming_rules_group (MODEST_WINDOW (window), MODEST_DIMMING_RULES_CLIPBOARD);
1171 return MODEST_WINDOW(window);
1175 modest_msg_view_window_is_other_body (ModestMsgViewWindow *self)
1177 ModestMsgViewWindowPrivate *priv = NULL;
1179 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self), FALSE);
1180 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
1182 return (priv->other_body != NULL);
1186 modest_msg_view_window_new_with_other_body (TnyMsg *msg,
1187 TnyMimePart *other_body,
1188 const gchar *modest_account_name,
1189 const gchar *mailbox,
1190 const gchar *msg_uid)
1192 GObject *obj = NULL;
1193 ModestMsgViewWindowPrivate *priv;
1194 ModestWindowMgr *mgr = NULL;
1196 g_return_val_if_fail (msg, NULL);
1197 mgr = modest_runtime_get_window_mgr ();
1198 obj = G_OBJECT (modest_window_mgr_get_msg_view_window (mgr));
1199 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (obj);
1200 modest_msg_view_window_construct (MODEST_MSG_VIEW_WINDOW (obj),
1201 modest_account_name, mailbox, msg_uid);
1204 priv->other_body = g_object_ref (other_body);
1205 modest_msg_view_set_msg_with_other_body (MODEST_MSG_VIEW (priv->msg_view), msg, other_body);
1207 tny_msg_view_set_msg (TNY_MSG_VIEW (priv->msg_view), msg);
1209 update_window_title (MODEST_MSG_VIEW_WINDOW (obj));
1210 update_branding (MODEST_MSG_VIEW_WINDOW (obj));
1212 /* gtk_widget_show_all (GTK_WIDGET (obj)); */
1214 /* Check dimming rules */
1215 modest_ui_actions_check_toolbar_dimming_rules (MODEST_WINDOW (obj));
1216 modest_ui_actions_check_menu_dimming_rules (MODEST_WINDOW (obj));
1217 modest_window_check_dimming_rules_group (MODEST_WINDOW (obj), MODEST_DIMMING_RULES_CLIPBOARD);
1219 return MODEST_WINDOW(obj);
1223 modest_msg_view_window_new_for_attachment (TnyMsg *msg,
1224 const gchar *modest_account_name,
1225 const gchar *mailbox,
1226 const gchar *msg_uid)
1228 return modest_msg_view_window_new_with_other_body (msg, NULL, modest_account_name, mailbox, msg_uid);
1232 modest_msg_view_window_on_row_changed (GtkTreeModel *header_model,
1235 ModestMsgViewWindow *window)
1237 check_dimming_rules_after_change (window);
1241 modest_msg_view_window_on_row_deleted(GtkTreeModel *header_model,
1243 ModestMsgViewWindow *window)
1245 check_dimming_rules_after_change (window);
1247 /* The window could have dissapeared */
1250 check_dimming_rules_after_change (ModestMsgViewWindow *window)
1252 modest_ui_actions_check_menu_dimming_rules (MODEST_WINDOW (window));
1253 modest_ui_actions_check_toolbar_dimming_rules (MODEST_WINDOW (window));
1257 /* On insertions we check if the folder still has the message we are
1258 * showing or do not. If do not, we do nothing. Which means we are still
1259 * not attached to any header folder and thus next/prev buttons are
1260 * still dimmed. Once the message that is shown by msg-view is found, the
1261 * new model of header-view will be attached and the references will be set.
1262 * On each further insertions dimming rules will be checked. However
1263 * this requires extra CPU time at least works.
1264 * (An message might be deleted from TnyFolder and thus will not be
1265 * inserted into the model again for example if it is removed by the
1266 * imap server and the header view is refreshed.)
1269 modest_msg_view_window_on_row_inserted (GtkTreeModel *model,
1270 GtkTreePath *tree_path,
1271 GtkTreeIter *tree_iter,
1272 ModestMsgViewWindow *window)
1274 ModestMsgViewWindowPrivate *priv = NULL;
1275 TnyHeader *header = NULL;
1277 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window));
1278 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1280 g_assert (model == priv->header_model);
1282 /* Check if the newly inserted message is the same we are actually
1283 * showing. IF not, we should remain detached from the header model
1284 * and thus prev and next toolbar buttons should remain dimmed. */
1285 gtk_tree_model_get (model, tree_iter,
1286 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
1289 if (TNY_IS_HEADER (header)) {
1292 uid = modest_tny_folder_get_header_unique_id (header);
1293 if (!g_str_equal(priv->msg_uid, uid)) {
1294 check_dimming_rules_after_change (window);
1296 g_object_unref (G_OBJECT(header));
1300 g_object_unref(G_OBJECT(header));
1303 if (priv->row_reference) {
1304 gtk_tree_row_reference_free (priv->row_reference);
1307 /* Setup row_reference for the actual msg. */
1308 priv->row_reference = gtk_tree_row_reference_new (priv->header_model, tree_path);
1309 if (priv->row_reference == NULL) {
1310 g_warning("%s: No reference for msg header item.", __FUNCTION__);
1314 /* Now set up next_row_reference. */
1315 if (priv->next_row_reference) {
1316 gtk_tree_row_reference_free (priv->next_row_reference);
1319 priv->next_row_reference = gtk_tree_row_reference_copy (priv->row_reference);
1320 select_next_valid_row (priv->header_model,
1321 &(priv->next_row_reference), FALSE, priv->is_outbox);
1323 /* Connect the remaining callbacks to become able to detect
1324 * changes in header-view. */
1325 priv->row_changed_handler =
1326 g_signal_connect (priv->header_model, "row-changed",
1327 G_CALLBACK (modest_msg_view_window_on_row_changed),
1329 priv->row_deleted_handler =
1330 g_signal_connect (priv->header_model, "row-deleted",
1331 G_CALLBACK (modest_msg_view_window_on_row_deleted),
1333 priv->rows_reordered_handler =
1334 g_signal_connect (priv->header_model, "rows-reordered",
1335 G_CALLBACK (modest_msg_view_window_on_row_reordered),
1338 check_dimming_rules_after_change (window);
1342 modest_msg_view_window_on_row_reordered (GtkTreeModel *header_model,
1346 ModestMsgViewWindow *window)
1348 ModestMsgViewWindowPrivate *priv = NULL;
1349 gboolean already_changed = FALSE;
1351 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(window);
1353 /* If the current row was reordered select the proper next
1354 valid row. The same if the next row reference changes */
1355 if (!priv->row_reference ||
1356 !gtk_tree_row_reference_valid (priv->row_reference))
1359 if (priv->next_row_reference &&
1360 gtk_tree_row_reference_valid (priv->next_row_reference)) {
1361 GtkTreePath *cur, *next;
1362 /* Check that the order is still the correct one */
1363 cur = gtk_tree_row_reference_get_path (priv->row_reference);
1364 next = gtk_tree_row_reference_get_path (priv->next_row_reference);
1365 gtk_tree_path_next (cur);
1366 if (gtk_tree_path_compare (cur, next) != 0) {
1367 gtk_tree_row_reference_free (priv->next_row_reference);
1368 priv->next_row_reference = gtk_tree_row_reference_copy (priv->row_reference);
1369 select_next_valid_row (header_model, &(priv->next_row_reference), FALSE, priv->is_outbox);
1370 already_changed = TRUE;
1372 gtk_tree_path_free (cur);
1373 gtk_tree_path_free (next);
1375 if (priv->next_row_reference)
1376 gtk_tree_row_reference_free (priv->next_row_reference);
1377 /* Update next row reference */
1378 priv->next_row_reference = gtk_tree_row_reference_copy (priv->row_reference);
1379 select_next_valid_row (header_model, &(priv->next_row_reference), FALSE, priv->is_outbox);
1380 already_changed = TRUE;
1383 check_dimming_rules_after_change (window);
1386 /* The modest_msg_view_window_update_model_replaced implements update
1387 * function for ModestHeaderViewObserver. Checks whether the TnyFolder
1388 * actually belongs to the header-view is the same as the TnyFolder of
1389 * the message of msg-view or not. If they are different, there is
1390 * nothing to do. If they are the same, then the model has replaced and
1391 * the reference in msg-view shall be replaced from the old model to
1392 * the new model. In this case the view will be detached from it's
1393 * header folder. From this point the next/prev buttons are dimmed.
1396 modest_msg_view_window_update_model_replaced (ModestHeaderViewObserver *observer,
1397 GtkTreeModel *model,
1398 const gchar *tny_folder_id)
1400 ModestMsgViewWindowPrivate *priv = NULL;
1401 ModestMsgViewWindow *window = NULL;
1403 g_assert(MODEST_IS_HEADER_VIEW_OBSERVER(observer));
1404 g_assert(MODEST_IS_MSG_VIEW_WINDOW(observer));
1406 window = MODEST_MSG_VIEW_WINDOW(observer);
1407 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(window);
1409 /* If there is an other folder in the header-view then we do
1410 * not care about it's model (msg list). Else if the
1411 * header-view shows the folder the msg shown by us is in, we
1412 * shall replace our model reference and make some check. */
1413 if(model == NULL || tny_folder_id == NULL ||
1414 (priv->header_folder_id && !g_str_equal(tny_folder_id, priv->header_folder_id)))
1417 /* Model is changed(replaced), so we should forget the old
1418 * one. Because there might be other references and there
1419 * might be some change on the model even if we unreferenced
1420 * it, we need to disconnect our signals here. */
1421 if (priv->header_model) {
1422 if (g_signal_handler_is_connected(G_OBJECT (priv->header_model),
1423 priv->row_changed_handler))
1424 g_signal_handler_disconnect(G_OBJECT (priv->header_model),
1425 priv->row_changed_handler);
1426 if (g_signal_handler_is_connected(G_OBJECT (priv->header_model),
1427 priv->row_deleted_handler))
1428 g_signal_handler_disconnect(G_OBJECT (priv->header_model),
1429 priv->row_deleted_handler);
1430 if (g_signal_handler_is_connected(G_OBJECT (priv->header_model),
1431 priv->row_inserted_handler))
1432 g_signal_handler_disconnect(G_OBJECT (priv->header_model),
1433 priv->row_inserted_handler);
1434 if (g_signal_handler_is_connected(G_OBJECT (priv->header_model),
1435 priv->rows_reordered_handler))
1436 g_signal_handler_disconnect(G_OBJECT (priv->header_model),
1437 priv->rows_reordered_handler);
1440 if (priv->row_reference)
1441 gtk_tree_row_reference_free (priv->row_reference);
1442 if (priv->next_row_reference)
1443 gtk_tree_row_reference_free (priv->next_row_reference);
1444 g_object_unref(priv->header_model);
1447 priv->row_changed_handler = 0;
1448 priv->row_deleted_handler = 0;
1449 priv->row_inserted_handler = 0;
1450 priv->rows_reordered_handler = 0;
1451 priv->next_row_reference = NULL;
1452 priv->row_reference = NULL;
1453 priv->header_model = NULL;
1456 priv->header_model = g_object_ref (model);
1458 /* Also we must connect to the new model for row insertions.
1459 * Only for insertions now. We will need other ones only after
1460 * the msg is show by msg-view is added to the new model. */
1461 priv->row_inserted_handler =
1462 g_signal_connect (priv->header_model, "row-inserted",
1463 G_CALLBACK(modest_msg_view_window_on_row_inserted),
1466 modest_ui_actions_check_menu_dimming_rules(MODEST_WINDOW(window));
1467 modest_ui_actions_check_toolbar_dimming_rules(MODEST_WINDOW(window));
1471 modest_msg_view_window_toolbar_on_transfer_mode (ModestMsgViewWindow *self)
1473 ModestMsgViewWindowPrivate *priv= NULL;
1475 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self), FALSE);
1476 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
1478 return priv->progress_hint;
1482 modest_msg_view_window_get_header (ModestMsgViewWindow *self)
1484 ModestMsgViewWindowPrivate *priv= NULL;
1486 TnyHeader *header = NULL;
1487 GtkTreePath *path = NULL;
1490 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self), NULL);
1491 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
1493 /* If the message was not obtained from a treemodel,
1494 * for instance if it was opened directly by the search UI:
1496 if (priv->header_model == NULL ||
1497 priv->row_reference == NULL ||
1498 !gtk_tree_row_reference_valid (priv->row_reference)) {
1499 msg = modest_msg_view_window_get_message (self);
1501 header = tny_msg_get_header (msg);
1502 g_object_unref (msg);
1507 /* Get iter of the currently selected message in the header view: */
1508 path = gtk_tree_row_reference_get_path (priv->row_reference);
1509 g_return_val_if_fail (path != NULL, NULL);
1510 gtk_tree_model_get_iter (priv->header_model,
1514 /* Get current message header */
1515 gtk_tree_model_get (priv->header_model, &iter,
1516 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
1519 gtk_tree_path_free (path);
1524 modest_msg_view_window_get_message (ModestMsgViewWindow *self)
1526 ModestMsgViewWindowPrivate *priv;
1528 g_return_val_if_fail (self, NULL);
1530 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(self);
1532 return tny_msg_view_get_msg (TNY_MSG_VIEW (priv->msg_view));
1536 modest_msg_view_window_get_message_uid (ModestMsgViewWindow *self)
1538 ModestMsgViewWindowPrivate *priv;
1540 g_return_val_if_fail (self, NULL);
1542 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
1544 return (const gchar*) priv->msg_uid;
1547 /* Used for the Ctrl+F accelerator */
1549 modest_msg_view_window_toggle_isearch_toolbar (GtkWidget *obj,
1552 ModestMsgViewWindow *window = MODEST_MSG_VIEW_WINDOW (data);
1553 ModestMsgViewWindowPrivate *priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1555 if (GTK_WIDGET_VISIBLE (priv->isearch_toolbar)) {
1556 modest_msg_view_window_isearch_toolbar_close (obj, data);
1558 modest_msg_view_window_show_isearch_toolbar (obj, data);
1562 /* Handler for menu option */
1564 modest_msg_view_window_show_isearch_toolbar (GtkWidget *obj,
1567 ModestMsgViewWindow *window = MODEST_MSG_VIEW_WINDOW (data);
1568 ModestMsgViewWindowPrivate *priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1570 gtk_widget_show (priv->isearch_toolbar);
1571 modest_isearch_toolbar_highlight_entry (MODEST_ISEARCH_TOOLBAR (priv->isearch_toolbar), TRUE);
1574 /* Handler for click on the "X" close button in isearch toolbar */
1576 modest_msg_view_window_isearch_toolbar_close (GtkWidget *widget,
1577 ModestMsgViewWindow *obj)
1579 ModestMsgViewWindowPrivate *priv;
1581 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (obj);
1584 gtk_widget_hide (priv->isearch_toolbar);
1585 modest_msg_view_grab_focus (MODEST_MSG_VIEW (priv->msg_view));
1589 modest_msg_view_window_isearch_toolbar_search (GtkWidget *widget,
1590 ModestMsgViewWindow *obj)
1592 const gchar *current_search;
1593 ModestMsgViewWindowPrivate *priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (obj);
1595 if (modest_mime_part_view_is_empty (MODEST_MIME_PART_VIEW (priv->msg_view))) {
1596 modest_platform_system_banner (NULL, NULL, _("mail_ib_nothing_to_find"));
1600 current_search = modest_isearch_toolbar_get_search (MODEST_ISEARCH_TOOLBAR (widget));
1602 if ((current_search == NULL) || (strcmp (current_search, "") == 0)) {
1603 modest_platform_system_banner (NULL, NULL, _CS_FIND_REP_ENTER_TEXT);
1607 if ((priv->last_search == NULL) || (strcmp (priv->last_search, current_search) != 0)) {
1609 g_free (priv->last_search);
1610 priv->last_search = g_strdup (current_search);
1611 result = modest_isearch_view_search (MODEST_ISEARCH_VIEW (priv->msg_view),
1614 modest_platform_system_banner (NULL, NULL,
1615 _HL_IB_FIND_NO_MATCHES);
1616 g_free (priv->last_search);
1617 priv->last_search = NULL;
1619 modest_isearch_toolbar_highlight_entry (MODEST_ISEARCH_TOOLBAR (priv->isearch_toolbar), TRUE);
1622 if (!modest_isearch_view_search_next (MODEST_ISEARCH_VIEW (priv->msg_view))) {
1623 modest_platform_system_banner (NULL, NULL,
1624 _HL_IB_FIND_COMPLETE);
1625 g_free (priv->last_search);
1626 priv->last_search = NULL;
1628 modest_isearch_toolbar_highlight_entry (MODEST_ISEARCH_TOOLBAR (priv->isearch_toolbar), TRUE);
1635 modest_msg_view_window_set_zoom (ModestWindow *window,
1638 ModestMsgViewWindowPrivate *priv;
1639 ModestWindowPrivate *parent_priv;
1641 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window));
1643 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1644 parent_priv = MODEST_WINDOW_GET_PRIVATE (window);
1645 modest_zoomable_set_zoom (MODEST_ZOOMABLE (priv->msg_view), zoom);
1650 modest_msg_view_window_get_zoom (ModestWindow *window)
1652 ModestMsgViewWindowPrivate *priv;
1654 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), 1.0);
1656 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1657 return modest_zoomable_get_zoom (MODEST_ZOOMABLE (priv->msg_view));
1661 modest_msg_view_window_zoom_plus (ModestWindow *window)
1664 ModestMsgViewWindowPrivate *priv;
1668 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), 1.0);
1669 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1671 zoom_level = modest_zoomable_get_zoom (MODEST_ZOOMABLE (priv->msg_view));
1673 if (zoom_level >= 2.0) {
1674 modest_platform_system_banner (NULL, NULL,
1675 _CS_MAX_ZOOM_LEVEL_REACHED);
1677 } else if (zoom_level >= 1.5) {
1679 } else if (zoom_level >= 1.2) {
1681 } else if (zoom_level >= 1.0) {
1683 } else if (zoom_level >= 0.8) {
1685 } else if (zoom_level >= 0.5) {
1691 /* set zoom level */
1692 int_zoom = (gint) rint (zoom_level*100.0+0.1);
1693 banner_text = g_strdup_printf (_HL_IB_ZOOM, int_zoom);
1694 modest_platform_information_banner (GTK_WIDGET (window), NULL, banner_text);
1695 g_free (banner_text);
1696 modest_zoomable_set_zoom (MODEST_ZOOMABLE (priv->msg_view), zoom_level);
1702 modest_msg_view_window_zoom_minus (ModestWindow *window)
1705 ModestMsgViewWindowPrivate *priv;
1709 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), 1.0);
1710 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1712 zoom_level = modest_zoomable_get_zoom (MODEST_ZOOMABLE (priv->msg_view));
1714 if (zoom_level <= 0.5) {
1715 modest_platform_system_banner (NULL, NULL,
1716 _CS_MIN_ZOOM_LEVEL_REACHED);
1718 } else if (zoom_level <= 0.8) {
1720 } else if (zoom_level <= 1.0) {
1722 } else if (zoom_level <= 1.2) {
1724 } else if (zoom_level <= 1.5) {
1726 } else if (zoom_level <= 2.0) {
1732 /* set zoom level */
1733 int_zoom = (gint) rint (zoom_level*100.0+0.1);
1734 banner_text = g_strdup_printf (_HL_IB_ZOOM, int_zoom);
1735 modest_platform_information_banner (GTK_WIDGET (window), NULL, banner_text);
1736 g_free (banner_text);
1737 modest_zoomable_set_zoom (MODEST_ZOOMABLE (priv->msg_view), zoom_level);
1743 modest_msg_view_window_key_event (GtkWidget *window,
1749 focus = gtk_container_get_focus_child ((GtkContainer *) window);
1751 /* for the isearch toolbar case */
1752 if (focus && GTK_IS_ENTRY (focus)) {
1753 if (event->keyval == GDK_BackSpace) {
1755 copy = gdk_event_copy ((GdkEvent *) event);
1756 gtk_widget_event (focus, copy);
1757 gdk_event_free (copy);
1767 modest_msg_view_window_last_message_selected (ModestMsgViewWindow *window)
1770 ModestMsgViewWindowPrivate *priv;
1771 GtkTreeIter tmp_iter;
1772 gboolean is_last_selected;
1774 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), TRUE);
1775 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1777 /*if no model (so no rows at all), then virtually we are the last*/
1778 if (!priv->header_model || !priv->row_reference)
1781 if (!gtk_tree_row_reference_valid (priv->row_reference))
1784 path = gtk_tree_row_reference_get_path (priv->row_reference);
1788 is_last_selected = TRUE;
1789 while (is_last_selected) {
1791 gtk_tree_path_next (path);
1792 if (!gtk_tree_model_get_iter (priv->header_model, &tmp_iter, path))
1794 gtk_tree_model_get (priv->header_model, &tmp_iter,
1795 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
1798 if (msg_is_visible (header, priv->is_outbox))
1799 is_last_selected = FALSE;
1800 g_object_unref(G_OBJECT(header));
1803 gtk_tree_path_free (path);
1804 return is_last_selected;
1808 modest_msg_view_window_has_headers_model (ModestMsgViewWindow *window)
1810 ModestMsgViewWindowPrivate *priv;
1812 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), TRUE);
1813 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1815 return priv->header_model != NULL;
1819 modest_msg_view_window_is_search_result (ModestMsgViewWindow *window)
1821 ModestMsgViewWindowPrivate *priv;
1823 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), TRUE);
1824 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1826 return priv->is_search_result;
1830 msg_is_visible (TnyHeader *header, gboolean check_outbox)
1832 if ((tny_header_get_flags(header) & TNY_HEADER_FLAG_DELETED))
1834 if (!check_outbox) {
1837 ModestTnySendQueueStatus status;
1838 status = modest_tny_all_send_queues_get_msg_status (header);
1839 return ((status != MODEST_TNY_SEND_QUEUE_FAILED) &&
1840 (status != MODEST_TNY_SEND_QUEUE_SENDING));
1845 modest_msg_view_window_first_message_selected (ModestMsgViewWindow *window)
1848 ModestMsgViewWindowPrivate *priv;
1849 gboolean is_first_selected;
1850 GtkTreeIter tmp_iter;
1852 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), TRUE);
1853 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1855 /*if no model (so no rows at all), then virtually we are the first*/
1856 if (!priv->header_model || !priv->row_reference)
1859 if (!gtk_tree_row_reference_valid (priv->row_reference))
1862 path = gtk_tree_row_reference_get_path (priv->row_reference);
1866 is_first_selected = TRUE;
1867 while (is_first_selected) {
1869 if(!gtk_tree_path_prev (path))
1871 /* Here the 'if' is needless for logic, but let make sure
1872 * iter is valid for gtk_tree_model_get. */
1873 if (!gtk_tree_model_get_iter (priv->header_model, &tmp_iter, path))
1875 gtk_tree_model_get (priv->header_model, &tmp_iter,
1876 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
1879 if (msg_is_visible (header, priv->is_outbox))
1880 is_first_selected = FALSE;
1881 g_object_unref(G_OBJECT(header));
1884 gtk_tree_path_free (path);
1885 return is_first_selected;
1892 GtkTreeRowReference *row_reference;
1896 message_reader_performer (gboolean canceled,
1898 ModestWindow *parent_window,
1899 TnyAccount *account,
1902 ModestMailOperation *mail_op = NULL;
1903 MsgReaderInfo *info;
1905 info = (MsgReaderInfo *) user_data;
1906 if (canceled || err) {
1907 update_window_title (MODEST_MSG_VIEW_WINDOW (parent_window));
1908 modest_ui_actions_on_close_window (NULL, MODEST_WINDOW (parent_window));
1912 /* Register the header - it'll be unregistered in the callback */
1914 modest_window_mgr_register_header (modest_runtime_get_window_mgr (), info->header, NULL);
1916 /* New mail operation */
1917 mail_op = modest_mail_operation_new_with_error_handling (G_OBJECT(parent_window),
1918 modest_ui_actions_disk_operations_error_handler,
1921 modest_mail_operation_queue_add (modest_runtime_get_mail_operation_queue (), mail_op);
1923 modest_mail_operation_get_msg (mail_op, info->header, TRUE, view_msg_cb, info->row_reference);
1925 modest_mail_operation_find_msg (mail_op, info->folder, info->msg_uid, TRUE, view_msg_cb, NULL);
1926 g_object_unref (mail_op);
1928 /* Update dimming rules */
1929 modest_ui_actions_check_toolbar_dimming_rules (MODEST_WINDOW (parent_window));
1930 modest_ui_actions_check_menu_dimming_rules (MODEST_WINDOW (parent_window));
1933 /* Frees. The row_reference will be freed by the view_msg_cb callback */
1934 g_free (info->msg_uid);
1936 g_object_unref (info->folder);
1938 g_object_unref (info->header);
1939 g_slice_free (MsgReaderInfo, info);
1944 * Reads the message whose summary item is @header. It takes care of
1945 * several things, among others:
1947 * If the message was not previously downloaded then ask the user
1948 * before downloading. If there is no connection launch the connection
1949 * dialog. Update toolbar dimming rules.
1951 * Returns: TRUE if the mail operation was started, otherwise if the
1952 * user do not want to download the message, or if the user do not
1953 * want to connect, then the operation is not issued
1956 message_reader (ModestMsgViewWindow *window,
1957 ModestMsgViewWindowPrivate *priv,
1959 const gchar *msg_uid,
1961 GtkTreeRowReference *row_reference)
1963 ModestWindowMgr *mgr;
1964 TnyAccount *account = NULL;
1965 MsgReaderInfo *info;
1967 /* We set the header from model while we're loading */
1968 tny_header_view_set_header (TNY_HEADER_VIEW (priv->msg_view), header);
1969 modest_window_set_title (MODEST_WINDOW (window), _CS_UPDATING);
1975 g_object_ref (folder);
1977 mgr = modest_runtime_get_window_mgr ();
1978 /* Msg download completed */
1979 if (!header || !(tny_header_get_flags (header) & TNY_HEADER_FLAG_CACHED)) {
1981 /* Ask the user if he wants to download the message if
1983 if (!tny_device_is_online (modest_runtime_get_device())) {
1984 GtkResponseType response;
1985 GtkWindow *toplevel;
1987 toplevel = (GtkWindow *) gtk_widget_get_toplevel ((GtkWidget *) window);
1988 response = modest_platform_run_confirmation_dialog (toplevel, _("mcen_nc_get_msg"));
1989 if (response == GTK_RESPONSE_CANCEL) {
1990 update_window_title (window);
1995 folder = tny_header_get_folder (header);
1997 info = g_slice_new (MsgReaderInfo);
1998 info->msg_uid = g_strdup (msg_uid);
2000 info->header = g_object_ref (header);
2002 info->header = NULL;
2004 info->folder = g_object_ref (folder);
2006 info->folder = NULL;
2007 if (row_reference) {
2008 info->row_reference = gtk_tree_row_reference_copy (row_reference);
2010 info->row_reference = NULL;
2013 /* Offer the connection dialog if necessary */
2014 modest_platform_connect_if_remote_and_perform ((ModestWindow *) window,
2016 TNY_FOLDER_STORE (folder),
2017 message_reader_performer,
2020 g_object_unref (folder);
2026 folder = tny_header_get_folder (header);
2029 account = tny_folder_get_account (folder);
2031 info = g_slice_new (MsgReaderInfo);
2032 info->msg_uid = g_strdup (msg_uid);
2034 info->folder = g_object_ref (folder);
2036 info->folder = NULL;
2038 info->header = g_object_ref (header);
2040 info->header = NULL;
2042 info->row_reference = gtk_tree_row_reference_copy (row_reference);
2044 info->row_reference = NULL;
2046 message_reader_performer (FALSE, NULL, (ModestWindow *) window, account, info);
2048 g_object_unref (account);
2050 g_object_unref (folder);
2056 modest_msg_view_window_select_next_message (ModestMsgViewWindow *window)
2058 ModestMsgViewWindowPrivate *priv;
2059 GtkTreePath *path= NULL;
2060 GtkTreeIter tmp_iter;
2062 gboolean retval = TRUE;
2063 GtkTreeRowReference *row_reference = NULL;
2065 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), FALSE);
2066 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
2068 if (!priv->row_reference)
2071 /* Update the next row reference if it's not valid. This could
2072 happen if for example the header which it was pointing to,
2073 was deleted. The best place to do it is in the row-deleted
2074 handler but the tinymail model do not work like the glib
2075 tree models and reports the deletion when the row is still
2077 if (!gtk_tree_row_reference_valid (priv->next_row_reference)) {
2078 if (priv->next_row_reference) {
2079 gtk_tree_row_reference_free (priv->next_row_reference);
2081 if (gtk_tree_row_reference_valid (priv->row_reference)) {
2082 priv->next_row_reference = gtk_tree_row_reference_copy (priv->row_reference);
2083 select_next_valid_row (priv->header_model, &(priv->next_row_reference), FALSE, priv->is_outbox);
2085 priv->next_row_reference = NULL;
2088 if (priv->next_row_reference)
2089 path = gtk_tree_row_reference_get_path (priv->next_row_reference);
2093 row_reference = gtk_tree_row_reference_copy (priv->next_row_reference);
2095 gtk_tree_model_get_iter (priv->header_model,
2098 gtk_tree_path_free (path);
2100 gtk_tree_model_get (priv->header_model, &tmp_iter,
2101 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
2104 /* Read the message & show it */
2105 if (!message_reader (window, priv, header, NULL, NULL, row_reference)) {
2108 gtk_tree_row_reference_free (row_reference);
2111 g_object_unref (header);
2117 modest_msg_view_window_select_previous_message (ModestMsgViewWindow *window)
2119 ModestMsgViewWindowPrivate *priv = NULL;
2121 gboolean finished = FALSE;
2122 gboolean retval = FALSE;
2124 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), FALSE);
2125 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
2127 if (priv->row_reference && !gtk_tree_row_reference_valid (priv->row_reference)) {
2128 gtk_tree_row_reference_free (priv->row_reference);
2129 priv->row_reference = NULL;
2132 /* Return inmediatly if there is no header model */
2133 if (!priv->header_model || !priv->row_reference)
2136 path = gtk_tree_row_reference_get_path (priv->row_reference);
2137 while (!finished && gtk_tree_path_prev (path)) {
2141 gtk_tree_model_get_iter (priv->header_model, &iter, path);
2142 gtk_tree_model_get (priv->header_model, &iter,
2143 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
2147 if (msg_is_visible (header, priv->is_outbox)) {
2148 GtkTreeRowReference *row_reference;
2149 row_reference = gtk_tree_row_reference_new (priv->header_model, path);
2150 /* Read the message & show it */
2151 retval = message_reader (window, priv, header, NULL, NULL, row_reference);
2152 gtk_tree_row_reference_free (row_reference);
2156 g_object_unref (header);
2160 gtk_tree_path_free (path);
2165 view_msg_cb (ModestMailOperation *mail_op,
2172 ModestMsgViewWindow *self = NULL;
2173 ModestMsgViewWindowPrivate *priv = NULL;
2174 GtkTreeRowReference *row_reference = NULL;
2176 /* Unregister the header (it was registered before creating the mail operation) */
2177 modest_window_mgr_unregister_header (modest_runtime_get_window_mgr (), header);
2179 row_reference = (GtkTreeRowReference *) user_data;
2182 gtk_tree_row_reference_free (row_reference);
2183 self = (ModestMsgViewWindow *) modest_mail_operation_get_source (mail_op);
2185 /* Restore window title */
2186 update_window_title (self);
2187 modest_ui_actions_on_close_window (NULL, MODEST_WINDOW (self));
2188 g_object_unref (self);
2193 /* If there was any error */
2194 if (!modest_ui_actions_msg_retrieval_check (mail_op, header, msg)) {
2196 gtk_tree_row_reference_free (row_reference);
2197 self = (ModestMsgViewWindow *) modest_mail_operation_get_source (mail_op);
2199 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
2200 /* First we check if the parent is a folder window */
2201 if (priv->msg_uid && !modest_window_mgr_get_folder_window (MODEST_WINDOW_MGR (modest_runtime_get_window_mgr ()))) {
2203 TnyAccount *account = NULL;
2204 GtkWidget *header_window = NULL;
2206 is_merge = g_str_has_prefix (priv->msg_uid, "merge:");
2208 /* Get the account */
2210 account = tny_account_store_find_account (TNY_ACCOUNT_STORE (modest_runtime_get_account_store ()),
2213 if (is_merge || account) {
2214 TnyFolder *folder = NULL;
2216 /* Try to get the message, if it's already downloaded
2217 we don't need to connect */
2219 folder = modest_tny_folder_store_find_folder_from_uri (TNY_FOLDER_STORE (account),
2222 ModestTnyAccountStore *account_store;
2223 ModestTnyLocalFoldersAccount *local_folders_account;
2225 account_store = modest_runtime_get_account_store ();
2226 local_folders_account = MODEST_TNY_LOCAL_FOLDERS_ACCOUNT (
2227 modest_tny_account_store_get_local_folders_account (account_store));
2228 folder = modest_tny_local_folders_account_get_merged_outbox (local_folders_account);
2229 g_object_unref (local_folders_account);
2231 if (account) g_object_unref (account);
2234 header_window = (GtkWidget *)
2235 modest_header_window_new (
2237 modest_window_get_active_account (MODEST_WINDOW (self)),
2238 modest_window_get_active_mailbox (MODEST_WINDOW (self)));
2239 if (!modest_window_mgr_register_window (modest_runtime_get_window_mgr (),
2240 MODEST_WINDOW (header_window),
2242 gtk_widget_destroy (GTK_WIDGET (header_window));
2244 gtk_widget_show_all (GTK_WIDGET (header_window));
2246 g_object_unref (folder);
2252 /* Restore window title */
2253 update_window_title (self);
2254 modest_ui_actions_on_close_window (NULL, MODEST_WINDOW (self));
2255 g_object_unref (self);
2260 if (msg && TNY_IS_CAMEL_BS_MSG (msg)) {
2262 body = modest_tny_msg_find_body_part (msg, TRUE);
2264 if (body && !tny_camel_bs_mime_part_is_fetched (TNY_CAMEL_BS_MIME_PART (body))) {
2265 /* We have body structure but not the body mime part. What we do
2266 * is restarting load of message */
2267 self = (ModestMsgViewWindow *) modest_mail_operation_get_source (mail_op);
2268 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
2270 tny_header_unset_flag (TNY_HEADER (header), TNY_HEADER_FLAG_CACHED);
2272 modest_msg_view_window_reload (self);
2275 gtk_tree_row_reference_free (row_reference);
2276 g_object_unref (body);
2281 g_object_unref (body);
2284 /* Get the window */
2285 self = (ModestMsgViewWindow *) modest_mail_operation_get_source (mail_op);
2286 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self));
2287 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
2289 /* Update the row reference */
2290 if (priv->row_reference != NULL) {
2291 gtk_tree_row_reference_free (priv->row_reference);
2292 priv->row_reference = (row_reference && gtk_tree_row_reference_valid (row_reference))?gtk_tree_row_reference_copy (row_reference):NULL;
2293 if (priv->next_row_reference != NULL) {
2294 gtk_tree_row_reference_free (priv->next_row_reference);
2296 if (priv->row_reference) {
2297 priv->next_row_reference = gtk_tree_row_reference_copy (priv->row_reference);
2298 select_next_valid_row (priv->header_model, &(priv->next_row_reference), TRUE, priv->is_outbox);
2300 priv->next_row_reference = NULL;
2304 /* Mark header as read */
2305 if (!(tny_header_get_flags (header) & TNY_HEADER_FLAG_SEEN))
2306 tny_header_set_flag (header, TNY_HEADER_FLAG_SEEN);
2308 /* Set new message */
2309 if (priv->msg_view != NULL && TNY_IS_MSG_VIEW (priv->msg_view)) {
2310 tny_msg_view_set_msg (TNY_MSG_VIEW (priv->msg_view), msg);
2311 modest_msg_view_window_update_priority (self);
2312 update_window_title (MODEST_MSG_VIEW_WINDOW (self));
2313 update_branding (MODEST_MSG_VIEW_WINDOW (self));
2314 modest_msg_view_grab_focus (MODEST_MSG_VIEW (priv->msg_view));
2317 /* Set the new message uid of the window */
2318 if (priv->msg_uid) {
2319 g_free (priv->msg_uid);
2320 priv->msg_uid = modest_tny_folder_get_header_unique_id (header);
2323 /* Notify the observers */
2324 g_signal_emit (G_OBJECT (self), signals[MSG_CHANGED_SIGNAL],
2325 0, priv->header_model, priv->row_reference);
2327 /* Sync the flags if the message is not opened from a header
2328 model, i.e, if it's opened from a notification */
2329 if (!priv->header_model)
2333 g_object_unref (self);
2335 gtk_tree_row_reference_free (row_reference);
2339 modest_msg_view_window_get_folder_type (ModestMsgViewWindow *window)
2341 ModestMsgViewWindowPrivate *priv;
2343 TnyFolderType folder_type;
2345 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
2347 folder_type = TNY_FOLDER_TYPE_UNKNOWN;
2349 msg = tny_msg_view_get_msg (TNY_MSG_VIEW (priv->msg_view));
2353 folder = tny_msg_get_folder (msg);
2355 folder_type = modest_tny_folder_guess_folder_type (folder);
2356 g_object_unref (folder);
2358 g_object_unref (msg);
2366 modest_msg_view_window_update_priority (ModestMsgViewWindow *window)
2368 ModestMsgViewWindowPrivate *priv;
2369 TnyHeader *header = NULL;
2370 TnyHeaderFlags flags = 0;
2372 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
2374 if (priv->header_model && priv->row_reference && gtk_tree_row_reference_valid (priv->row_reference)) {
2376 GtkTreePath *path = NULL;
2378 path = gtk_tree_row_reference_get_path (priv->row_reference);
2379 g_return_if_fail (path != NULL);
2380 gtk_tree_model_get_iter (priv->header_model,
2382 gtk_tree_row_reference_get_path (priv->row_reference));
2384 gtk_tree_model_get (priv->header_model, &iter, TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
2386 gtk_tree_path_free (path);
2389 msg = tny_msg_view_get_msg (TNY_MSG_VIEW (priv->msg_view));
2391 header = tny_msg_get_header (msg);
2392 g_object_unref (msg);
2397 flags = tny_header_get_flags (header);
2398 g_object_unref(G_OBJECT(header));
2401 modest_msg_view_set_priority (MODEST_MSG_VIEW(priv->msg_view), flags);
2406 toolbar_resize (ModestMsgViewWindow *self)
2408 ModestMsgViewWindowPrivate *priv = NULL;
2409 ModestWindowPrivate *parent_priv = NULL;
2411 gint static_button_size;
2412 ModestWindowMgr *mgr;
2414 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self));
2415 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
2416 parent_priv = MODEST_WINDOW_GET_PRIVATE(self);
2418 mgr = modest_runtime_get_window_mgr ();
2419 static_button_size = modest_window_mgr_get_fullscreen_mode (mgr)?120:120;
2421 if (parent_priv->toolbar) {
2422 /* Set expandable and homogeneous tool buttons */
2423 widget = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/ToolbarMessageReply");
2424 gtk_tool_item_set_expand (GTK_TOOL_ITEM (widget), TRUE);
2425 gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (widget), TRUE);
2426 widget = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/ToolbarMessageReplyAll");
2427 gtk_tool_item_set_expand (GTK_TOOL_ITEM (widget), TRUE);
2428 gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (widget), TRUE);
2429 widget = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/ToolbarMessageForward");
2430 gtk_tool_item_set_expand (GTK_TOOL_ITEM (widget), TRUE);
2431 gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (widget), TRUE);
2432 widget = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/ToolbarDeleteMessage");
2433 gtk_tool_item_set_expand (GTK_TOOL_ITEM (widget), TRUE);
2434 gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (widget), TRUE);
2435 widget = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/ToolbarDownloadExternalImages");
2436 gtk_tool_item_set_expand (GTK_TOOL_ITEM (widget), TRUE);
2437 gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (widget), TRUE);
2438 gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (priv->next_toolitem), TRUE);
2439 gtk_tool_item_set_expand (GTK_TOOL_ITEM (priv->next_toolitem), TRUE);
2440 gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (priv->prev_toolitem), TRUE);
2441 gtk_tool_item_set_expand (GTK_TOOL_ITEM (priv->prev_toolitem), TRUE);
2446 modest_msg_view_window_show_toolbar (ModestWindow *self,
2447 gboolean show_toolbar)
2449 ModestMsgViewWindowPrivate *priv = NULL;
2450 ModestWindowPrivate *parent_priv;
2452 parent_priv = MODEST_WINDOW_GET_PRIVATE(self);
2453 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(self);
2455 /* Set optimized view status */
2456 priv->optimized_view = !show_toolbar;
2458 if (!parent_priv->toolbar) {
2459 parent_priv->toolbar = gtk_ui_manager_get_widget (parent_priv->ui_manager,
2462 #ifdef MODEST_TOOLKIT_HILDON2
2463 gtk_toolbar_set_icon_size (GTK_TOOLBAR (parent_priv->toolbar), HILDON_ICON_SIZE_FINGER);
2465 gtk_toolbar_set_icon_size (GTK_TOOLBAR (parent_priv->toolbar), GTK_ICON_SIZE_LARGE_TOOLBAR);
2467 gtk_widget_set_no_show_all (parent_priv->toolbar, TRUE);
2469 priv->next_toolitem = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/ToolbarMessageNext");
2470 priv->prev_toolitem = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/ToolbarMessageBack");
2471 toolbar_resize (MODEST_MSG_VIEW_WINDOW (self));
2473 modest_window_add_toolbar (MODEST_WINDOW (self),
2474 GTK_TOOLBAR (parent_priv->toolbar));
2479 /* Quick hack: this prevents toolbar icons "dance" when progress bar show status is changed */
2480 /* TODO: resize mode migth be GTK_RESIZE_QUEUE, in order to avoid unneccesary shows */
2481 gtk_container_set_resize_mode (GTK_CONTAINER(parent_priv->toolbar), GTK_RESIZE_IMMEDIATE);
2483 gtk_widget_show (GTK_WIDGET (parent_priv->toolbar));
2484 if (modest_msg_view_window_transfer_mode_enabled (MODEST_MSG_VIEW_WINDOW (self)))
2485 set_progress_hint (MODEST_MSG_VIEW_WINDOW (self), TRUE);
2487 set_progress_hint (MODEST_MSG_VIEW_WINDOW (self), FALSE);
2490 gtk_widget_set_no_show_all (parent_priv->toolbar, TRUE);
2491 gtk_widget_hide (GTK_WIDGET (parent_priv->toolbar));
2496 modest_msg_view_window_clipboard_owner_change (GtkClipboard *clipboard,
2498 ModestMsgViewWindow *window)
2500 if (!GTK_WIDGET_VISIBLE (window))
2503 modest_window_check_dimming_rules_group (MODEST_WINDOW (window), MODEST_DIMMING_RULES_CLIPBOARD);
2507 modest_msg_view_window_transfer_mode_enabled (ModestMsgViewWindow *self)
2509 ModestMsgViewWindowPrivate *priv;
2511 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self), FALSE);
2512 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(self);
2514 return priv->progress_hint;
2518 observers_empty (ModestMsgViewWindow *self)
2521 ModestMsgViewWindowPrivate *priv;
2522 gboolean is_empty = TRUE;
2523 guint pending_ops = 0;
2525 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(self);
2526 tmp = priv->progress_widgets;
2528 /* Check all observers */
2529 while (tmp && is_empty) {
2530 pending_ops = modest_progress_object_num_pending_operations (MODEST_PROGRESS_OBJECT(tmp->data));
2531 is_empty = pending_ops == 0;
2533 tmp = g_slist_next(tmp);
2540 on_account_removed (TnyAccountStore *account_store,
2541 TnyAccount *account,
2544 /* Do nothing if it's a transport account, because we only
2545 show the messages of a store account */
2546 if (tny_account_get_account_type(account) == TNY_ACCOUNT_TYPE_STORE) {
2547 const gchar *parent_acc = NULL;
2548 const gchar *our_acc = NULL;
2550 our_acc = modest_window_get_active_account (MODEST_WINDOW (user_data));
2551 parent_acc = modest_tny_account_get_parent_modest_account_name_for_server_account (account);
2553 /* Close this window if I'm showing a message of the removed account */
2554 if (our_acc && parent_acc && strcmp (parent_acc, our_acc) == 0)
2555 modest_ui_actions_on_close_window (NULL, MODEST_WINDOW (user_data));
2560 on_mail_operation_started (ModestMailOperation *mail_op,
2563 ModestMsgViewWindow *self;
2564 ModestMailOperationTypeOperation op_type;
2566 ModestMsgViewWindowPrivate *priv;
2567 GObject *source = NULL;
2569 self = MODEST_MSG_VIEW_WINDOW (user_data);
2570 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
2571 op_type = modest_mail_operation_get_type_operation (mail_op);
2572 tmp = priv->progress_widgets;
2573 source = modest_mail_operation_get_source(mail_op);
2574 if (G_OBJECT (self) == source) {
2575 if (op_type == MODEST_MAIL_OPERATION_TYPE_RECEIVE ||
2576 op_type == MODEST_MAIL_OPERATION_TYPE_OPEN ||
2577 op_type == MODEST_MAIL_OPERATION_TYPE_DELETE) {
2578 set_progress_hint (self, TRUE);
2580 modest_progress_object_add_operation (
2581 MODEST_PROGRESS_OBJECT (tmp->data),
2583 tmp = g_slist_next (tmp);
2587 g_object_unref (source);
2589 /* Update dimming rules */
2590 check_dimming_rules_after_change (self);
2594 on_mail_operation_finished (ModestMailOperation *mail_op,
2597 ModestMsgViewWindow *self;
2598 ModestMailOperationTypeOperation op_type;
2600 ModestMsgViewWindowPrivate *priv;
2602 self = MODEST_MSG_VIEW_WINDOW (user_data);
2603 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
2604 op_type = modest_mail_operation_get_type_operation (mail_op);
2605 tmp = priv->progress_widgets;
2607 if (op_type == MODEST_MAIL_OPERATION_TYPE_RECEIVE ||
2608 op_type == MODEST_MAIL_OPERATION_TYPE_OPEN ||
2609 op_type == MODEST_MAIL_OPERATION_TYPE_DELETE) {
2611 modest_progress_object_remove_operation (MODEST_PROGRESS_OBJECT (tmp->data),
2613 tmp = g_slist_next (tmp);
2616 /* If no more operations are being observed, NORMAL mode is enabled again */
2617 if (observers_empty (self)) {
2618 set_progress_hint (self, FALSE);
2622 /* Update dimming rules. We have to do this right here
2623 and not in view_msg_cb because at that point the
2624 transfer mode is still enabled so the dimming rule
2625 won't let the user delete the message that has been
2626 readed for example */
2627 check_dimming_rules_after_change (self);
2631 on_queue_changed (ModestMailOperationQueue *queue,
2632 ModestMailOperation *mail_op,
2633 ModestMailOperationQueueNotification type,
2634 ModestMsgViewWindow *self)
2636 ModestMsgViewWindowPrivate *priv;
2638 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
2640 /* If this operations was created by another window, do nothing */
2641 if (!modest_mail_operation_is_mine (mail_op, G_OBJECT(self)))
2644 if (type == MODEST_MAIL_OPERATION_QUEUE_OPERATION_ADDED) {
2645 priv->sighandlers = modest_signal_mgr_connect (priv->sighandlers,
2647 "operation-started",
2648 G_CALLBACK (on_mail_operation_started),
2650 priv->sighandlers = modest_signal_mgr_connect (priv->sighandlers,
2652 "operation-finished",
2653 G_CALLBACK (on_mail_operation_finished),
2655 } else if (type == MODEST_MAIL_OPERATION_QUEUE_OPERATION_REMOVED) {
2656 priv->sighandlers = modest_signal_mgr_disconnect (priv->sighandlers,
2658 "operation-started");
2659 priv->sighandlers = modest_signal_mgr_disconnect (priv->sighandlers,
2661 "operation-finished");
2666 modest_msg_view_window_get_attachments (ModestMsgViewWindow *win)
2668 ModestMsgViewWindowPrivate *priv;
2669 TnyList *selected_attachments = NULL;
2671 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (win), NULL);
2672 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (win);
2674 /* In Hildon 2.2 as there's no selection we assume we have all attachments selected */
2675 selected_attachments = modest_msg_view_get_attachments (MODEST_MSG_VIEW (priv->msg_view));
2677 return selected_attachments;
2681 ModestMsgViewWindow *self;
2683 gchar *attachment_uid;
2684 } DecodeAsyncHelper;
2687 on_decode_to_stream_async_handler (TnyMimePart *mime_part,
2693 DecodeAsyncHelper *helper = (DecodeAsyncHelper *) user_data;
2694 const gchar *content_type;
2696 if (cancelled || err) {
2699 if ((err->domain == TNY_ERROR_DOMAIN) &&
2700 (err->code == TNY_IO_ERROR_WRITE) &&
2701 (errno == ENOSPC)) {
2702 msg = g_strdup_printf (_KR("cerm_device_memory_full"), "");
2704 msg = g_strdup (_("mail_ib_file_operation_failed"));
2706 modest_platform_information_banner (NULL, NULL, msg);
2712 /* It could happen that the window was closed. So we
2713 assume it is a cancelation */
2714 if (!GTK_WIDGET_VISIBLE (helper->self))
2717 /* Remove the progress hint */
2718 set_progress_hint (helper->self, FALSE);
2720 content_type = tny_mime_part_get_content_type (mime_part);
2721 if (content_type && g_str_has_prefix (content_type, "message/rfc822")) {
2722 ModestWindowMgr *mgr;
2723 ModestWindow *msg_win = NULL;
2726 const gchar *mailbox;
2727 TnyStream *file_stream;
2730 fd = g_open (helper->file_path, O_RDONLY, 0644);
2732 file_stream = tny_fs_stream_new (fd);
2734 mgr = modest_runtime_get_window_mgr ();
2736 account = g_strdup (modest_window_get_active_account (MODEST_WINDOW (helper->self)));
2737 mailbox = modest_window_get_active_mailbox (MODEST_WINDOW (helper->self));
2740 account = modest_account_mgr_get_default_account (modest_runtime_get_account_mgr ());
2742 msg = tny_camel_msg_new ();
2743 tny_camel_msg_parse ((TnyCamelMsg *) msg, file_stream);
2744 msg_win = modest_msg_view_window_new_for_attachment (TNY_MSG (msg), account, mailbox, helper->attachment_uid);
2745 modest_window_set_zoom (MODEST_WINDOW (msg_win),
2746 modest_window_get_zoom (MODEST_WINDOW (helper->self)));
2747 if (modest_window_mgr_register_window (mgr, msg_win, MODEST_WINDOW (helper->self)))
2748 gtk_widget_show_all (GTK_WIDGET (msg_win));
2750 gtk_widget_destroy (GTK_WIDGET (msg_win));
2751 g_object_unref (msg);
2752 g_object_unref (file_stream);
2754 modest_platform_information_banner (NULL, NULL, _("mail_ib_file_operation_failed"));
2759 /* make the file read-only */
2760 g_chmod(helper->file_path, 0444);
2762 /* Activate the file */
2763 modest_platform_activate_file (helper->file_path, tny_mime_part_get_content_type (mime_part));
2768 g_object_unref (helper->self);
2769 g_free (helper->file_path);
2770 g_free (helper->attachment_uid);
2771 g_slice_free (DecodeAsyncHelper, helper);
2775 view_attachment_connect_handler (gboolean canceled,
2777 GtkWindow *parent_window,
2778 TnyAccount *account,
2782 if (canceled || err) {
2783 g_object_unref (part);
2787 modest_msg_view_window_view_attachment (MODEST_MSG_VIEW_WINDOW (parent_window),
2789 g_object_unref (part);
2793 modest_msg_view_window_view_attachment (ModestMsgViewWindow *window,
2794 TnyMimePart *mime_part)
2796 ModestMsgViewWindowPrivate *priv;
2797 const gchar *msg_uid;
2798 gchar *attachment_uid = NULL;
2799 gint attachment_index = 0;
2800 TnyList *attachments;
2802 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window));
2803 g_return_if_fail (TNY_IS_MIME_PART (mime_part) || (mime_part == NULL));
2804 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
2806 msg_uid = modest_msg_view_window_get_message_uid (MODEST_MSG_VIEW_WINDOW (window));
2807 attachments = modest_msg_view_get_attachments (MODEST_MSG_VIEW (priv->msg_view));
2808 attachment_index = modest_list_index (attachments, (GObject *) mime_part);
2809 g_object_unref (attachments);
2811 if (msg_uid && attachment_index >= 0) {
2812 attachment_uid = g_strdup_printf ("%s/%d", msg_uid, attachment_index);
2815 if (mime_part == NULL) {
2816 gboolean error = FALSE;
2817 TnyList *selected_attachments = modest_msg_view_get_selected_attachments (MODEST_MSG_VIEW (priv->msg_view));
2818 if (selected_attachments == NULL || tny_list_get_length (selected_attachments) == 0) {
2820 } else if (tny_list_get_length (selected_attachments) > 1) {
2821 modest_platform_system_banner (NULL, NULL, _("mcen_ib_unable_to_display_more"));
2825 iter = tny_list_create_iterator (selected_attachments);
2826 mime_part = (TnyMimePart *) tny_iterator_get_current (iter);
2827 g_object_unref (iter);
2829 if (selected_attachments)
2830 g_object_unref (selected_attachments);
2835 g_object_ref (mime_part);
2838 if (tny_mime_part_is_purged (mime_part))
2841 if (TNY_IS_CAMEL_BS_MIME_PART (mime_part) &&
2842 !tny_camel_bs_mime_part_is_fetched (TNY_CAMEL_BS_MIME_PART (mime_part))) {
2844 TnyAccount *account;
2846 is_merge = g_str_has_prefix (priv->msg_uid, "merge:");
2848 /* Get the account */
2850 account = tny_account_store_find_account (TNY_ACCOUNT_STORE (modest_runtime_get_account_store ()),
2853 if (!tny_device_is_online (modest_runtime_get_device())) {
2854 modest_platform_connect_and_perform ((ModestWindow *) window,
2856 TNY_ACCOUNT (account),
2857 (ModestConnectedPerformer) view_attachment_connect_handler,
2858 g_object_ref (mime_part));
2863 if (!modest_tny_mime_part_is_msg (mime_part) && tny_mime_part_get_filename (mime_part)) {
2864 gchar *filepath = NULL;
2865 const gchar *att_filename = tny_mime_part_get_filename (mime_part);
2866 gboolean show_error_banner = FALSE;
2867 TnyFsStream *temp_stream = NULL;
2868 temp_stream = modest_utils_create_temp_stream (att_filename, attachment_uid,
2871 if (temp_stream != NULL) {
2872 ModestAccountMgr *mgr;
2873 DecodeAsyncHelper *helper;
2874 gboolean decode_in_provider;
2875 ModestProtocol *protocol;
2876 const gchar *account;
2878 /* Activate progress hint */
2879 set_progress_hint (window, TRUE);
2881 helper = g_slice_new0 (DecodeAsyncHelper);
2882 helper->self = g_object_ref (window);
2883 helper->file_path = g_strdup (filepath);
2884 helper->attachment_uid = g_strdup (attachment_uid);
2886 decode_in_provider = FALSE;
2887 mgr = modest_runtime_get_account_mgr ();
2888 account = modest_window_get_active_account (MODEST_WINDOW (window));
2889 if (modest_account_mgr_account_is_multimailbox (mgr, account, &protocol)) {
2890 if (MODEST_IS_ACCOUNT_PROTOCOL (protocol)) {
2892 uri = g_strconcat ("file://", filepath, NULL);
2893 decode_in_provider =
2894 modest_account_protocol_decode_part_to_stream_async (
2895 MODEST_ACCOUNT_PROTOCOL (protocol),
2898 TNY_STREAM (temp_stream),
2899 on_decode_to_stream_async_handler,
2906 if (!decode_in_provider)
2907 tny_mime_part_decode_to_stream_async (mime_part, TNY_STREAM (temp_stream),
2908 on_decode_to_stream_async_handler,
2911 g_object_unref (temp_stream);
2912 /* NOTE: files in the temporary area will be automatically
2913 * cleaned after some time if they are no longer in use */
2916 const gchar *content_type;
2917 /* the file may already exist but it isn't writable,
2918 * let's try to open it anyway */
2919 content_type = tny_mime_part_get_content_type (mime_part);
2920 modest_platform_activate_file (filepath, content_type);
2922 g_warning ("%s: modest_utils_create_temp_stream failed", __FUNCTION__);
2923 show_error_banner = TRUE;
2928 if (show_error_banner)
2929 modest_platform_information_banner (NULL, NULL, _("mail_ib_file_operation_failed"));
2930 } else if (!modest_tny_mime_part_is_msg (mime_part)) {
2931 ModestWindowMgr *mgr;
2932 ModestWindow *msg_win = NULL;
2933 TnyMsg *current_msg;
2937 current_msg = modest_msg_view_window_get_message (MODEST_MSG_VIEW_WINDOW (window));
2938 mgr = modest_runtime_get_window_mgr ();
2939 header = tny_msg_get_header (TNY_MSG (current_msg));
2940 found = modest_window_mgr_find_registered_message_uid (mgr,
2945 g_debug ("window for this body is already being created");
2948 /* it's not found, so create a new window for it */
2949 modest_window_mgr_register_header (mgr, header, attachment_uid); /* register the uid before building the window */
2950 gchar *account = g_strdup (modest_window_get_active_account (MODEST_WINDOW (window)));
2951 const gchar *mailbox = modest_window_get_active_mailbox (MODEST_WINDOW (window));
2953 account = modest_account_mgr_get_default_account (modest_runtime_get_account_mgr ());
2955 msg_win = modest_msg_view_window_new_with_other_body (TNY_MSG (current_msg), TNY_MIME_PART (mime_part),
2956 account, mailbox, attachment_uid);
2958 modest_window_set_zoom (MODEST_WINDOW (msg_win),
2959 modest_window_get_zoom (MODEST_WINDOW (window)));
2960 if (modest_window_mgr_register_window (mgr, msg_win, MODEST_WINDOW (window)))
2961 gtk_widget_show_all (GTK_WIDGET (msg_win));
2963 gtk_widget_destroy (GTK_WIDGET (msg_win));
2965 g_object_unref (current_msg);
2967 /* message attachment */
2968 TnyHeader *header = NULL;
2969 ModestWindowMgr *mgr;
2970 ModestWindow *msg_win = NULL;
2973 header = tny_msg_get_header (TNY_MSG (mime_part));
2974 mgr = modest_runtime_get_window_mgr ();
2975 found = modest_window_mgr_find_registered_header (mgr, header, &msg_win);
2978 /* if it's found, but there is no msg_win, it's probably in the process of being created;
2979 * thus, we don't do anything */
2980 g_debug ("window for is already being created");
2982 /* it's not found, so create a new window for it */
2983 modest_window_mgr_register_header (mgr, header, attachment_uid); /* register the uid before building the window */
2984 gchar *account = g_strdup (modest_window_get_active_account (MODEST_WINDOW (window)));
2985 const gchar *mailbox = modest_window_get_active_mailbox (MODEST_WINDOW (window));
2987 account = modest_account_mgr_get_default_account (modest_runtime_get_account_mgr ());
2988 msg_win = modest_msg_view_window_new_for_attachment (TNY_MSG (mime_part), account,
2989 mailbox, attachment_uid);
2990 modest_window_set_zoom (MODEST_WINDOW (msg_win),
2991 modest_window_get_zoom (MODEST_WINDOW (window)));
2992 if (modest_window_mgr_register_window (mgr, msg_win, MODEST_WINDOW (window)))
2993 gtk_widget_show_all (GTK_WIDGET (msg_win));
2995 gtk_widget_destroy (GTK_WIDGET (msg_win));
3001 g_free (attachment_uid);
3003 g_object_unref (mime_part);
3015 GnomeVFSResult result;
3017 ModestMsgViewWindow *window;
3020 static void save_mime_part_info_free (SaveMimePartInfo *info, gboolean with_struct);
3021 static gboolean idle_save_mime_part_show_result (SaveMimePartInfo *info);
3022 static gpointer save_mime_part_to_file (SaveMimePartInfo *info);
3023 static void save_mime_parts_to_file_with_checks (GtkWindow *parent, SaveMimePartInfo *info);
3026 save_mime_part_info_free (SaveMimePartInfo *info, gboolean with_struct)
3030 for (node = info->pairs; node != NULL; node = g_list_next (node)) {
3031 SaveMimePartPair *pair = (SaveMimePartPair *) node->data;
3032 g_free (pair->filename);
3033 g_object_unref (pair->part);
3034 g_slice_free (SaveMimePartPair, pair);
3036 g_list_free (info->pairs);
3039 g_object_unref (info->window);
3040 info->window = NULL;
3042 g_slice_free (SaveMimePartInfo, info);
3047 idle_save_mime_part_show_result (SaveMimePartInfo *info)
3049 /* This is a GDK lock because we are an idle callback and
3050 * modest_platform_system_banner is or does Gtk+ code */
3052 gdk_threads_enter (); /* CHECKED */
3053 if (info->result == GNOME_VFS_ERROR_CANCELLED) {
3055 } else if (info->result == GNOME_VFS_OK) {
3056 modest_platform_system_banner (NULL, NULL, _CS_SAVED);
3057 } else if (info->result == GNOME_VFS_ERROR_NO_SPACE) {
3060 /* Check if the uri belongs to the external mmc */
3061 if (g_str_has_prefix (info->uri, g_getenv (MODEST_MMC1_VOLUMEPATH_ENV)))
3062 msg = g_strdup_printf (_KR("cerm_device_memory_full"), "");
3064 msg = g_strdup (_KR("cerm_memory_card_full"));
3065 modest_platform_information_banner (NULL, NULL, msg);
3068 modest_platform_system_banner (NULL, NULL, _("mail_ib_file_operation_failed"));
3070 save_mime_part_info_free (info, FALSE);
3071 gdk_threads_leave (); /* CHECKED */
3077 save_mime_part_to_file_connect_handler (gboolean canceled,
3079 GtkWindow *parent_window,
3080 TnyAccount *account,
3081 SaveMimePartInfo *info)
3083 if (canceled || err) {
3084 if (canceled && !err) {
3085 info->result = GNOME_VFS_ERROR_CANCELLED;
3087 g_idle_add ((GSourceFunc) idle_save_mime_part_show_result, info);
3089 g_thread_create ((GThreadFunc)save_mime_part_to_file, info, FALSE, NULL);
3094 save_mime_part_to_file_connect_idle (SaveMimePartInfo *info)
3097 TnyAccount *account;
3098 ModestMsgViewWindowPrivate *priv;
3100 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (info->window);
3102 is_merge = g_str_has_prefix (priv->msg_uid, "merge:");
3105 /* Get the account */
3107 account = tny_account_store_find_account (TNY_ACCOUNT_STORE (modest_runtime_get_account_store ()),
3110 modest_platform_connect_and_perform ((ModestWindow *) info->window,
3112 TNY_ACCOUNT (account),
3113 (ModestConnectedPerformer) save_mime_part_to_file_connect_handler,
3119 save_mime_part_to_file (SaveMimePartInfo *info)
3121 GnomeVFSHandle *handle;
3123 SaveMimePartPair *pair = (SaveMimePartPair *) info->pairs->data;
3125 if (TNY_IS_CAMEL_BS_MIME_PART (pair->part) &&
3126 !tny_camel_bs_mime_part_is_fetched (TNY_CAMEL_BS_MIME_PART (pair->part))) {
3127 g_idle_add ((GSourceFunc) save_mime_part_to_file_connect_idle, info);
3131 info->result = gnome_vfs_create (&handle, pair->filename, GNOME_VFS_OPEN_WRITE, FALSE, 0644);
3132 if (info->result == GNOME_VFS_OK) {
3133 GError *error = NULL;
3134 gboolean decode_in_provider;
3136 ModestAccountMgr *mgr;
3137 const gchar *account;
3138 ModestProtocol *protocol = NULL;
3140 stream = tny_vfs_stream_new (handle);
3142 decode_in_provider = FALSE;
3143 mgr = modest_runtime_get_account_mgr ();
3144 account = modest_window_get_active_account (MODEST_WINDOW (info->window));
3145 if (modest_account_mgr_account_is_multimailbox (mgr, account, &protocol)) {
3146 if (MODEST_IS_ACCOUNT_PROTOCOL (protocol)) {
3147 decode_in_provider =
3148 modest_account_protocol_decode_part_to_stream (
3149 MODEST_ACCOUNT_PROTOCOL (protocol),
3157 if (!decode_in_provider)
3158 written = tny_mime_part_decode_to_stream (pair->part, stream, &error);
3161 g_warning ("modest: could not save attachment %s: %d (%s)\n", pair->filename, error?error->code:-1, error?error->message:"Unknown error");
3163 if ((error->domain == TNY_ERROR_DOMAIN) &&
3164 (error->code == TNY_IO_ERROR_WRITE) &&
3165 (errno == ENOSPC)) {
3166 info->result = GNOME_VFS_ERROR_NO_SPACE;
3168 info->result = GNOME_VFS_ERROR_IO;
3171 g_object_unref (G_OBJECT (stream));
3173 g_warning ("Could not create save attachment %s: %s\n",
3174 pair->filename, gnome_vfs_result_to_string (info->result));
3177 /* Go on saving remaining files */
3178 info->pairs = g_list_remove_link (info->pairs, info->pairs);
3179 if (info->pairs != NULL) {
3180 save_mime_part_to_file (info);
3182 g_idle_add ((GSourceFunc) idle_save_mime_part_show_result, info);
3189 save_mime_parts_to_file_with_checks (GtkWindow *parent,
3190 SaveMimePartInfo *info)
3192 gboolean is_ok = TRUE;
3193 gint replaced_files = 0;
3194 const GList *files = info->pairs;
3195 const GList *iter, *to_replace = NULL;
3197 for (iter = files; (iter != NULL) && (replaced_files < 2); iter = g_list_next(iter)) {
3198 SaveMimePartPair *pair = iter->data;
3199 gchar *unescaped = g_uri_unescape_string (pair->filename, NULL);
3201 if (modest_utils_file_exists (unescaped)) {
3203 if (replaced_files == 1)
3208 if (replaced_files) {
3211 if (replaced_files == 1) {
3212 SaveMimePartPair *pair = to_replace->data;
3213 const gchar *basename = strrchr (pair->filename, G_DIR_SEPARATOR) + 1;
3214 gchar *escaped_basename, *message;
3216 escaped_basename = g_uri_unescape_string (basename, NULL);
3217 message = g_strdup_printf ("%s\n%s",
3219 (escaped_basename) ? escaped_basename : "");
3220 response = modest_platform_run_confirmation_dialog (parent, message);
3222 g_free (escaped_basename);
3224 response = modest_platform_run_confirmation_dialog (parent,
3225 _FM_REPLACE_MULTIPLE);
3227 if (response != GTK_RESPONSE_OK)
3232 save_mime_part_info_free (info, TRUE);
3234 g_thread_create ((GThreadFunc)save_mime_part_to_file, info, FALSE, NULL);
3239 typedef struct _SaveAttachmentsInfo {
3240 TnyList *attachments_list;
3241 ModestMsgViewWindow *window;
3242 } SaveAttachmentsInfo;
3245 save_attachments_response (GtkDialog *dialog,
3249 TnyList *mime_parts;
3251 GList *files_to_save = NULL;
3252 gchar *current_folder;
3253 SaveAttachmentsInfo *sa_info = (SaveAttachmentsInfo *) user_data;
3255 mime_parts = TNY_LIST (sa_info->attachments_list);
3257 if (arg1 != GTK_RESPONSE_OK)
3260 chooser_uri = gtk_file_chooser_get_uri (GTK_FILE_CHOOSER (dialog));
3261 current_folder = gtk_file_chooser_get_current_folder_uri (GTK_FILE_CHOOSER (dialog));
3262 if (current_folder && *current_folder != '\0') {
3264 modest_conf_set_string (modest_runtime_get_conf (), MODEST_CONF_LATEST_SAVE_ATTACHMENT_PATH,
3265 current_folder,&err);
3267 g_debug ("Error storing latest used folder: %s", err->message);
3271 g_free (current_folder);
3273 if (!modest_utils_folder_writable (chooser_uri)) {
3274 const gchar *err_msg;
3276 #ifdef MODEST_PLATFORM_MAEMO
3277 if (modest_maemo_utils_in_usb_mode ()) {
3278 err_msg = dgettext ("hildon-status-bar-usb", "usbh_ib_mmc_usb_connected");
3280 err_msg = _FM_READ_ONLY_LOCATION;
3283 err_msg = _FM_READ_ONLY_LOCATION;
3285 modest_platform_system_banner (NULL, NULL, err_msg);
3289 iter = tny_list_create_iterator (mime_parts);
3290 while (!tny_iterator_is_done (iter)) {
3291 TnyMimePart *mime_part = (TnyMimePart *) tny_iterator_get_current (iter);
3293 if ((modest_tny_mime_part_is_attachment_for_modest (mime_part)) &&
3294 !tny_mime_part_is_purged (mime_part) &&
3295 (tny_mime_part_get_filename (mime_part) != NULL)) {
3296 SaveMimePartPair *pair;
3298 pair = g_slice_new0 (SaveMimePartPair);
3300 if (tny_list_get_length (mime_parts) > 1) {
3302 gnome_vfs_escape_slashes (tny_mime_part_get_filename (mime_part));
3303 pair->filename = g_build_filename (chooser_uri, escaped, NULL);
3306 pair->filename = g_strdup (chooser_uri);
3308 pair->part = mime_part;
3309 files_to_save = g_list_prepend (files_to_save, pair);
3311 tny_iterator_next (iter);
3313 g_object_unref (iter);
3316 if (files_to_save != NULL) {
3317 SaveMimePartInfo *info = g_slice_new0 (SaveMimePartInfo);
3318 info->pairs = files_to_save;
3319 info->result = TRUE;
3320 info->uri = g_strdup (chooser_uri);
3321 info->window = g_object_ref (sa_info->window);
3322 save_mime_parts_to_file_with_checks ((GtkWindow *) dialog, info);
3324 g_free (chooser_uri);
3327 /* Free and close the dialog */
3328 g_object_unref (mime_parts);
3329 g_object_unref (sa_info->window);
3330 g_slice_free (SaveAttachmentsInfo, sa_info);
3331 gtk_widget_destroy (GTK_WIDGET (dialog));
3335 modest_msg_view_window_save_attachments (ModestMsgViewWindow *window,
3336 TnyList *mime_parts)
3338 ModestMsgViewWindowPrivate *priv;
3339 GtkWidget *save_dialog = NULL;
3340 gchar *conf_folder = NULL;
3341 gchar *filename = NULL;
3342 gchar *save_multiple_str = NULL;
3343 const gchar *root_folder = "file:///";
3345 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window));
3346 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
3348 if (mime_parts == NULL) {
3349 /* In Hildon 2.2 save and delete operate over all the attachments as there's no
3350 * selection available */
3351 mime_parts = modest_msg_view_get_attachments (MODEST_MSG_VIEW (priv->msg_view));
3352 if (mime_parts && !modest_toolkit_utils_select_attachments (GTK_WINDOW (gtk_widget_get_toplevel (GTK_WIDGET (window))), mime_parts, FALSE)) {
3353 g_object_unref (mime_parts);
3356 if (mime_parts == NULL || tny_list_get_length (mime_parts) == 0) {
3358 g_object_unref (mime_parts);
3364 g_object_ref (mime_parts);
3367 /* prepare dialog */
3368 if (tny_list_get_length (mime_parts) == 1) {
3370 /* only one attachment selected */
3371 iter = tny_list_create_iterator (mime_parts);
3372 TnyMimePart *mime_part = (TnyMimePart *) tny_iterator_get_current (iter);
3373 g_object_unref (iter);
3374 if (!modest_tny_mime_part_is_msg (mime_part) &&
3375 modest_tny_mime_part_is_attachment_for_modest (mime_part) &&
3376 !tny_mime_part_is_purged (mime_part)) {
3377 filename = g_strdup (tny_mime_part_get_filename (mime_part));
3379 /* TODO: show any error? */
3380 g_warning ("%s: Tried to save a non-file attachment", __FUNCTION__);
3381 g_object_unref (mime_parts);
3384 g_object_unref (mime_part);
3386 gint num = tny_list_get_length (mime_parts);
3387 save_multiple_str = g_strdup_printf (dngettext("hildon-fm",
3388 "sfil_va_number_of_objects_attachment",
3389 "sfil_va_number_of_objects_attachments",
3393 /* Creation of hildon file chooser dialog for saving */
3394 save_dialog = modest_toolkit_factory_create_file_chooser_dialog (modest_runtime_get_toolkit_factory (),
3396 (GtkWindow *) window,
3397 GTK_FILE_CHOOSER_ACTION_SAVE);
3399 /* Get last used folder */
3400 conf_folder = modest_conf_get_string (modest_runtime_get_conf (),
3401 MODEST_CONF_LATEST_SAVE_ATTACHMENT_PATH, NULL);
3403 /* File chooser stops working if we select "file:///" as current folder */
3404 if (conf_folder && g_ascii_strcasecmp (root_folder, conf_folder) != 0) {
3405 g_free (conf_folder);
3409 if (conf_folder && conf_folder[0] != '\0') {
3410 gtk_file_chooser_set_current_folder_uri (GTK_FILE_CHOOSER (save_dialog), conf_folder);
3413 /* Set the default folder to documents folder */
3414 docs_folder = (gchar *) g_strdup(g_get_user_special_dir (G_USER_DIRECTORY_DOCUMENTS));
3417 docs_folder = g_build_filename (g_getenv (MYDOCS_ENV), DOCS_FOLDER, NULL);
3419 gtk_file_chooser_set_current_folder (GTK_FILE_CHOOSER (save_dialog), docs_folder);
3420 g_free (docs_folder);
3422 g_free (conf_folder);
3426 gtk_file_chooser_set_current_name (GTK_FILE_CHOOSER (save_dialog),
3431 /* if multiple, set multiple string */
3432 if (save_multiple_str) {
3433 g_object_set (G_OBJECT (save_dialog), "save-multiple", save_multiple_str, NULL);
3434 gtk_window_set_title (GTK_WINDOW (save_dialog), _FM_SAVE_OBJECT_FILES);
3435 g_free (save_multiple_str);
3438 /* We must run this asynchronously, because the hildon dialog
3439 performs a gtk_dialog_run by itself which leads to gdk
3441 SaveAttachmentsInfo *sa_info;
3442 sa_info = g_slice_new (SaveAttachmentsInfo);
3443 sa_info->attachments_list = mime_parts;
3444 sa_info->window = g_object_ref (window);
3445 g_signal_connect (save_dialog, "response",
3446 G_CALLBACK (save_attachments_response), sa_info);
3448 gtk_widget_show_all (save_dialog);
3452 show_remove_attachment_information (gpointer userdata)
3454 ModestMsgViewWindow *window = (ModestMsgViewWindow *) userdata;
3455 ModestMsgViewWindowPrivate *priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
3457 /* We're outside the main lock */
3458 gdk_threads_enter ();
3460 if (priv->remove_attachment_banner != NULL) {
3461 gtk_widget_destroy (priv->remove_attachment_banner);
3462 g_object_unref (priv->remove_attachment_banner);
3465 priv->remove_attachment_banner = g_object_ref (
3466 modest_platform_animation_banner (NULL, NULL, _("mcen_me_inbox_remove_attachments")));
3468 gdk_threads_leave ();
3474 modest_msg_view_window_remove_attachments (ModestMsgViewWindow *window, gboolean get_all)
3476 ModestMsgViewWindowPrivate *priv;
3477 TnyList *mime_parts = NULL, *tmp;
3478 gchar *confirmation_message;
3484 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window));
3485 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
3487 #ifdef MODEST_TOOLKIT_HILDON2
3488 /* In hildon 2.2 we ignore the get_all flag as we always get all attachments. This is
3489 * because we don't have selection
3491 mime_parts = modest_msg_view_get_attachments (MODEST_MSG_VIEW (priv->msg_view));
3493 /* Remove already purged messages from mime parts list. We use
3494 a copy of the list to remove items in the original one */
3495 tmp = tny_list_copy (mime_parts);
3496 iter = tny_list_create_iterator (tmp);
3497 while (!tny_iterator_is_done (iter)) {
3498 TnyMimePart *part = TNY_MIME_PART (tny_iterator_get_current (iter));
3499 if (tny_mime_part_is_purged (part))
3500 tny_list_remove (mime_parts, (GObject *) part);
3502 g_object_unref (part);
3503 tny_iterator_next (iter);
3505 g_object_unref (tmp);
3506 g_object_unref (iter);
3508 if (!modest_toolkit_utils_select_attachments (GTK_WINDOW (window), mime_parts, TRUE) ||
3509 tny_list_get_length (mime_parts) == 0) {
3510 g_object_unref (mime_parts);
3514 /* In gtk we get only selected attachments for the operation.
3516 mime_parts = modest_msg_view_get_selected_attachments (MODEST_MSG_VIEW (priv->msg_view));
3518 /* Remove already purged messages from mime parts list. We use
3519 a copy of the list to remove items in the original one */
3520 tmp = tny_list_copy (mime_parts);
3521 iter = tny_list_create_iterator (tmp);
3522 while (!tny_iterator_is_done (iter)) {
3523 TnyMimePart *part = TNY_MIME_PART (tny_iterator_get_current (iter));
3524 if (tny_mime_part_is_purged (part))
3525 tny_list_remove (mime_parts, (GObject *) part);
3527 g_object_unref (part);
3528 tny_iterator_next (iter);
3530 g_object_unref (tmp);
3531 g_object_unref (iter);
3533 if (tny_list_get_length (mime_parts) == 0) {
3534 g_object_unref (mime_parts);
3539 n_attachments = tny_list_get_length (mime_parts);
3540 if (n_attachments == 1) {
3544 iter = tny_list_create_iterator (mime_parts);
3545 part = (TnyMimePart *) tny_iterator_get_current (iter);
3546 g_object_unref (iter);
3547 if (modest_tny_mime_part_is_msg (part)) {
3549 header = tny_msg_get_header (TNY_MSG (part));
3550 filename = tny_header_dup_subject (header);
3551 g_object_unref (header);
3552 if (filename == NULL)
3553 filename = g_strdup (_("mail_va_no_subject"));
3555 filename = g_strdup (tny_mime_part_get_filename (TNY_MIME_PART (part)));
3557 confirmation_message = g_strdup_printf (_("mcen_nc_purge_file_text"), filename);
3559 g_object_unref (part);
3561 confirmation_message = g_strdup_printf (ngettext("mcen_nc_purge_file_text",
3562 "mcen_nc_purge_files_text",
3563 n_attachments), n_attachments);
3565 response = modest_platform_run_confirmation_dialog (GTK_WINDOW (gtk_widget_get_toplevel (GTK_WIDGET (window))),
3566 confirmation_message);
3567 g_free (confirmation_message);
3569 if (response != GTK_RESPONSE_OK) {
3570 g_object_unref (mime_parts);
3574 priv->purge_timeout = g_timeout_add (2000, show_remove_attachment_information, window);
3576 iter = tny_list_create_iterator (mime_parts);
3577 while (!tny_iterator_is_done (iter)) {
3580 part = (TnyMimePart *) tny_iterator_get_current (iter);
3581 tny_mime_part_set_purged (TNY_MIME_PART (part));
3582 g_object_unref (part);
3583 tny_iterator_next (iter);
3585 g_object_unref (iter);
3587 msg = tny_msg_view_get_msg (TNY_MSG_VIEW (priv->msg_view));
3588 tny_msg_view_clear (TNY_MSG_VIEW (priv->msg_view));
3589 tny_msg_rewrite_cache (msg);
3590 tny_msg_view_set_msg (TNY_MSG_VIEW (priv->msg_view), msg);
3591 g_object_unref (msg);
3592 update_branding (MODEST_MSG_VIEW_WINDOW (window));
3594 g_object_unref (mime_parts);
3596 if (priv->purge_timeout > 0) {
3597 g_source_remove (priv->purge_timeout);
3598 priv->purge_timeout = 0;
3601 if (priv->remove_attachment_banner) {
3602 gtk_widget_destroy (priv->remove_attachment_banner);
3603 g_object_unref (priv->remove_attachment_banner);
3604 priv->remove_attachment_banner = NULL;
3610 update_window_title (ModestMsgViewWindow *window)
3612 ModestMsgViewWindowPrivate *priv;
3614 TnyHeader *header = NULL;
3615 gchar *subject = NULL;
3617 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
3619 /* Note that if the window is closed while we're retrieving
3620 the message, this widget could de deleted */
3621 if (!priv->msg_view)
3624 msg = tny_msg_view_get_msg (TNY_MSG_VIEW (priv->msg_view));
3626 if (priv->other_body) {
3629 description = modest_tny_mime_part_get_header_value (priv->other_body, "Content-Description");
3631 g_strstrip (description);
3632 subject = description;
3634 } else if (msg != NULL) {
3635 header = tny_msg_get_header (msg);
3636 subject = tny_header_dup_subject (header);
3637 g_object_unref (header);
3638 g_object_unref (msg);
3641 if ((subject == NULL)||(subject[0] == '\0')) {
3643 subject = g_strdup (_("mail_va_no_subject"));
3646 modest_window_set_title (MODEST_WINDOW (window), subject);
3651 on_move_focus (GtkWidget *widget,
3652 GtkDirectionType direction,
3655 g_signal_stop_emission_by_name (G_OBJECT (widget), "move-focus");
3659 fetch_image_open_stream (TnyStreamCache *self, gint64 *expected_size, gchar *uri)
3661 GnomeVFSResult result;
3662 GnomeVFSHandle *handle = NULL;
3663 GnomeVFSFileInfo *info = NULL;
3666 result = gnome_vfs_open (&handle, uri, GNOME_VFS_OPEN_READ);
3667 if (result != GNOME_VFS_OK) {
3672 info = gnome_vfs_file_info_new ();
3673 result = gnome_vfs_get_file_info_from_handle (handle, info, GNOME_VFS_FILE_INFO_DEFAULT);
3674 if (result != GNOME_VFS_OK || ! (info->valid_fields & GNOME_VFS_FILE_INFO_FIELDS_SIZE)) {
3675 /* We put a "safe" default size for going to cache */
3676 *expected_size = (300*1024);
3678 *expected_size = info->size;
3680 gnome_vfs_file_info_unref (info);
3682 stream = tny_vfs_stream_new (handle);
3691 TnyStream *output_stream;
3692 GtkWidget *msg_view;
3697 on_fetch_image_timeout_refresh_view (gpointer userdata)
3699 ModestMsgViewWindowPrivate *priv;
3701 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (userdata);
3702 update_progress_hint (MODEST_MSG_VIEW_WINDOW (userdata));
3703 if (GTK_WIDGET_DRAWABLE (priv->msg_view)) {
3704 gtk_widget_queue_draw (GTK_WIDGET (priv->msg_view));
3706 priv->fetch_image_redraw_handler = 0;
3707 g_object_unref (userdata);
3712 on_fetch_image_idle_refresh_view (gpointer userdata)
3715 FetchImageData *fidata = (FetchImageData *) userdata;
3717 gdk_threads_enter ();
3718 if (GTK_WIDGET_DRAWABLE (fidata->msg_view)) {
3719 ModestMsgViewWindowPrivate *priv;
3721 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (fidata->window);
3722 priv->fetching_images--;
3723 if (priv->fetch_image_redraw_handler == 0) {
3724 priv->fetch_image_redraw_handler = g_timeout_add (500, on_fetch_image_timeout_refresh_view, g_object_ref (fidata->window));
3728 gdk_threads_leave ();
3730 g_object_unref (fidata->msg_view);
3731 g_object_unref (fidata->window);
3732 g_slice_free (FetchImageData, fidata);
3737 on_fetch_image_thread (gpointer userdata)
3739 FetchImageData *fidata = (FetchImageData *) userdata;
3740 TnyStreamCache *cache;
3741 TnyStream *cache_stream;
3743 cache = modest_runtime_get_images_cache ();
3745 tny_stream_cache_get_stream (cache,
3747 (TnyStreamCacheOpenStreamFetcher) fetch_image_open_stream,
3748 (gpointer) fidata->uri);
3749 g_free (fidata->cache_id);
3750 g_free (fidata->uri);
3752 if (cache_stream != NULL) {
3755 while (G_LIKELY (!tny_stream_is_eos (cache_stream))) {
3758 nb_read = tny_stream_read (cache_stream, buffer, sizeof (buffer));
3759 if (G_UNLIKELY (nb_read < 0)) {
3761 } else if (G_LIKELY (nb_read > 0)) {
3762 gssize nb_written = 0;
3764 while (G_UNLIKELY (nb_written < nb_read)) {
3767 len = tny_stream_write (fidata->output_stream, buffer + nb_written,
3768 nb_read - nb_written);
3769 if (G_UNLIKELY (len < 0))
3775 tny_stream_close (cache_stream);
3776 g_object_unref (cache_stream);
3779 tny_stream_close (fidata->output_stream);
3780 g_object_unref (fidata->output_stream);
3782 g_idle_add (on_fetch_image_idle_refresh_view, fidata);
3788 on_fetch_image (ModestMsgView *msgview,
3791 ModestMsgViewWindow *window)
3793 const gchar *current_account;
3794 ModestMsgViewWindowPrivate *priv;
3795 FetchImageData *fidata;
3797 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
3799 current_account = modest_window_get_active_account (MODEST_WINDOW (window));
3801 fidata = g_slice_new0 (FetchImageData);
3802 fidata->msg_view = g_object_ref (msgview);
3803 fidata->window = g_object_ref (window);
3804 fidata->uri = g_strdup (uri);
3805 fidata->cache_id = modest_images_cache_get_id (current_account, uri);
3806 fidata->output_stream = g_object_ref (stream);
3808 priv->fetching_images++;
3809 if (g_thread_create (on_fetch_image_thread, fidata, FALSE, NULL) == NULL) {
3810 g_object_unref (fidata->output_stream);
3811 g_free (fidata->cache_id);
3812 g_free (fidata->uri);
3813 g_object_unref (fidata->msg_view);
3814 g_slice_free (FetchImageData, fidata);
3815 tny_stream_close (stream);
3816 priv->fetching_images--;
3817 update_progress_hint (window);
3820 update_progress_hint (window);
3826 setup_menu (ModestMsgViewWindow *self)
3828 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW(self));
3830 /* Settings menu buttons */
3831 modest_window_add_to_menu (MODEST_WINDOW (self), _("mcen_me_viewer_find"), NULL,
3832 MODEST_WINDOW_MENU_CALLBACK (modest_msg_view_window_show_isearch_toolbar),
3833 MODEST_DIMMING_CALLBACK (modest_ui_dimming_rules_on_find_in_msg));
3835 modest_window_add_to_menu (MODEST_WINDOW (self),
3836 dngettext(GETTEXT_PACKAGE,
3837 "mcen_me_move_message",
3838 "mcen_me_move_messages",
3841 MODEST_WINDOW_MENU_CALLBACK (modest_ui_actions_on_move_to),
3842 MODEST_DIMMING_CALLBACK (modest_ui_dimming_rules_on_move_to));
3844 modest_window_add_to_menu (MODEST_WINDOW (self), _("mcen_me_inbox_mark_as_read"), NULL,
3845 MODEST_WINDOW_MENU_CALLBACK (modest_ui_actions_on_mark_as_read),
3846 MODEST_DIMMING_CALLBACK (modest_ui_dimming_rules_on_mark_as_read_msg_in_view));
3848 modest_window_add_to_menu (MODEST_WINDOW (self), _("mcen_me_inbox_mark_as_unread"), NULL,
3849 MODEST_WINDOW_MENU_CALLBACK (modest_ui_actions_on_mark_as_unread),
3850 MODEST_DIMMING_CALLBACK (modest_ui_dimming_rules_on_mark_as_unread_msg_in_view));
3852 modest_window_add_to_menu (MODEST_WINDOW (self), _("mcen_me_viewer_save_attachments"), NULL,
3853 MODEST_WINDOW_MENU_CALLBACK (modest_ui_actions_save_attachments),
3854 MODEST_DIMMING_CALLBACK (modest_ui_dimming_rules_on_save_attachments));
3855 modest_window_add_to_menu (MODEST_WINDOW (self), _("mcen_me_inbox_remove_attachments"), NULL,
3856 MODEST_WINDOW_MENU_CALLBACK (modest_ui_actions_remove_attachments),
3857 MODEST_DIMMING_CALLBACK (modest_ui_dimming_rules_on_remove_attachments));
3859 modest_window_add_to_menu (MODEST_WINDOW (self), _("mcen_me_new_message"), "<Control>n",
3860 MODEST_WINDOW_MENU_CALLBACK (modest_ui_actions_on_new_msg),
3861 MODEST_DIMMING_CALLBACK (modest_ui_dimming_rules_on_new_msg));
3862 modest_window_add_to_menu (MODEST_WINDOW (self), _("mcen_me_viewer_addtocontacts"), NULL,
3863 MODEST_WINDOW_MENU_CALLBACK (modest_ui_actions_add_to_contacts),
3864 MODEST_DIMMING_CALLBACK (modest_ui_dimming_rules_on_add_to_contacts));
3866 modest_window_add_to_menu (MODEST_WINDOW (self), _("mcen_ti_message_properties"), NULL,
3867 MODEST_WINDOW_MENU_CALLBACK (modest_ui_actions_on_details),
3868 MODEST_DIMMING_CALLBACK (modest_ui_dimming_rules_on_details));
3872 modest_msg_view_window_add_to_contacts (ModestMsgViewWindow *self)
3874 ModestMsgViewWindowPrivate *priv;
3875 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
3876 GSList *recipients = NULL;
3879 msg = tny_msg_view_get_msg (TNY_MSG_VIEW (priv->msg_view));
3883 header = modest_msg_view_window_get_header (self);
3886 recipients = modest_tny_msg_header_get_all_recipients_list (header);
3887 g_object_unref (header);
3889 recipients = modest_tny_msg_get_all_recipients_list (msg);
3890 g_object_unref (msg);
3894 /* Offer the user to add recipients to the address book */
3895 modest_address_book_add_address_list_with_selector (recipients, (GtkWindow *) self);
3896 g_slist_foreach (recipients, (GFunc) g_free, NULL); g_slist_free (recipients);
3901 _modest_msg_view_window_map_event (GtkWidget *widget,
3905 ModestMsgViewWindow *self = (ModestMsgViewWindow *) userdata;
3907 update_progress_hint (self);
3913 modest_msg_view_window_fetch_images (ModestMsgViewWindow *self)
3915 ModestMsgViewWindowPrivate *priv;
3916 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
3918 modest_msg_view_request_fetch_images (MODEST_MSG_VIEW (priv->msg_view));
3922 modest_msg_view_window_has_blocked_external_images (ModestMsgViewWindow *self)
3924 ModestMsgViewWindowPrivate *priv;
3925 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
3927 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self), FALSE);
3929 return modest_msg_view_has_blocked_external_images (MODEST_MSG_VIEW (priv->msg_view));
3933 modest_msg_view_window_reload (ModestMsgViewWindow *self)
3935 ModestMsgViewWindowPrivate *priv;
3936 const gchar *msg_uid;
3937 TnyHeader *header = NULL;
3938 TnyFolder *folder = NULL;
3940 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self));
3942 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
3944 header = modest_msg_view_window_get_header (MODEST_MSG_VIEW_WINDOW (self));
3948 folder = tny_header_get_folder (header);
3949 g_object_unref (header);
3954 msg_uid = modest_msg_view_window_get_message_uid (self);
3956 GtkTreeRowReference *row_reference;
3958 if (priv->row_reference && gtk_tree_row_reference_valid (priv->row_reference)) {
3959 row_reference = priv->row_reference;
3961 row_reference = NULL;
3963 if (!message_reader (self, priv, NULL, msg_uid, folder, row_reference))
3964 g_warning ("Shouldn't happen, trying to reload a message failed");
3967 g_object_unref (folder);
3971 update_branding (ModestMsgViewWindow *self)
3973 const gchar *account;
3974 const gchar *mailbox;
3975 ModestAccountMgr *mgr;
3976 ModestProtocol *protocol = NULL;
3977 gchar *service_name = NULL;
3978 const GdkPixbuf *service_icon = NULL;
3979 ModestMsgViewWindowPrivate *priv;
3981 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
3983 account = modest_window_get_active_account (MODEST_WINDOW (self));
3984 mailbox = modest_window_get_active_mailbox (MODEST_WINDOW (self));
3986 mgr = modest_runtime_get_account_mgr ();
3988 if (modest_account_mgr_account_is_multimailbox (mgr, account, &protocol)) {
3989 if (MODEST_IS_ACCOUNT_PROTOCOL (protocol)) {
3990 service_name = modest_account_protocol_get_service_name (MODEST_ACCOUNT_PROTOCOL (protocol),
3992 service_icon = modest_account_protocol_get_service_icon (MODEST_ACCOUNT_PROTOCOL (protocol),
3993 account, mailbox, MODEST_ICON_SIZE_SMALL);
3997 modest_msg_view_set_branding (MODEST_MSG_VIEW (priv->msg_view), service_name, service_icon);
3998 g_free (service_name);
4002 sync_flags (ModestMsgViewWindow *self)
4004 TnyHeader *header = NULL;
4006 header = modest_msg_view_window_get_header (self);
4008 TnyMsg *msg = modest_msg_view_window_get_message (self);
4010 header = tny_msg_get_header (msg);
4011 g_object_unref (msg);
4016 TnyFolder *folder = tny_header_get_folder (header);
4019 ModestMailOperation *mail_op;
4021 /* Sync folder, we need this to save the seen flag */
4022 mail_op = modest_mail_operation_new (NULL);
4023 modest_mail_operation_queue_add (modest_runtime_get_mail_operation_queue (),
4025 modest_mail_operation_sync_folder (mail_op, folder, FALSE, NULL, NULL);
4026 g_object_unref (mail_op);
4027 g_object_unref (folder);
4029 g_object_unref (header);