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 GtkWindow *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;
1986 response = modest_platform_run_confirmation_dialog (GTK_WINDOW (window),
1987 _("mcen_nc_get_msg"));
1988 if (response == GTK_RESPONSE_CANCEL) {
1989 update_window_title (window);
1994 folder = tny_header_get_folder (header);
1996 info = g_slice_new (MsgReaderInfo);
1997 info->msg_uid = g_strdup (msg_uid);
1999 info->header = g_object_ref (header);
2001 info->header = NULL;
2003 info->folder = g_object_ref (folder);
2005 info->folder = NULL;
2006 if (row_reference) {
2007 info->row_reference = gtk_tree_row_reference_copy (row_reference);
2009 info->row_reference = NULL;
2012 /* Offer the connection dialog if necessary */
2013 modest_platform_connect_if_remote_and_perform ((GtkWindow *) window,
2015 TNY_FOLDER_STORE (folder),
2016 message_reader_performer,
2019 g_object_unref (folder);
2025 folder = tny_header_get_folder (header);
2028 account = tny_folder_get_account (folder);
2030 info = g_slice_new (MsgReaderInfo);
2031 info->msg_uid = g_strdup (msg_uid);
2033 info->folder = g_object_ref (folder);
2035 info->folder = NULL;
2037 info->header = g_object_ref (header);
2039 info->header = NULL;
2041 info->row_reference = gtk_tree_row_reference_copy (row_reference);
2043 info->row_reference = NULL;
2045 message_reader_performer (FALSE, NULL, (GtkWindow *) window, account, info);
2047 g_object_unref (account);
2049 g_object_unref (folder);
2055 modest_msg_view_window_select_next_message (ModestMsgViewWindow *window)
2057 ModestMsgViewWindowPrivate *priv;
2058 GtkTreePath *path= NULL;
2059 GtkTreeIter tmp_iter;
2061 gboolean retval = TRUE;
2062 GtkTreeRowReference *row_reference = NULL;
2064 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), FALSE);
2065 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
2067 if (!priv->row_reference)
2070 /* Update the next row reference if it's not valid. This could
2071 happen if for example the header which it was pointing to,
2072 was deleted. The best place to do it is in the row-deleted
2073 handler but the tinymail model do not work like the glib
2074 tree models and reports the deletion when the row is still
2076 if (!gtk_tree_row_reference_valid (priv->next_row_reference)) {
2077 if (priv->next_row_reference) {
2078 gtk_tree_row_reference_free (priv->next_row_reference);
2080 if (gtk_tree_row_reference_valid (priv->row_reference)) {
2081 priv->next_row_reference = gtk_tree_row_reference_copy (priv->row_reference);
2082 select_next_valid_row (priv->header_model, &(priv->next_row_reference), FALSE, priv->is_outbox);
2084 priv->next_row_reference = NULL;
2087 if (priv->next_row_reference)
2088 path = gtk_tree_row_reference_get_path (priv->next_row_reference);
2092 row_reference = gtk_tree_row_reference_copy (priv->next_row_reference);
2094 gtk_tree_model_get_iter (priv->header_model,
2097 gtk_tree_path_free (path);
2099 gtk_tree_model_get (priv->header_model, &tmp_iter,
2100 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
2103 /* Read the message & show it */
2104 if (!message_reader (window, priv, header, NULL, NULL, row_reference)) {
2107 gtk_tree_row_reference_free (row_reference);
2110 g_object_unref (header);
2116 modest_msg_view_window_select_previous_message (ModestMsgViewWindow *window)
2118 ModestMsgViewWindowPrivate *priv = NULL;
2120 gboolean finished = FALSE;
2121 gboolean retval = FALSE;
2123 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), FALSE);
2124 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
2126 if (priv->row_reference && !gtk_tree_row_reference_valid (priv->row_reference)) {
2127 gtk_tree_row_reference_free (priv->row_reference);
2128 priv->row_reference = NULL;
2131 /* Return inmediatly if there is no header model */
2132 if (!priv->header_model || !priv->row_reference)
2135 path = gtk_tree_row_reference_get_path (priv->row_reference);
2136 while (!finished && gtk_tree_path_prev (path)) {
2140 gtk_tree_model_get_iter (priv->header_model, &iter, path);
2141 gtk_tree_model_get (priv->header_model, &iter,
2142 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
2146 if (msg_is_visible (header, priv->is_outbox)) {
2147 GtkTreeRowReference *row_reference;
2148 row_reference = gtk_tree_row_reference_new (priv->header_model, path);
2149 /* Read the message & show it */
2150 retval = message_reader (window, priv, header, NULL, NULL, row_reference);
2151 gtk_tree_row_reference_free (row_reference);
2155 g_object_unref (header);
2159 gtk_tree_path_free (path);
2164 view_msg_cb (ModestMailOperation *mail_op,
2171 ModestMsgViewWindow *self = NULL;
2172 ModestMsgViewWindowPrivate *priv = NULL;
2173 GtkTreeRowReference *row_reference = NULL;
2175 /* Unregister the header (it was registered before creating the mail operation) */
2176 modest_window_mgr_unregister_header (modest_runtime_get_window_mgr (), header);
2178 row_reference = (GtkTreeRowReference *) user_data;
2181 gtk_tree_row_reference_free (row_reference);
2182 self = (ModestMsgViewWindow *) modest_mail_operation_get_source (mail_op);
2184 /* Restore window title */
2185 update_window_title (self);
2186 modest_ui_actions_on_close_window (NULL, MODEST_WINDOW (self));
2187 g_object_unref (self);
2192 /* If there was any error */
2193 if (!modest_ui_actions_msg_retrieval_check (mail_op, header, msg)) {
2195 gtk_tree_row_reference_free (row_reference);
2196 self = (ModestMsgViewWindow *) modest_mail_operation_get_source (mail_op);
2198 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
2199 /* First we check if the parent is a folder window */
2200 if (priv->msg_uid && !modest_window_mgr_get_folder_window (MODEST_WINDOW_MGR (modest_runtime_get_window_mgr ()))) {
2202 TnyAccount *account = NULL;
2203 GtkWidget *header_window = NULL;
2205 is_merge = g_str_has_prefix (priv->msg_uid, "merge:");
2207 /* Get the account */
2209 account = tny_account_store_find_account (TNY_ACCOUNT_STORE (modest_runtime_get_account_store ()),
2212 if (is_merge || account) {
2213 TnyFolder *folder = NULL;
2215 /* Try to get the message, if it's already downloaded
2216 we don't need to connect */
2218 folder = modest_tny_folder_store_find_folder_from_uri (TNY_FOLDER_STORE (account),
2221 ModestTnyAccountStore *account_store;
2222 ModestTnyLocalFoldersAccount *local_folders_account;
2224 account_store = modest_runtime_get_account_store ();
2225 local_folders_account = MODEST_TNY_LOCAL_FOLDERS_ACCOUNT (
2226 modest_tny_account_store_get_local_folders_account (account_store));
2227 folder = modest_tny_local_folders_account_get_merged_outbox (local_folders_account);
2228 g_object_unref (local_folders_account);
2230 if (account) g_object_unref (account);
2233 header_window = (GtkWidget *)
2234 modest_header_window_new (
2236 modest_window_get_active_account (MODEST_WINDOW (self)),
2237 modest_window_get_active_mailbox (MODEST_WINDOW (self)));
2238 if (!modest_window_mgr_register_window (modest_runtime_get_window_mgr (),
2239 MODEST_WINDOW (header_window),
2241 gtk_widget_destroy (GTK_WIDGET (header_window));
2243 gtk_widget_show_all (GTK_WIDGET (header_window));
2245 g_object_unref (folder);
2251 /* Restore window title */
2252 update_window_title (self);
2253 modest_ui_actions_on_close_window (NULL, MODEST_WINDOW (self));
2254 g_object_unref (self);
2259 if (msg && TNY_IS_CAMEL_BS_MSG (msg)) {
2261 body = modest_tny_msg_find_body_part (msg, TRUE);
2263 if (body && !tny_camel_bs_mime_part_is_fetched (TNY_CAMEL_BS_MIME_PART (body))) {
2264 /* We have body structure but not the body mime part. What we do
2265 * is restarting load of message */
2266 self = (ModestMsgViewWindow *) modest_mail_operation_get_source (mail_op);
2267 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
2269 tny_header_unset_flag (TNY_HEADER (header), TNY_HEADER_FLAG_CACHED);
2271 modest_msg_view_window_reload (self);
2274 gtk_tree_row_reference_free (row_reference);
2275 g_object_unref (body);
2280 g_object_unref (body);
2283 /* Get the window */
2284 self = (ModestMsgViewWindow *) modest_mail_operation_get_source (mail_op);
2285 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self));
2286 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
2288 /* Update the row reference */
2289 if (priv->row_reference != NULL) {
2290 gtk_tree_row_reference_free (priv->row_reference);
2291 priv->row_reference = (row_reference && gtk_tree_row_reference_valid (row_reference))?gtk_tree_row_reference_copy (row_reference):NULL;
2292 if (priv->next_row_reference != NULL) {
2293 gtk_tree_row_reference_free (priv->next_row_reference);
2295 if (priv->row_reference) {
2296 priv->next_row_reference = gtk_tree_row_reference_copy (priv->row_reference);
2297 select_next_valid_row (priv->header_model, &(priv->next_row_reference), TRUE, priv->is_outbox);
2299 priv->next_row_reference = NULL;
2303 /* Mark header as read */
2304 if (!(tny_header_get_flags (header) & TNY_HEADER_FLAG_SEEN))
2305 tny_header_set_flag (header, TNY_HEADER_FLAG_SEEN);
2307 /* Set new message */
2308 if (priv->msg_view != NULL && TNY_IS_MSG_VIEW (priv->msg_view)) {
2309 tny_msg_view_set_msg (TNY_MSG_VIEW (priv->msg_view), msg);
2310 modest_msg_view_window_update_priority (self);
2311 update_window_title (MODEST_MSG_VIEW_WINDOW (self));
2312 update_branding (MODEST_MSG_VIEW_WINDOW (self));
2313 modest_msg_view_grab_focus (MODEST_MSG_VIEW (priv->msg_view));
2316 /* Set the new message uid of the window */
2317 if (priv->msg_uid) {
2318 g_free (priv->msg_uid);
2319 priv->msg_uid = modest_tny_folder_get_header_unique_id (header);
2322 /* Notify the observers */
2323 g_signal_emit (G_OBJECT (self), signals[MSG_CHANGED_SIGNAL],
2324 0, priv->header_model, priv->row_reference);
2326 /* Sync the flags if the message is not opened from a header
2327 model, i.e, if it's opened from a notification */
2328 if (!priv->header_model)
2332 g_object_unref (self);
2334 gtk_tree_row_reference_free (row_reference);
2338 modest_msg_view_window_get_folder_type (ModestMsgViewWindow *window)
2340 ModestMsgViewWindowPrivate *priv;
2342 TnyFolderType folder_type;
2344 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
2346 folder_type = TNY_FOLDER_TYPE_UNKNOWN;
2348 msg = tny_msg_view_get_msg (TNY_MSG_VIEW (priv->msg_view));
2352 folder = tny_msg_get_folder (msg);
2354 folder_type = modest_tny_folder_guess_folder_type (folder);
2355 g_object_unref (folder);
2357 g_object_unref (msg);
2365 modest_msg_view_window_update_priority (ModestMsgViewWindow *window)
2367 ModestMsgViewWindowPrivate *priv;
2368 TnyHeader *header = NULL;
2369 TnyHeaderFlags flags = 0;
2371 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
2373 if (priv->header_model && priv->row_reference && gtk_tree_row_reference_valid (priv->row_reference)) {
2375 GtkTreePath *path = NULL;
2377 path = gtk_tree_row_reference_get_path (priv->row_reference);
2378 g_return_if_fail (path != NULL);
2379 gtk_tree_model_get_iter (priv->header_model,
2381 gtk_tree_row_reference_get_path (priv->row_reference));
2383 gtk_tree_model_get (priv->header_model, &iter, TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
2385 gtk_tree_path_free (path);
2388 msg = tny_msg_view_get_msg (TNY_MSG_VIEW (priv->msg_view));
2390 header = tny_msg_get_header (msg);
2391 g_object_unref (msg);
2396 flags = tny_header_get_flags (header);
2397 g_object_unref(G_OBJECT(header));
2400 modest_msg_view_set_priority (MODEST_MSG_VIEW(priv->msg_view), flags);
2405 toolbar_resize (ModestMsgViewWindow *self)
2407 ModestMsgViewWindowPrivate *priv = NULL;
2408 ModestWindowPrivate *parent_priv = NULL;
2410 gint static_button_size;
2411 ModestWindowMgr *mgr;
2413 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self));
2414 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
2415 parent_priv = MODEST_WINDOW_GET_PRIVATE(self);
2417 mgr = modest_runtime_get_window_mgr ();
2418 static_button_size = modest_window_mgr_get_fullscreen_mode (mgr)?120:120;
2420 if (parent_priv->toolbar) {
2421 /* Set expandable and homogeneous tool buttons */
2422 widget = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/ToolbarMessageReply");
2423 gtk_tool_item_set_expand (GTK_TOOL_ITEM (widget), TRUE);
2424 gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (widget), TRUE);
2425 widget = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/ToolbarMessageReplyAll");
2426 gtk_tool_item_set_expand (GTK_TOOL_ITEM (widget), TRUE);
2427 gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (widget), TRUE);
2428 widget = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/ToolbarMessageForward");
2429 gtk_tool_item_set_expand (GTK_TOOL_ITEM (widget), TRUE);
2430 gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (widget), TRUE);
2431 widget = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/ToolbarDeleteMessage");
2432 gtk_tool_item_set_expand (GTK_TOOL_ITEM (widget), TRUE);
2433 gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (widget), TRUE);
2434 widget = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/ToolbarDownloadExternalImages");
2435 gtk_tool_item_set_expand (GTK_TOOL_ITEM (widget), TRUE);
2436 gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (widget), TRUE);
2437 gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (priv->next_toolitem), TRUE);
2438 gtk_tool_item_set_expand (GTK_TOOL_ITEM (priv->next_toolitem), TRUE);
2439 gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (priv->prev_toolitem), TRUE);
2440 gtk_tool_item_set_expand (GTK_TOOL_ITEM (priv->prev_toolitem), TRUE);
2445 modest_msg_view_window_show_toolbar (ModestWindow *self,
2446 gboolean show_toolbar)
2448 ModestMsgViewWindowPrivate *priv = NULL;
2449 ModestWindowPrivate *parent_priv;
2451 parent_priv = MODEST_WINDOW_GET_PRIVATE(self);
2452 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(self);
2454 /* Set optimized view status */
2455 priv->optimized_view = !show_toolbar;
2457 if (!parent_priv->toolbar) {
2458 parent_priv->toolbar = gtk_ui_manager_get_widget (parent_priv->ui_manager,
2461 #ifdef MODEST_TOOLKIT_HILDON2
2462 gtk_toolbar_set_icon_size (GTK_TOOLBAR (parent_priv->toolbar), HILDON_ICON_SIZE_FINGER);
2464 gtk_toolbar_set_icon_size (GTK_TOOLBAR (parent_priv->toolbar), GTK_ICON_SIZE_LARGE_TOOLBAR);
2466 gtk_widget_set_no_show_all (parent_priv->toolbar, TRUE);
2468 priv->next_toolitem = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/ToolbarMessageNext");
2469 priv->prev_toolitem = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/ToolbarMessageBack");
2470 toolbar_resize (MODEST_MSG_VIEW_WINDOW (self));
2472 modest_window_add_toolbar (MODEST_WINDOW (self),
2473 GTK_TOOLBAR (parent_priv->toolbar));
2478 /* Quick hack: this prevents toolbar icons "dance" when progress bar show status is changed */
2479 /* TODO: resize mode migth be GTK_RESIZE_QUEUE, in order to avoid unneccesary shows */
2480 gtk_container_set_resize_mode (GTK_CONTAINER(parent_priv->toolbar), GTK_RESIZE_IMMEDIATE);
2482 gtk_widget_show (GTK_WIDGET (parent_priv->toolbar));
2483 if (modest_msg_view_window_transfer_mode_enabled (MODEST_MSG_VIEW_WINDOW (self)))
2484 set_progress_hint (MODEST_MSG_VIEW_WINDOW (self), TRUE);
2486 set_progress_hint (MODEST_MSG_VIEW_WINDOW (self), FALSE);
2489 gtk_widget_set_no_show_all (parent_priv->toolbar, TRUE);
2490 gtk_widget_hide (GTK_WIDGET (parent_priv->toolbar));
2495 modest_msg_view_window_clipboard_owner_change (GtkClipboard *clipboard,
2497 ModestMsgViewWindow *window)
2499 if (!GTK_WIDGET_VISIBLE (window))
2502 modest_window_check_dimming_rules_group (MODEST_WINDOW (window), MODEST_DIMMING_RULES_CLIPBOARD);
2506 modest_msg_view_window_transfer_mode_enabled (ModestMsgViewWindow *self)
2508 ModestMsgViewWindowPrivate *priv;
2510 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self), FALSE);
2511 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(self);
2513 return priv->progress_hint;
2517 observers_empty (ModestMsgViewWindow *self)
2520 ModestMsgViewWindowPrivate *priv;
2521 gboolean is_empty = TRUE;
2522 guint pending_ops = 0;
2524 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(self);
2525 tmp = priv->progress_widgets;
2527 /* Check all observers */
2528 while (tmp && is_empty) {
2529 pending_ops = modest_progress_object_num_pending_operations (MODEST_PROGRESS_OBJECT(tmp->data));
2530 is_empty = pending_ops == 0;
2532 tmp = g_slist_next(tmp);
2539 on_account_removed (TnyAccountStore *account_store,
2540 TnyAccount *account,
2543 /* Do nothing if it's a transport account, because we only
2544 show the messages of a store account */
2545 if (tny_account_get_account_type(account) == TNY_ACCOUNT_TYPE_STORE) {
2546 const gchar *parent_acc = NULL;
2547 const gchar *our_acc = NULL;
2549 our_acc = modest_window_get_active_account (MODEST_WINDOW (user_data));
2550 parent_acc = modest_tny_account_get_parent_modest_account_name_for_server_account (account);
2552 /* Close this window if I'm showing a message of the removed account */
2553 if (our_acc && parent_acc && strcmp (parent_acc, our_acc) == 0)
2554 modest_ui_actions_on_close_window (NULL, MODEST_WINDOW (user_data));
2559 on_mail_operation_started (ModestMailOperation *mail_op,
2562 ModestMsgViewWindow *self;
2563 ModestMailOperationTypeOperation op_type;
2565 ModestMsgViewWindowPrivate *priv;
2566 GObject *source = NULL;
2568 self = MODEST_MSG_VIEW_WINDOW (user_data);
2569 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
2570 op_type = modest_mail_operation_get_type_operation (mail_op);
2571 tmp = priv->progress_widgets;
2572 source = modest_mail_operation_get_source(mail_op);
2573 if (G_OBJECT (self) == source) {
2574 if (op_type == MODEST_MAIL_OPERATION_TYPE_RECEIVE ||
2575 op_type == MODEST_MAIL_OPERATION_TYPE_OPEN ||
2576 op_type == MODEST_MAIL_OPERATION_TYPE_DELETE) {
2577 set_progress_hint (self, TRUE);
2579 modest_progress_object_add_operation (
2580 MODEST_PROGRESS_OBJECT (tmp->data),
2582 tmp = g_slist_next (tmp);
2586 g_object_unref (source);
2588 /* Update dimming rules */
2589 check_dimming_rules_after_change (self);
2593 on_mail_operation_finished (ModestMailOperation *mail_op,
2596 ModestMsgViewWindow *self;
2597 ModestMailOperationTypeOperation op_type;
2599 ModestMsgViewWindowPrivate *priv;
2601 self = MODEST_MSG_VIEW_WINDOW (user_data);
2602 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
2603 op_type = modest_mail_operation_get_type_operation (mail_op);
2604 tmp = priv->progress_widgets;
2606 if (op_type == MODEST_MAIL_OPERATION_TYPE_RECEIVE ||
2607 op_type == MODEST_MAIL_OPERATION_TYPE_OPEN ||
2608 op_type == MODEST_MAIL_OPERATION_TYPE_DELETE) {
2610 modest_progress_object_remove_operation (MODEST_PROGRESS_OBJECT (tmp->data),
2612 tmp = g_slist_next (tmp);
2615 /* If no more operations are being observed, NORMAL mode is enabled again */
2616 if (observers_empty (self)) {
2617 set_progress_hint (self, FALSE);
2621 /* Update dimming rules. We have to do this right here
2622 and not in view_msg_cb because at that point the
2623 transfer mode is still enabled so the dimming rule
2624 won't let the user delete the message that has been
2625 readed for example */
2626 check_dimming_rules_after_change (self);
2630 on_queue_changed (ModestMailOperationQueue *queue,
2631 ModestMailOperation *mail_op,
2632 ModestMailOperationQueueNotification type,
2633 ModestMsgViewWindow *self)
2635 ModestMsgViewWindowPrivate *priv;
2637 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
2639 /* If this operations was created by another window, do nothing */
2640 if (!modest_mail_operation_is_mine (mail_op, G_OBJECT(self)))
2643 if (type == MODEST_MAIL_OPERATION_QUEUE_OPERATION_ADDED) {
2644 priv->sighandlers = modest_signal_mgr_connect (priv->sighandlers,
2646 "operation-started",
2647 G_CALLBACK (on_mail_operation_started),
2649 priv->sighandlers = modest_signal_mgr_connect (priv->sighandlers,
2651 "operation-finished",
2652 G_CALLBACK (on_mail_operation_finished),
2654 } else if (type == MODEST_MAIL_OPERATION_QUEUE_OPERATION_REMOVED) {
2655 priv->sighandlers = modest_signal_mgr_disconnect (priv->sighandlers,
2657 "operation-started");
2658 priv->sighandlers = modest_signal_mgr_disconnect (priv->sighandlers,
2660 "operation-finished");
2665 modest_msg_view_window_get_attachments (ModestMsgViewWindow *win)
2667 ModestMsgViewWindowPrivate *priv;
2668 TnyList *selected_attachments = NULL;
2670 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (win), NULL);
2671 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (win);
2673 /* In Hildon 2.2 as there's no selection we assume we have all attachments selected */
2674 selected_attachments = modest_msg_view_get_attachments (MODEST_MSG_VIEW (priv->msg_view));
2676 return selected_attachments;
2680 ModestMsgViewWindow *self;
2682 gchar *attachment_uid;
2683 } DecodeAsyncHelper;
2686 on_decode_to_stream_async_handler (TnyMimePart *mime_part,
2692 DecodeAsyncHelper *helper = (DecodeAsyncHelper *) user_data;
2693 const gchar *content_type;
2695 if (cancelled || err) {
2698 if ((err->domain == TNY_ERROR_DOMAIN) &&
2699 (err->code == TNY_IO_ERROR_WRITE) &&
2700 (errno == ENOSPC)) {
2701 msg = g_strdup_printf (_KR("cerm_device_memory_full"), "");
2703 msg = g_strdup (_("mail_ib_file_operation_failed"));
2705 modest_platform_information_banner (NULL, NULL, msg);
2711 /* It could happen that the window was closed. So we
2712 assume it is a cancelation */
2713 if (!GTK_WIDGET_VISIBLE (helper->self))
2716 /* Remove the progress hint */
2717 set_progress_hint (helper->self, FALSE);
2719 content_type = tny_mime_part_get_content_type (mime_part);
2720 if (g_str_has_prefix (content_type, "message/rfc822")) {
2721 ModestWindowMgr *mgr;
2722 ModestWindow *msg_win = NULL;
2725 const gchar *mailbox;
2726 TnyStream *file_stream;
2729 fd = g_open (helper->file_path, O_RDONLY, 0644);
2731 file_stream = tny_fs_stream_new (fd);
2733 mgr = modest_runtime_get_window_mgr ();
2735 account = g_strdup (modest_window_get_active_account (MODEST_WINDOW (helper->self)));
2736 mailbox = modest_window_get_active_mailbox (MODEST_WINDOW (helper->self));
2739 account = modest_account_mgr_get_default_account (modest_runtime_get_account_mgr ());
2741 msg = tny_camel_msg_new ();
2742 tny_camel_msg_parse (msg, file_stream);
2743 msg_win = modest_msg_view_window_new_for_attachment (TNY_MSG (msg), account, mailbox, helper->attachment_uid);
2744 modest_window_set_zoom (MODEST_WINDOW (msg_win),
2745 modest_window_get_zoom (MODEST_WINDOW (helper->self)));
2746 if (modest_window_mgr_register_window (mgr, msg_win, MODEST_WINDOW (helper->self)))
2747 gtk_widget_show_all (GTK_WIDGET (msg_win));
2749 gtk_widget_destroy (GTK_WIDGET (msg_win));
2750 g_object_unref (msg);
2751 g_object_unref (file_stream);
2753 modest_platform_information_banner (NULL, NULL, _("mail_ib_file_operation_failed"));
2758 /* make the file read-only */
2759 g_chmod(helper->file_path, 0444);
2761 /* Activate the file */
2762 modest_platform_activate_file (helper->file_path, tny_mime_part_get_content_type (mime_part));
2767 g_object_unref (helper->self);
2768 g_free (helper->file_path);
2769 g_free (helper->attachment_uid);
2770 g_slice_free (DecodeAsyncHelper, helper);
2774 view_attachment_connect_handler (gboolean canceled,
2776 GtkWindow *parent_window,
2777 TnyAccount *account,
2781 if (canceled || err) {
2782 g_object_unref (part);
2786 modest_msg_view_window_view_attachment (MODEST_MSG_VIEW_WINDOW (parent_window),
2788 g_object_unref (part);
2792 modest_msg_view_window_view_attachment (ModestMsgViewWindow *window,
2793 TnyMimePart *mime_part)
2795 ModestMsgViewWindowPrivate *priv;
2796 const gchar *msg_uid;
2797 gchar *attachment_uid = NULL;
2798 gint attachment_index = 0;
2799 TnyList *attachments;
2801 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window));
2802 g_return_if_fail (TNY_IS_MIME_PART (mime_part) || (mime_part == NULL));
2803 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
2805 msg_uid = modest_msg_view_window_get_message_uid (MODEST_MSG_VIEW_WINDOW (window));
2806 attachments = modest_msg_view_get_attachments (MODEST_MSG_VIEW (priv->msg_view));
2807 attachment_index = modest_list_index (attachments, (GObject *) mime_part);
2808 g_object_unref (attachments);
2810 if (msg_uid && attachment_index >= 0) {
2811 attachment_uid = g_strdup_printf ("%s/%d", msg_uid, attachment_index);
2814 if (mime_part == NULL) {
2815 gboolean error = FALSE;
2816 TnyList *selected_attachments = modest_msg_view_get_selected_attachments (MODEST_MSG_VIEW (priv->msg_view));
2817 if (selected_attachments == NULL || tny_list_get_length (selected_attachments) == 0) {
2819 } else if (tny_list_get_length (selected_attachments) > 1) {
2820 modest_platform_system_banner (NULL, NULL, _("mcen_ib_unable_to_display_more"));
2824 iter = tny_list_create_iterator (selected_attachments);
2825 mime_part = (TnyMimePart *) tny_iterator_get_current (iter);
2826 g_object_unref (iter);
2828 if (selected_attachments)
2829 g_object_unref (selected_attachments);
2834 g_object_ref (mime_part);
2837 if (tny_mime_part_is_purged (mime_part))
2840 if (TNY_IS_CAMEL_BS_MIME_PART (mime_part) &&
2841 !tny_camel_bs_mime_part_is_fetched (TNY_CAMEL_BS_MIME_PART (mime_part))) {
2843 TnyAccount *account;
2845 is_merge = g_str_has_prefix (priv->msg_uid, "merge:");
2847 /* Get the account */
2849 account = tny_account_store_find_account (TNY_ACCOUNT_STORE (modest_runtime_get_account_store ()),
2852 if (!tny_device_is_online (modest_runtime_get_device())) {
2853 modest_platform_connect_and_perform (GTK_WINDOW (window),
2855 TNY_ACCOUNT (account),
2856 (ModestConnectedPerformer) view_attachment_connect_handler,
2857 g_object_ref (mime_part));
2862 if (!modest_tny_mime_part_is_msg (mime_part) && tny_mime_part_get_filename (mime_part)) {
2863 gchar *filepath = NULL;
2864 const gchar *att_filename = tny_mime_part_get_filename (mime_part);
2865 gboolean show_error_banner = FALSE;
2866 TnyFsStream *temp_stream = NULL;
2867 temp_stream = modest_utils_create_temp_stream (att_filename, attachment_uid,
2870 if (temp_stream != NULL) {
2871 ModestAccountMgr *mgr;
2872 DecodeAsyncHelper *helper;
2873 gboolean decode_in_provider;
2874 ModestProtocol *protocol;
2875 const gchar *account;
2877 /* Activate progress hint */
2878 set_progress_hint (window, TRUE);
2880 helper = g_slice_new0 (DecodeAsyncHelper);
2881 helper->self = g_object_ref (window);
2882 helper->file_path = g_strdup (filepath);
2883 helper->attachment_uid = g_strdup (attachment_uid);
2885 decode_in_provider = FALSE;
2886 mgr = modest_runtime_get_account_mgr ();
2887 account = modest_window_get_active_account (MODEST_WINDOW (window));
2888 if (modest_account_mgr_account_is_multimailbox (mgr, account, &protocol)) {
2889 if (MODEST_IS_ACCOUNT_PROTOCOL (protocol)) {
2891 uri = g_strconcat ("file://", filepath, NULL);
2892 decode_in_provider =
2893 modest_account_protocol_decode_part_to_stream_async (
2894 MODEST_ACCOUNT_PROTOCOL (protocol),
2897 TNY_STREAM (temp_stream),
2898 on_decode_to_stream_async_handler,
2905 if (!decode_in_provider)
2906 tny_mime_part_decode_to_stream_async (mime_part, TNY_STREAM (temp_stream),
2907 on_decode_to_stream_async_handler,
2910 g_object_unref (temp_stream);
2911 /* NOTE: files in the temporary area will be automatically
2912 * cleaned after some time if they are no longer in use */
2915 const gchar *content_type;
2916 /* the file may already exist but it isn't writable,
2917 * let's try to open it anyway */
2918 content_type = tny_mime_part_get_content_type (mime_part);
2919 modest_platform_activate_file (filepath, content_type);
2921 g_warning ("%s: modest_utils_create_temp_stream failed", __FUNCTION__);
2922 show_error_banner = TRUE;
2927 if (show_error_banner)
2928 modest_platform_information_banner (NULL, NULL, _("mail_ib_file_operation_failed"));
2929 } else if (!modest_tny_mime_part_is_msg (mime_part)) {
2930 ModestWindowMgr *mgr;
2931 ModestWindow *msg_win = NULL;
2932 TnyMsg *current_msg;
2936 current_msg = modest_msg_view_window_get_message (MODEST_MSG_VIEW_WINDOW (window));
2937 mgr = modest_runtime_get_window_mgr ();
2938 header = tny_msg_get_header (TNY_MSG (current_msg));
2939 found = modest_window_mgr_find_registered_message_uid (mgr,
2944 g_debug ("window for this body is already being created");
2947 /* it's not found, so create a new window for it */
2948 modest_window_mgr_register_header (mgr, header, attachment_uid); /* register the uid before building the window */
2949 gchar *account = g_strdup (modest_window_get_active_account (MODEST_WINDOW (window)));
2950 const gchar *mailbox = modest_window_get_active_mailbox (MODEST_WINDOW (window));
2952 account = modest_account_mgr_get_default_account (modest_runtime_get_account_mgr ());
2954 msg_win = modest_msg_view_window_new_with_other_body (TNY_MSG (current_msg), TNY_MIME_PART (mime_part),
2955 account, mailbox, attachment_uid);
2957 modest_window_set_zoom (MODEST_WINDOW (msg_win),
2958 modest_window_get_zoom (MODEST_WINDOW (window)));
2959 if (modest_window_mgr_register_window (mgr, msg_win, MODEST_WINDOW (window)))
2960 gtk_widget_show_all (GTK_WIDGET (msg_win));
2962 gtk_widget_destroy (GTK_WIDGET (msg_win));
2964 g_object_unref (current_msg);
2966 /* message attachment */
2967 TnyHeader *header = NULL;
2968 ModestWindowMgr *mgr;
2969 ModestWindow *msg_win = NULL;
2972 header = tny_msg_get_header (TNY_MSG (mime_part));
2973 mgr = modest_runtime_get_window_mgr ();
2974 found = modest_window_mgr_find_registered_header (mgr, header, &msg_win);
2977 /* if it's found, but there is no msg_win, it's probably in the process of being created;
2978 * thus, we don't do anything */
2979 g_debug ("window for is already being created");
2981 /* it's not found, so create a new window for it */
2982 modest_window_mgr_register_header (mgr, header, attachment_uid); /* register the uid before building the window */
2983 gchar *account = g_strdup (modest_window_get_active_account (MODEST_WINDOW (window)));
2984 const gchar *mailbox = modest_window_get_active_mailbox (MODEST_WINDOW (window));
2986 account = modest_account_mgr_get_default_account (modest_runtime_get_account_mgr ());
2987 msg_win = modest_msg_view_window_new_for_attachment (TNY_MSG (mime_part), account,
2988 mailbox, attachment_uid);
2989 modest_window_set_zoom (MODEST_WINDOW (msg_win),
2990 modest_window_get_zoom (MODEST_WINDOW (window)));
2991 if (modest_window_mgr_register_window (mgr, msg_win, MODEST_WINDOW (window)))
2992 gtk_widget_show_all (GTK_WIDGET (msg_win));
2994 gtk_widget_destroy (GTK_WIDGET (msg_win));
3000 g_free (attachment_uid);
3002 g_object_unref (mime_part);
3014 GnomeVFSResult result;
3016 ModestMsgViewWindow *window;
3019 static void save_mime_part_info_free (SaveMimePartInfo *info, gboolean with_struct);
3020 static gboolean idle_save_mime_part_show_result (SaveMimePartInfo *info);
3021 static gpointer save_mime_part_to_file (SaveMimePartInfo *info);
3022 static void save_mime_parts_to_file_with_checks (GtkWindow *parent, SaveMimePartInfo *info);
3025 save_mime_part_info_free (SaveMimePartInfo *info, gboolean with_struct)
3029 for (node = info->pairs; node != NULL; node = g_list_next (node)) {
3030 SaveMimePartPair *pair = (SaveMimePartPair *) node->data;
3031 g_free (pair->filename);
3032 g_object_unref (pair->part);
3033 g_slice_free (SaveMimePartPair, pair);
3035 g_list_free (info->pairs);
3038 g_object_unref (info->window);
3039 info->window = NULL;
3041 g_slice_free (SaveMimePartInfo, info);
3046 idle_save_mime_part_show_result (SaveMimePartInfo *info)
3048 /* This is a GDK lock because we are an idle callback and
3049 * modest_platform_system_banner is or does Gtk+ code */
3051 gdk_threads_enter (); /* CHECKED */
3052 if (info->result == GNOME_VFS_ERROR_CANCELLED) {
3054 } else if (info->result == GNOME_VFS_OK) {
3055 modest_platform_system_banner (NULL, NULL, _CS_SAVED);
3056 } else if (info->result == GNOME_VFS_ERROR_NO_SPACE) {
3059 /* Check if the uri belongs to the external mmc */
3060 if (g_str_has_prefix (info->uri, g_getenv (MODEST_MMC1_VOLUMEPATH_ENV)))
3061 msg = g_strdup_printf (_KR("cerm_device_memory_full"), "");
3063 msg = g_strdup (_KR("cerm_memory_card_full"));
3064 modest_platform_information_banner (NULL, NULL, msg);
3067 modest_platform_system_banner (NULL, NULL, _("mail_ib_file_operation_failed"));
3069 save_mime_part_info_free (info, FALSE);
3070 gdk_threads_leave (); /* CHECKED */
3076 save_mime_part_to_file_connect_handler (gboolean canceled,
3078 GtkWindow *parent_window,
3079 TnyAccount *account,
3080 SaveMimePartInfo *info)
3082 if (canceled || err) {
3083 if (canceled && !err) {
3084 info->result = GNOME_VFS_ERROR_CANCELLED;
3086 g_idle_add ((GSourceFunc) idle_save_mime_part_show_result, info);
3088 g_thread_create ((GThreadFunc)save_mime_part_to_file, info, FALSE, NULL);
3093 save_mime_part_to_file_connect_idle (SaveMimePartInfo *info)
3096 TnyAccount *account;
3097 ModestMsgViewWindowPrivate *priv;
3099 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (info->window);
3101 is_merge = g_str_has_prefix (priv->msg_uid, "merge:");
3104 /* Get the account */
3106 account = tny_account_store_find_account (TNY_ACCOUNT_STORE (modest_runtime_get_account_store ()),
3109 modest_platform_connect_and_perform (GTK_WINDOW (info->window),
3111 TNY_ACCOUNT (account),
3112 (ModestConnectedPerformer) save_mime_part_to_file_connect_handler,
3118 save_mime_part_to_file (SaveMimePartInfo *info)
3120 GnomeVFSHandle *handle;
3122 SaveMimePartPair *pair = (SaveMimePartPair *) info->pairs->data;
3124 if (TNY_IS_CAMEL_BS_MIME_PART (pair->part) &&
3125 !tny_camel_bs_mime_part_is_fetched (TNY_CAMEL_BS_MIME_PART (pair->part))) {
3126 g_idle_add ((GSourceFunc) save_mime_part_to_file_connect_idle, info);
3130 info->result = gnome_vfs_create (&handle, pair->filename, GNOME_VFS_OPEN_WRITE, FALSE, 0644);
3131 if (info->result == GNOME_VFS_OK) {
3132 GError *error = NULL;
3133 gboolean decode_in_provider;
3135 ModestAccountMgr *mgr;
3136 const gchar *account;
3137 ModestProtocol *protocol = NULL;
3139 stream = tny_vfs_stream_new (handle);
3141 decode_in_provider = FALSE;
3142 mgr = modest_runtime_get_account_mgr ();
3143 account = modest_window_get_active_account (MODEST_WINDOW (info->window));
3144 if (modest_account_mgr_account_is_multimailbox (mgr, account, &protocol)) {
3145 if (MODEST_IS_ACCOUNT_PROTOCOL (protocol)) {
3146 decode_in_provider =
3147 modest_account_protocol_decode_part_to_stream (
3148 MODEST_ACCOUNT_PROTOCOL (protocol),
3156 if (!decode_in_provider)
3157 written = tny_mime_part_decode_to_stream (pair->part, stream, &error);
3160 g_warning ("modest: could not save attachment %s: %d (%s)\n", pair->filename, error?error->code:-1, error?error->message:"Unknown error");
3162 if ((error->domain == TNY_ERROR_DOMAIN) &&
3163 (error->code == TNY_IO_ERROR_WRITE) &&
3164 (errno == ENOSPC)) {
3165 info->result = GNOME_VFS_ERROR_NO_SPACE;
3167 info->result = GNOME_VFS_ERROR_IO;
3170 g_object_unref (G_OBJECT (stream));
3172 g_warning ("Could not create save attachment %s: %s\n",
3173 pair->filename, gnome_vfs_result_to_string (info->result));
3176 /* Go on saving remaining files */
3177 info->pairs = g_list_remove_link (info->pairs, info->pairs);
3178 if (info->pairs != NULL) {
3179 save_mime_part_to_file (info);
3181 g_idle_add ((GSourceFunc) idle_save_mime_part_show_result, info);
3188 save_mime_parts_to_file_with_checks (GtkWindow *parent,
3189 SaveMimePartInfo *info)
3191 gboolean is_ok = TRUE;
3192 gint replaced_files = 0;
3193 const GList *files = info->pairs;
3194 const GList *iter, *to_replace = NULL;
3196 for (iter = files; (iter != NULL) && (replaced_files < 2); iter = g_list_next(iter)) {
3197 SaveMimePartPair *pair = iter->data;
3198 gchar *unescaped = g_uri_unescape_string (pair->filename, NULL);
3200 if (modest_utils_file_exists (unescaped)) {
3202 if (replaced_files == 1)
3207 if (replaced_files) {
3210 if (replaced_files == 1) {
3211 SaveMimePartPair *pair = to_replace->data;
3212 const gchar *basename = strrchr (pair->filename, G_DIR_SEPARATOR) + 1;
3213 gchar *escaped_basename, *message;
3215 escaped_basename = g_uri_unescape_string (basename, NULL);
3216 message = g_strdup_printf ("%s\n%s",
3218 (escaped_basename) ? escaped_basename : "");
3219 response = modest_platform_run_confirmation_dialog (parent, message);
3221 g_free (escaped_basename);
3223 response = modest_platform_run_confirmation_dialog (parent,
3224 _FM_REPLACE_MULTIPLE);
3226 if (response != GTK_RESPONSE_OK)
3231 save_mime_part_info_free (info, TRUE);
3233 g_thread_create ((GThreadFunc)save_mime_part_to_file, info, FALSE, NULL);
3238 typedef struct _SaveAttachmentsInfo {
3239 TnyList *attachments_list;
3240 ModestMsgViewWindow *window;
3241 } SaveAttachmentsInfo;
3244 save_attachments_response (GtkDialog *dialog,
3248 TnyList *mime_parts;
3250 GList *files_to_save = NULL;
3251 gchar *current_folder;
3252 SaveAttachmentsInfo *sa_info = (SaveAttachmentsInfo *) user_data;
3254 mime_parts = TNY_LIST (sa_info->attachments_list);
3256 if (arg1 != GTK_RESPONSE_OK)
3259 chooser_uri = gtk_file_chooser_get_uri (GTK_FILE_CHOOSER (dialog));
3260 current_folder = gtk_file_chooser_get_current_folder_uri (GTK_FILE_CHOOSER (dialog));
3261 if (current_folder && *current_folder != '\0') {
3263 modest_conf_set_string (modest_runtime_get_conf (), MODEST_CONF_LATEST_SAVE_ATTACHMENT_PATH,
3264 current_folder,&err);
3266 g_debug ("Error storing latest used folder: %s", err->message);
3270 g_free (current_folder);
3272 if (!modest_utils_folder_writable (chooser_uri)) {
3273 const gchar *err_msg;
3275 #ifdef MODEST_PLATFORM_MAEMO
3276 if (modest_maemo_utils_in_usb_mode ()) {
3277 err_msg = dgettext ("hildon-status-bar-usb", "usbh_ib_mmc_usb_connected");
3279 err_msg = _FM_READ_ONLY_LOCATION;
3282 err_msg = _FM_READ_ONLY_LOCATION;
3284 modest_platform_system_banner (NULL, NULL, err_msg);
3288 iter = tny_list_create_iterator (mime_parts);
3289 while (!tny_iterator_is_done (iter)) {
3290 TnyMimePart *mime_part = (TnyMimePart *) tny_iterator_get_current (iter);
3292 if ((modest_tny_mime_part_is_attachment_for_modest (mime_part)) &&
3293 !tny_mime_part_is_purged (mime_part) &&
3294 (tny_mime_part_get_filename (mime_part) != NULL)) {
3295 SaveMimePartPair *pair;
3297 pair = g_slice_new0 (SaveMimePartPair);
3299 if (tny_list_get_length (mime_parts) > 1) {
3301 gnome_vfs_escape_slashes (tny_mime_part_get_filename (mime_part));
3302 pair->filename = g_build_filename (chooser_uri, escaped, NULL);
3305 pair->filename = g_strdup (chooser_uri);
3307 pair->part = mime_part;
3308 files_to_save = g_list_prepend (files_to_save, pair);
3310 tny_iterator_next (iter);
3312 g_object_unref (iter);
3315 if (files_to_save != NULL) {
3316 SaveMimePartInfo *info = g_slice_new0 (SaveMimePartInfo);
3317 info->pairs = files_to_save;
3318 info->result = TRUE;
3319 info->uri = g_strdup (chooser_uri);
3320 info->window = g_object_ref (sa_info->window);
3321 save_mime_parts_to_file_with_checks ((GtkWindow *) dialog, info);
3323 g_free (chooser_uri);
3326 /* Free and close the dialog */
3327 g_object_unref (mime_parts);
3328 g_object_unref (sa_info->window);
3329 g_slice_free (SaveAttachmentsInfo, sa_info);
3330 gtk_widget_destroy (GTK_WIDGET (dialog));
3334 modest_msg_view_window_save_attachments (ModestMsgViewWindow *window,
3335 TnyList *mime_parts)
3337 ModestMsgViewWindowPrivate *priv;
3338 GtkWidget *save_dialog = NULL;
3339 gchar *conf_folder = NULL;
3340 gchar *filename = NULL;
3341 gchar *save_multiple_str = NULL;
3342 const gchar *root_folder = "file:///";
3344 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window));
3345 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
3347 if (mime_parts == NULL) {
3348 GtkWindow *toplevel = (GtkWindow *) gtk_widget_get_toplevel ((GtkWidget *) window);
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 (toplevel, 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 /* In hildon 2.2 we ignore the get_all flag as we always get all attachments. This is
3488 * because we don't have selection
3490 mime_parts = modest_msg_view_get_attachments (MODEST_MSG_VIEW (priv->msg_view));
3492 /* Remove already purged messages from mime parts list. We use
3493 a copy of the list to remove items in the original one */
3494 tmp = tny_list_copy (mime_parts);
3495 iter = tny_list_create_iterator (tmp);
3496 while (!tny_iterator_is_done (iter)) {
3497 TnyMimePart *part = TNY_MIME_PART (tny_iterator_get_current (iter));
3498 if (tny_mime_part_is_purged (part))
3499 tny_list_remove (mime_parts, (GObject *) part);
3501 g_object_unref (part);
3502 tny_iterator_next (iter);
3504 g_object_unref (tmp);
3505 g_object_unref (iter);
3507 if (!modest_toolkit_utils_select_attachments (GTK_WINDOW (window), mime_parts, TRUE) ||
3508 tny_list_get_length (mime_parts) == 0) {
3509 g_object_unref (mime_parts);
3513 n_attachments = tny_list_get_length (mime_parts);
3514 if (n_attachments == 1) {
3518 iter = tny_list_create_iterator (mime_parts);
3519 part = (TnyMimePart *) tny_iterator_get_current (iter);
3520 g_object_unref (iter);
3521 if (modest_tny_mime_part_is_msg (part)) {
3523 header = tny_msg_get_header (TNY_MSG (part));
3524 filename = tny_header_dup_subject (header);
3525 g_object_unref (header);
3526 if (filename == NULL)
3527 filename = g_strdup (_("mail_va_no_subject"));
3529 filename = g_strdup (tny_mime_part_get_filename (TNY_MIME_PART (part)));
3531 confirmation_message = g_strdup_printf (_("mcen_nc_purge_file_text"), filename);
3533 g_object_unref (part);
3535 confirmation_message = g_strdup_printf (ngettext("mcen_nc_purge_file_text",
3536 "mcen_nc_purge_files_text",
3537 n_attachments), n_attachments);
3539 response = modest_platform_run_confirmation_dialog (GTK_WINDOW (window),
3540 confirmation_message);
3541 g_free (confirmation_message);
3543 if (response != GTK_RESPONSE_OK) {
3544 g_object_unref (mime_parts);
3548 priv->purge_timeout = g_timeout_add (2000, show_remove_attachment_information, window);
3550 iter = tny_list_create_iterator (mime_parts);
3551 while (!tny_iterator_is_done (iter)) {
3554 part = (TnyMimePart *) tny_iterator_get_current (iter);
3555 tny_mime_part_set_purged (TNY_MIME_PART (part));
3556 g_object_unref (part);
3557 tny_iterator_next (iter);
3559 g_object_unref (iter);
3561 msg = tny_msg_view_get_msg (TNY_MSG_VIEW (priv->msg_view));
3562 tny_msg_view_clear (TNY_MSG_VIEW (priv->msg_view));
3563 tny_msg_rewrite_cache (msg);
3564 tny_msg_view_set_msg (TNY_MSG_VIEW (priv->msg_view), msg);
3565 g_object_unref (msg);
3566 update_branding (MODEST_MSG_VIEW_WINDOW (window));
3568 g_object_unref (mime_parts);
3570 if (priv->purge_timeout > 0) {
3571 g_source_remove (priv->purge_timeout);
3572 priv->purge_timeout = 0;
3575 if (priv->remove_attachment_banner) {
3576 gtk_widget_destroy (priv->remove_attachment_banner);
3577 g_object_unref (priv->remove_attachment_banner);
3578 priv->remove_attachment_banner = NULL;
3584 update_window_title (ModestMsgViewWindow *window)
3586 ModestMsgViewWindowPrivate *priv;
3588 TnyHeader *header = NULL;
3589 gchar *subject = NULL;
3591 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
3593 /* Note that if the window is closed while we're retrieving
3594 the message, this widget could de deleted */
3595 if (!priv->msg_view)
3598 msg = tny_msg_view_get_msg (TNY_MSG_VIEW (priv->msg_view));
3600 if (priv->other_body) {
3603 description = modest_tny_mime_part_get_header_value (priv->other_body, "Content-Description");
3605 g_strstrip (description);
3606 subject = description;
3608 } else if (msg != NULL) {
3609 header = tny_msg_get_header (msg);
3610 subject = tny_header_dup_subject (header);
3611 g_object_unref (header);
3612 g_object_unref (msg);
3615 if ((subject == NULL)||(subject[0] == '\0')) {
3617 subject = g_strdup (_("mail_va_no_subject"));
3620 modest_window_set_title (MODEST_WINDOW (window), subject);
3625 on_move_focus (GtkWidget *widget,
3626 GtkDirectionType direction,
3629 g_signal_stop_emission_by_name (G_OBJECT (widget), "move-focus");
3633 fetch_image_open_stream (TnyStreamCache *self, gint64 *expected_size, gchar *uri)
3635 GnomeVFSResult result;
3636 GnomeVFSHandle *handle = NULL;
3637 GnomeVFSFileInfo *info = NULL;
3640 result = gnome_vfs_open (&handle, uri, GNOME_VFS_OPEN_READ);
3641 if (result != GNOME_VFS_OK) {
3646 info = gnome_vfs_file_info_new ();
3647 result = gnome_vfs_get_file_info_from_handle (handle, info, GNOME_VFS_FILE_INFO_DEFAULT);
3648 if (result != GNOME_VFS_OK || ! (info->valid_fields & GNOME_VFS_FILE_INFO_FIELDS_SIZE)) {
3649 /* We put a "safe" default size for going to cache */
3650 *expected_size = (300*1024);
3652 *expected_size = info->size;
3654 gnome_vfs_file_info_unref (info);
3656 stream = tny_vfs_stream_new (handle);
3665 TnyStream *output_stream;
3666 GtkWidget *msg_view;
3671 on_fetch_image_timeout_refresh_view (gpointer userdata)
3673 ModestMsgViewWindowPrivate *priv;
3675 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (userdata);
3676 update_progress_hint (MODEST_MSG_VIEW_WINDOW (userdata));
3677 if (GTK_WIDGET_DRAWABLE (priv->msg_view)) {
3678 gtk_widget_queue_draw (GTK_WIDGET (priv->msg_view));
3680 priv->fetch_image_redraw_handler = 0;
3681 g_object_unref (userdata);
3686 on_fetch_image_idle_refresh_view (gpointer userdata)
3689 FetchImageData *fidata = (FetchImageData *) userdata;
3691 gdk_threads_enter ();
3692 if (GTK_WIDGET_DRAWABLE (fidata->msg_view)) {
3693 ModestMsgViewWindowPrivate *priv;
3695 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (fidata->window);
3696 priv->fetching_images--;
3697 if (priv->fetch_image_redraw_handler == 0) {
3698 priv->fetch_image_redraw_handler = g_timeout_add (500, on_fetch_image_timeout_refresh_view, g_object_ref (fidata->window));
3702 gdk_threads_leave ();
3704 g_object_unref (fidata->msg_view);
3705 g_object_unref (fidata->window);
3706 g_slice_free (FetchImageData, fidata);
3711 on_fetch_image_thread (gpointer userdata)
3713 FetchImageData *fidata = (FetchImageData *) userdata;
3714 TnyStreamCache *cache;
3715 TnyStream *cache_stream;
3717 cache = modest_runtime_get_images_cache ();
3719 tny_stream_cache_get_stream (cache,
3721 (TnyStreamCacheOpenStreamFetcher) fetch_image_open_stream,
3722 (gpointer) fidata->uri);
3723 g_free (fidata->cache_id);
3724 g_free (fidata->uri);
3726 if (cache_stream != NULL) {
3729 while (G_LIKELY (!tny_stream_is_eos (cache_stream))) {
3732 nb_read = tny_stream_read (cache_stream, buffer, sizeof (buffer));
3733 if (G_UNLIKELY (nb_read < 0)) {
3735 } else if (G_LIKELY (nb_read > 0)) {
3736 gssize nb_written = 0;
3738 while (G_UNLIKELY (nb_written < nb_read)) {
3741 len = tny_stream_write (fidata->output_stream, buffer + nb_written,
3742 nb_read - nb_written);
3743 if (G_UNLIKELY (len < 0))
3749 tny_stream_close (cache_stream);
3750 g_object_unref (cache_stream);
3753 tny_stream_close (fidata->output_stream);
3754 g_object_unref (fidata->output_stream);
3756 g_idle_add (on_fetch_image_idle_refresh_view, fidata);
3762 on_fetch_image (ModestMsgView *msgview,
3765 ModestMsgViewWindow *window)
3767 const gchar *current_account;
3768 ModestMsgViewWindowPrivate *priv;
3769 FetchImageData *fidata;
3771 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
3773 current_account = modest_window_get_active_account (MODEST_WINDOW (window));
3775 fidata = g_slice_new0 (FetchImageData);
3776 fidata->msg_view = g_object_ref (msgview);
3777 fidata->window = g_object_ref (window);
3778 fidata->uri = g_strdup (uri);
3779 fidata->cache_id = modest_images_cache_get_id (current_account, uri);
3780 fidata->output_stream = g_object_ref (stream);
3782 priv->fetching_images++;
3783 if (g_thread_create (on_fetch_image_thread, fidata, FALSE, NULL) == NULL) {
3784 g_object_unref (fidata->output_stream);
3785 g_free (fidata->cache_id);
3786 g_free (fidata->uri);
3787 g_object_unref (fidata->msg_view);
3788 g_slice_free (FetchImageData, fidata);
3789 tny_stream_close (stream);
3790 priv->fetching_images--;
3791 update_progress_hint (window);
3794 update_progress_hint (window);
3800 setup_menu (ModestMsgViewWindow *self)
3802 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW(self));
3804 /* Settings menu buttons */
3805 modest_window_add_to_menu (MODEST_WINDOW (self), _("mcen_me_viewer_find"), NULL,
3806 MODEST_WINDOW_MENU_CALLBACK (modest_msg_view_window_show_isearch_toolbar),
3807 MODEST_DIMMING_CALLBACK (modest_ui_dimming_rules_on_find_in_msg));
3809 modest_window_add_to_menu (MODEST_WINDOW (self),
3810 dngettext(GETTEXT_PACKAGE,
3811 "mcen_me_move_message",
3812 "mcen_me_move_messages",
3815 MODEST_WINDOW_MENU_CALLBACK (modest_ui_actions_on_move_to),
3816 MODEST_DIMMING_CALLBACK (modest_ui_dimming_rules_on_move_to));
3818 modest_window_add_to_menu (MODEST_WINDOW (self), _("mcen_me_inbox_mark_as_read"), NULL,
3819 MODEST_WINDOW_MENU_CALLBACK (modest_ui_actions_on_mark_as_read),
3820 MODEST_DIMMING_CALLBACK (modest_ui_dimming_rules_on_mark_as_read_msg_in_view));
3822 modest_window_add_to_menu (MODEST_WINDOW (self), _("mcen_me_inbox_mark_as_unread"), NULL,
3823 MODEST_WINDOW_MENU_CALLBACK (modest_ui_actions_on_mark_as_unread),
3824 MODEST_DIMMING_CALLBACK (modest_ui_dimming_rules_on_mark_as_unread_msg_in_view));
3826 modest_window_add_to_menu (MODEST_WINDOW (self), _("mcen_me_viewer_save_attachments"), NULL,
3827 MODEST_WINDOW_MENU_CALLBACK (modest_ui_actions_save_attachments),
3828 MODEST_DIMMING_CALLBACK (modest_ui_dimming_rules_on_save_attachments));
3829 modest_window_add_to_menu (MODEST_WINDOW (self), _("mcen_me_inbox_remove_attachments"), NULL,
3830 MODEST_WINDOW_MENU_CALLBACK (modest_ui_actions_remove_attachments),
3831 MODEST_DIMMING_CALLBACK (modest_ui_dimming_rules_on_remove_attachments));
3833 modest_window_add_to_menu (MODEST_WINDOW (self), _("mcen_me_new_message"), "<Control>n",
3834 MODEST_WINDOW_MENU_CALLBACK (modest_ui_actions_on_new_msg),
3835 MODEST_DIMMING_CALLBACK (modest_ui_dimming_rules_on_new_msg));
3836 modest_window_add_to_menu (MODEST_WINDOW (self), _("mcen_me_viewer_addtocontacts"), NULL,
3837 MODEST_WINDOW_MENU_CALLBACK (modest_ui_actions_add_to_contacts),
3838 MODEST_DIMMING_CALLBACK (modest_ui_dimming_rules_on_add_to_contacts));
3840 modest_window_add_to_menu (MODEST_WINDOW (self), _("mcen_ti_message_properties"), NULL,
3841 MODEST_WINDOW_MENU_CALLBACK (modest_ui_actions_on_details),
3842 MODEST_DIMMING_CALLBACK (modest_ui_dimming_rules_on_details));
3846 modest_msg_view_window_add_to_contacts (ModestMsgViewWindow *self)
3848 ModestMsgViewWindowPrivate *priv;
3849 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
3850 GSList *recipients = NULL;
3853 msg = tny_msg_view_get_msg (TNY_MSG_VIEW (priv->msg_view));
3857 header = modest_msg_view_window_get_header (self);
3860 recipients = modest_tny_msg_header_get_all_recipients_list (header);
3861 g_object_unref (header);
3863 recipients = modest_tny_msg_get_all_recipients_list (msg);
3864 g_object_unref (msg);
3868 /* Offer the user to add recipients to the address book */
3869 modest_address_book_add_address_list_with_selector (recipients, (GtkWindow *) self);
3870 g_slist_foreach (recipients, (GFunc) g_free, NULL); g_slist_free (recipients);
3875 _modest_msg_view_window_map_event (GtkWidget *widget,
3879 ModestMsgViewWindow *self = (ModestMsgViewWindow *) userdata;
3881 update_progress_hint (self);
3887 modest_msg_view_window_fetch_images (ModestMsgViewWindow *self)
3889 ModestMsgViewWindowPrivate *priv;
3890 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
3892 modest_msg_view_request_fetch_images (MODEST_MSG_VIEW (priv->msg_view));
3896 modest_msg_view_window_has_blocked_external_images (ModestMsgViewWindow *self)
3898 ModestMsgViewWindowPrivate *priv;
3899 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
3901 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self), FALSE);
3903 return modest_msg_view_has_blocked_external_images (MODEST_MSG_VIEW (priv->msg_view));
3907 modest_msg_view_window_reload (ModestMsgViewWindow *self)
3909 ModestMsgViewWindowPrivate *priv;
3910 const gchar *msg_uid;
3911 TnyHeader *header = NULL;
3912 TnyFolder *folder = NULL;
3914 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self));
3916 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
3918 header = modest_msg_view_window_get_header (MODEST_MSG_VIEW_WINDOW (self));
3922 folder = tny_header_get_folder (header);
3923 g_object_unref (header);
3928 msg_uid = modest_msg_view_window_get_message_uid (self);
3930 GtkTreeRowReference *row_reference;
3932 if (priv->row_reference && gtk_tree_row_reference_valid (priv->row_reference)) {
3933 row_reference = priv->row_reference;
3935 row_reference = NULL;
3937 if (!message_reader (self, priv, NULL, msg_uid, folder, row_reference))
3938 g_warning ("Shouldn't happen, trying to reload a message failed");
3941 g_object_unref (folder);
3945 update_branding (ModestMsgViewWindow *self)
3947 const gchar *account;
3948 const gchar *mailbox;
3949 ModestAccountMgr *mgr;
3950 ModestProtocol *protocol = NULL;
3951 gchar *service_name = NULL;
3952 const GdkPixbuf *service_icon = NULL;
3953 ModestMsgViewWindowPrivate *priv;
3955 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
3957 account = modest_window_get_active_account (MODEST_WINDOW (self));
3958 mailbox = modest_window_get_active_mailbox (MODEST_WINDOW (self));
3960 mgr = modest_runtime_get_account_mgr ();
3962 if (modest_account_mgr_account_is_multimailbox (mgr, account, &protocol)) {
3963 if (MODEST_IS_ACCOUNT_PROTOCOL (protocol)) {
3964 service_name = modest_account_protocol_get_service_name (MODEST_ACCOUNT_PROTOCOL (protocol),
3966 service_icon = modest_account_protocol_get_service_icon (MODEST_ACCOUNT_PROTOCOL (protocol),
3967 account, mailbox, MODEST_ICON_SIZE_SMALL);
3971 modest_msg_view_set_branding (MODEST_MSG_VIEW (priv->msg_view), service_name, service_icon);
3972 g_free (service_name);
3976 sync_flags (ModestMsgViewWindow *self)
3978 TnyHeader *header = NULL;
3980 header = modest_msg_view_window_get_header (self);
3982 TnyMsg *msg = modest_msg_view_window_get_message (self);
3984 header = tny_msg_get_header (msg);
3985 g_object_unref (msg);
3990 TnyFolder *folder = tny_header_get_folder (header);
3993 ModestMailOperation *mail_op;
3995 /* Sync folder, we need this to save the seen flag */
3996 mail_op = modest_mail_operation_new (NULL);
3997 modest_mail_operation_queue_add (modest_runtime_get_mail_operation_queue (),
3999 modest_mail_operation_sync_folder (mail_op, folder, FALSE, NULL, NULL);
4000 g_object_unref (mail_op);
4001 g_object_unref (folder);
4003 g_object_unref (header);