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>
83 #define MYDOCS_ENV "MYDOCSDIR"
84 #define DOCS_FOLDER ".documents"
86 typedef struct _ModestMsgViewWindowPrivate ModestMsgViewWindowPrivate;
87 struct _ModestMsgViewWindowPrivate {
90 GtkWidget *main_scroll;
91 GtkWidget *isearch_toolbar;
94 /* Progress observers */
95 GSList *progress_widgets;
98 GtkWidget *prev_toolitem;
99 GtkWidget *next_toolitem;
100 gboolean progress_hint;
101 gint fetching_images;
103 /* Optimized view enabled */
104 gboolean optimized_view;
106 /* Whether this was created via the *_new_for_search_result() function. */
107 gboolean is_search_result;
109 /* Whether the message is in outbox */
112 /* A reference to the @model of the header view
113 * to allow selecting previous/next messages,
114 * if the message is currently selected in the header view.
116 const gchar *header_folder_id;
117 GtkTreeModel *header_model;
118 GtkTreeRowReference *row_reference;
119 GtkTreeRowReference *next_row_reference;
121 gulong clipboard_change_handler;
122 gulong queue_change_handler;
123 gulong account_removed_handler;
124 gulong row_changed_handler;
125 gulong row_deleted_handler;
126 gulong row_inserted_handler;
127 gulong rows_reordered_handler;
130 GtkWidget *remove_attachment_banner;
133 TnyMimePart *other_body;
138 static void modest_msg_view_window_class_init (ModestMsgViewWindowClass *klass);
139 static void modest_msg_view_window_init (ModestMsgViewWindow *obj);
140 static void modest_header_view_observer_init (ModestHeaderViewObserverIface *iface_class);
141 static void modest_msg_view_window_finalize (GObject *obj);
142 static void modest_msg_view_window_show_isearch_toolbar (GtkWidget *obj, gpointer data);
143 static void modest_msg_view_window_isearch_toolbar_close (GtkWidget *widget,
144 ModestMsgViewWindow *obj);
145 static void modest_msg_view_window_isearch_toolbar_search (GtkWidget *widget,
146 ModestMsgViewWindow *obj);
147 static void modest_msg_view_window_toggle_isearch_toolbar (GtkWidget *obj,
149 static void modest_msg_view_window_disconnect_signals (ModestWindow *self);
151 static gdouble modest_msg_view_window_get_zoom (ModestWindow *window);
152 static void modest_msg_view_window_set_zoom (ModestWindow *window,
154 static gboolean modest_msg_view_window_zoom_minus (ModestWindow *window);
155 static gboolean modest_msg_view_window_zoom_plus (ModestWindow *window);
156 static gboolean modest_msg_view_window_key_event (GtkWidget *window,
159 static void modest_msg_view_window_update_priority (ModestMsgViewWindow *window);
161 static void modest_msg_view_window_show_toolbar (ModestWindow *window,
162 gboolean show_toolbar);
164 static void modest_msg_view_window_clipboard_owner_change (GtkClipboard *clipboard,
166 ModestMsgViewWindow *window);
168 static void modest_msg_view_window_on_row_changed (GtkTreeModel *header_model,
171 ModestMsgViewWindow *window);
173 static void modest_msg_view_window_on_row_deleted (GtkTreeModel *header_model,
175 ModestMsgViewWindow *window);
177 static void modest_msg_view_window_on_row_inserted (GtkTreeModel *header_model,
178 GtkTreePath *tree_path,
179 GtkTreeIter *tree_iter,
180 ModestMsgViewWindow *window);
182 static void modest_msg_view_window_on_row_reordered (GtkTreeModel *header_model,
186 ModestMsgViewWindow *window);
188 static void modest_msg_view_window_update_model_replaced (ModestHeaderViewObserver *window,
190 const gchar *tny_folder_id);
192 static void on_queue_changed (ModestMailOperationQueue *queue,
193 ModestMailOperation *mail_op,
194 ModestMailOperationQueueNotification type,
195 ModestMsgViewWindow *self);
197 static void on_account_removed (TnyAccountStore *account_store,
201 static void on_move_focus (GtkWidget *widget,
202 GtkDirectionType direction,
205 static void view_msg_cb (ModestMailOperation *mail_op,
212 static void set_progress_hint (ModestMsgViewWindow *self,
215 static void update_window_title (ModestMsgViewWindow *window);
217 static void init_window (ModestMsgViewWindow *obj);
219 static gboolean msg_is_visible (TnyHeader *header, gboolean check_outbox);
221 static void check_dimming_rules_after_change (ModestMsgViewWindow *window);
223 static gboolean on_fetch_image (ModestMsgView *msgview,
226 ModestMsgViewWindow *window);
228 static gboolean modest_msg_view_window_scroll_child (ModestMsgViewWindow *self,
229 GtkScrollType scroll_type,
232 static gboolean message_reader (ModestMsgViewWindow *window,
233 ModestMsgViewWindowPrivate *priv,
235 const gchar *msg_uid,
237 GtkTreeRowReference *row_reference);
239 static void setup_menu (ModestMsgViewWindow *self);
240 static gboolean _modest_msg_view_window_map_event (GtkWidget *widget,
243 static void update_branding (ModestMsgViewWindow *self);
244 static void sync_flags (ModestMsgViewWindow *self);
246 /* list my signals */
253 static const GtkActionEntry msg_view_toolbar_action_entries [] = {
256 { "ToolbarMessageReply", MODEST_STOCK_REPLY, N_("mcen_me_inbox_reply"), "<CTRL>R", NULL, G_CALLBACK (modest_ui_actions_on_reply) },
257 { "ToolbarMessageReplyAll", MODEST_STOCK_REPLY_ALL, N_("mcen_me_inbox_replytoall"), NULL, NULL, G_CALLBACK (modest_ui_actions_on_reply_all) },
258 { "ToolbarMessageForward", MODEST_STOCK_FORWARD, N_("mcen_me_inbox_forward"), NULL, NULL, G_CALLBACK (modest_ui_actions_on_forward) },
259 { "ToolbarDeleteMessage", MODEST_STOCK_DELETE, N_("qgn_toolb_gene_deletebutton"), NULL, NULL, G_CALLBACK (modest_ui_actions_on_delete_message_or_folder) },
260 { "ToolbarMessageBack", MODEST_TOOLBAR_ICON_PREV, N_("qgn_toolb_gene_back"), NULL, NULL, G_CALLBACK (modest_ui_actions_on_prev) },
261 { "ToolbarMessageNext", MODEST_TOOLBAR_ICON_NEXT, N_("qgn_toolb_gene_forward"), NULL, NULL, G_CALLBACK (modest_ui_actions_on_next) },
262 { "ToolbarDownloadExternalImages", MODEST_TOOLBAR_ICON_DOWNLOAD_IMAGES, N_("mail_bd_external_images"), NULL, NULL, G_CALLBACK (modest_ui_actions_on_fetch_images) },
265 static const GtkToggleActionEntry msg_view_toggle_action_entries [] = {
266 { "FindInMessage", MODEST_TOOLBAR_ICON_FIND, N_("qgn_toolb_gene_find"), "<CTRL>F", NULL, G_CALLBACK (modest_msg_view_window_toggle_isearch_toolbar), FALSE },
269 #define MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE((o), \
270 MODEST_TYPE_MSG_VIEW_WINDOW, \
271 ModestMsgViewWindowPrivate))
273 static GtkWindowClass *parent_class = NULL;
275 /* uncomment the following if you have defined any signals */
276 static guint signals[LAST_SIGNAL] = {0};
279 modest_msg_view_window_get_type (void)
281 static GType my_type = 0;
283 static const GTypeInfo my_info = {
284 sizeof(ModestMsgViewWindowClass),
285 NULL, /* base init */
286 NULL, /* base finalize */
287 (GClassInitFunc) modest_msg_view_window_class_init,
288 NULL, /* class finalize */
289 NULL, /* class data */
290 sizeof(ModestMsgViewWindow),
292 (GInstanceInitFunc) modest_msg_view_window_init,
295 #ifndef MODEST_TOOLKIT_HILDON2
296 my_type = g_type_register_static (MODEST_TYPE_SHELL_WINDOW,
297 "ModestMsgViewWindow",
300 my_type = g_type_register_static (MODEST_TYPE_HILDON2_WINDOW,
301 "ModestMsgViewWindow",
305 static const GInterfaceInfo modest_header_view_observer_info =
307 (GInterfaceInitFunc) modest_header_view_observer_init,
308 NULL, /* interface_finalize */
309 NULL /* interface_data */
312 g_type_add_interface_static (my_type,
313 MODEST_TYPE_HEADER_VIEW_OBSERVER,
314 &modest_header_view_observer_info);
320 save_state (ModestWindow *self)
322 modest_widget_memory_save (modest_runtime_get_conf (),
324 MODEST_CONF_MSG_VIEW_WINDOW_KEY);
328 modest_msg_view_window_scroll_child (ModestMsgViewWindow *self,
329 GtkScrollType scroll_type,
333 ModestMsgViewWindowPrivate *priv;
336 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(self);
338 switch (scroll_type) {
339 case GTK_SCROLL_STEP_UP:
342 case GTK_SCROLL_STEP_DOWN:
345 case GTK_SCROLL_PAGE_UP:
348 case GTK_SCROLL_PAGE_DOWN:
351 case GTK_SCROLL_START:
362 modest_scrollable_scroll ((ModestScrollable *) priv->main_scroll, 0, step);
364 return (gboolean) step;
368 add_scroll_binding (GtkBindingSet *binding_set,
370 GtkScrollType scroll)
372 guint keypad_keyval = keyval - GDK_Left + GDK_KP_Left;
374 gtk_binding_entry_add_signal (binding_set, keyval, 0,
376 GTK_TYPE_SCROLL_TYPE, scroll,
377 G_TYPE_BOOLEAN, FALSE);
378 gtk_binding_entry_add_signal (binding_set, keypad_keyval, 0,
380 GTK_TYPE_SCROLL_TYPE, scroll,
381 G_TYPE_BOOLEAN, FALSE);
385 modest_msg_view_window_class_init (ModestMsgViewWindowClass *klass)
387 GObjectClass *gobject_class;
388 ModestWindowClass *modest_window_class;
389 GtkBindingSet *binding_set;
391 gobject_class = (GObjectClass*) klass;
392 modest_window_class = (ModestWindowClass *) klass;
394 parent_class = g_type_class_peek_parent (klass);
395 gobject_class->finalize = modest_msg_view_window_finalize;
397 modest_window_class->set_zoom_func = modest_msg_view_window_set_zoom;
398 modest_window_class->get_zoom_func = modest_msg_view_window_get_zoom;
399 modest_window_class->zoom_plus_func = modest_msg_view_window_zoom_plus;
400 modest_window_class->zoom_minus_func = modest_msg_view_window_zoom_minus;
401 modest_window_class->show_toolbar_func = modest_msg_view_window_show_toolbar;
402 modest_window_class->disconnect_signals_func = modest_msg_view_window_disconnect_signals;
404 modest_window_class->save_state_func = save_state;
406 klass->scroll_child = modest_msg_view_window_scroll_child;
408 signals[MSG_CHANGED_SIGNAL] =
409 g_signal_new ("msg-changed",
410 G_TYPE_FROM_CLASS (gobject_class),
412 G_STRUCT_OFFSET (ModestMsgViewWindowClass, msg_changed),
414 modest_marshal_VOID__POINTER_POINTER,
415 G_TYPE_NONE, 2, G_TYPE_POINTER, G_TYPE_POINTER);
417 signals[SCROLL_CHILD_SIGNAL] =
418 g_signal_new ("scroll-child",
419 G_TYPE_FROM_CLASS (gobject_class),
420 G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
421 G_STRUCT_OFFSET (ModestMsgViewWindowClass, scroll_child),
423 modest_marshal_BOOLEAN__ENUM_BOOLEAN,
424 G_TYPE_BOOLEAN, 2, GTK_TYPE_SCROLL_TYPE, G_TYPE_BOOLEAN);
426 binding_set = gtk_binding_set_by_class (klass);
427 add_scroll_binding (binding_set, GDK_Up, GTK_SCROLL_STEP_UP);
428 add_scroll_binding (binding_set, GDK_Down, GTK_SCROLL_STEP_DOWN);
429 add_scroll_binding (binding_set, GDK_Page_Up, GTK_SCROLL_PAGE_UP);
430 add_scroll_binding (binding_set, GDK_Page_Down, GTK_SCROLL_PAGE_DOWN);
431 add_scroll_binding (binding_set, GDK_Home, GTK_SCROLL_START);
432 add_scroll_binding (binding_set, GDK_End, GTK_SCROLL_END);
434 g_type_class_add_private (gobject_class, sizeof(ModestMsgViewWindowPrivate));
438 static void modest_header_view_observer_init(
439 ModestHeaderViewObserverIface *iface_class)
441 iface_class->update_func = modest_msg_view_window_update_model_replaced;
445 modest_msg_view_window_init (ModestMsgViewWindow *obj)
447 ModestMsgViewWindowPrivate *priv;
448 ModestWindowPrivate *parent_priv = NULL;
449 GtkActionGroup *action_group = NULL;
450 GError *error = NULL;
452 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(obj);
453 parent_priv = MODEST_WINDOW_GET_PRIVATE(obj);
454 parent_priv->ui_manager = gtk_ui_manager_new();
456 action_group = gtk_action_group_new ("ModestMsgViewWindowActions");
457 gtk_action_group_set_translation_domain (action_group, GETTEXT_PACKAGE);
459 /* Add common actions */
460 gtk_action_group_add_actions (action_group,
461 msg_view_toolbar_action_entries,
462 G_N_ELEMENTS (msg_view_toolbar_action_entries),
464 gtk_action_group_add_toggle_actions (action_group,
465 msg_view_toggle_action_entries,
466 G_N_ELEMENTS (msg_view_toggle_action_entries),
469 gtk_ui_manager_insert_action_group (parent_priv->ui_manager, action_group, 0);
470 g_object_unref (action_group);
472 /* Load the UI definition */
473 gtk_ui_manager_add_ui_from_file (parent_priv->ui_manager, MODEST_UIDIR "modest-msg-view-window-ui.xml",
476 g_printerr ("modest: could not merge modest-msg-view-window-ui.xml: %s\n", error->message);
477 g_error_free (error);
481 priv->is_search_result = FALSE;
482 priv->is_outbox = FALSE;
484 priv->msg_view = NULL;
485 priv->header_model = NULL;
486 priv->header_folder_id = NULL;
487 priv->clipboard_change_handler = 0;
488 priv->queue_change_handler = 0;
489 priv->account_removed_handler = 0;
490 priv->row_changed_handler = 0;
491 priv->row_deleted_handler = 0;
492 priv->row_inserted_handler = 0;
493 priv->rows_reordered_handler = 0;
494 priv->progress_hint = FALSE;
495 priv->fetching_images = 0;
497 priv->optimized_view = FALSE;
498 priv->purge_timeout = 0;
499 priv->remove_attachment_banner = NULL;
500 priv->msg_uid = NULL;
501 priv->other_body = NULL;
503 priv->sighandlers = NULL;
506 init_window (MODEST_MSG_VIEW_WINDOW(obj));
511 update_progress_hint (ModestMsgViewWindow *self)
513 ModestMsgViewWindowPrivate *priv;
514 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(self);
516 if (GTK_WIDGET_VISIBLE (self)) {
517 modest_window_show_progress (MODEST_WINDOW (self),
518 (priv->progress_hint || (priv->fetching_images > 0))?1:0);
523 set_progress_hint (ModestMsgViewWindow *self,
526 ModestWindowPrivate *parent_priv;
527 ModestMsgViewWindowPrivate *priv;
529 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self));
531 parent_priv = MODEST_WINDOW_GET_PRIVATE(self);
532 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(self);
534 /* Sets current progress hint */
535 priv->progress_hint = enabled;
537 update_progress_hint (self);
543 init_window (ModestMsgViewWindow *obj)
545 GtkWidget *main_vbox;
546 ModestMsgViewWindowPrivate *priv;
548 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(obj);
550 priv->msg_view = GTK_WIDGET (tny_platform_factory_new_msg_view (modest_tny_platform_factory_get_instance ()));
551 modest_msg_view_set_shadow_type (MODEST_MSG_VIEW (priv->msg_view), GTK_SHADOW_NONE);
552 main_vbox = gtk_vbox_new (FALSE, 6);
554 priv->main_scroll = modest_toolkit_factory_create_scrollable (modest_runtime_get_toolkit_factory ());
555 modest_scrollable_set_horizontal_policy (MODEST_SCROLLABLE (priv->main_scroll), GTK_POLICY_AUTOMATIC);
556 g_object_set (G_OBJECT (priv->main_scroll),
557 "movement-mode", MODEST_MOVEMENT_MODE_BOTH,
558 "horizontal-max-overshoot", 0,
560 gtk_container_add (GTK_CONTAINER (priv->main_scroll), priv->msg_view);
561 gtk_box_pack_start (GTK_BOX(main_vbox), priv->main_scroll, TRUE, TRUE, 0);
562 gtk_container_add (GTK_CONTAINER(obj), main_vbox);
564 /* NULL-ize fields if the window is destroyed */
565 g_signal_connect (priv->msg_view, "destroy", G_CALLBACK (gtk_widget_destroyed), &(priv->msg_view));
567 gtk_widget_show_all (GTK_WIDGET(main_vbox));
571 modest_msg_view_window_disconnect_signals (ModestWindow *self)
573 ModestMsgViewWindowPrivate *priv;
574 GtkWidget *header_view = NULL;
575 GtkWindow *parent_window = NULL;
577 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
579 if (gtk_clipboard_get (GDK_SELECTION_PRIMARY) &&
580 g_signal_handler_is_connected (gtk_clipboard_get (GDK_SELECTION_PRIMARY),
581 priv->clipboard_change_handler))
582 g_signal_handler_disconnect (gtk_clipboard_get (GDK_SELECTION_PRIMARY),
583 priv->clipboard_change_handler);
585 if (g_signal_handler_is_connected (G_OBJECT (modest_runtime_get_mail_operation_queue ()),
586 priv->queue_change_handler))
587 g_signal_handler_disconnect (G_OBJECT (modest_runtime_get_mail_operation_queue ()),
588 priv->queue_change_handler);
590 if (g_signal_handler_is_connected (G_OBJECT (modest_runtime_get_account_store ()),
591 priv->account_removed_handler))
592 g_signal_handler_disconnect (G_OBJECT (modest_runtime_get_account_store ()),
593 priv->account_removed_handler);
595 if (priv->header_model) {
596 if (g_signal_handler_is_connected(G_OBJECT (priv->header_model),
597 priv->row_changed_handler))
598 g_signal_handler_disconnect(G_OBJECT (priv->header_model),
599 priv->row_changed_handler);
601 if (g_signal_handler_is_connected(G_OBJECT (priv->header_model),
602 priv->row_deleted_handler))
603 g_signal_handler_disconnect(G_OBJECT (priv->header_model),
604 priv->row_deleted_handler);
606 if (g_signal_handler_is_connected(G_OBJECT (priv->header_model),
607 priv->row_inserted_handler))
608 g_signal_handler_disconnect(G_OBJECT (priv->header_model),
609 priv->row_inserted_handler);
611 if (g_signal_handler_is_connected(G_OBJECT (priv->header_model),
612 priv->rows_reordered_handler))
613 g_signal_handler_disconnect(G_OBJECT (priv->header_model),
614 priv->rows_reordered_handler);
617 modest_signal_mgr_disconnect_all_and_destroy (priv->sighandlers);
618 priv->sighandlers = NULL;
620 parent_window = gtk_window_get_transient_for (GTK_WINDOW (self));
621 if (parent_window && MODEST_IS_HEADER_WINDOW (parent_window)) {
622 header_view = GTK_WIDGET (modest_header_window_get_header_view (MODEST_HEADER_WINDOW (parent_window)));
624 modest_header_view_remove_observer(MODEST_HEADER_VIEW (header_view),
625 MODEST_HEADER_VIEW_OBSERVER(self));
631 modest_msg_view_window_finalize (GObject *obj)
633 ModestMsgViewWindowPrivate *priv;
635 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (obj);
637 /* Sanity check: shouldn't be needed, the window mgr should
638 call this function before */
639 modest_msg_view_window_disconnect_signals (MODEST_WINDOW (obj));
641 if (priv->other_body != NULL) {
642 g_object_unref (priv->other_body);
643 priv->other_body = NULL;
646 if (priv->header_model != NULL) {
647 g_object_unref (priv->header_model);
648 priv->header_model = NULL;
651 if (priv->remove_attachment_banner) {
652 gtk_widget_destroy (priv->remove_attachment_banner);
653 g_object_unref (priv->remove_attachment_banner);
654 priv->remove_attachment_banner = NULL;
657 if (priv->purge_timeout > 0) {
658 g_source_remove (priv->purge_timeout);
659 priv->purge_timeout = 0;
662 if (priv->row_reference) {
663 gtk_tree_row_reference_free (priv->row_reference);
664 priv->row_reference = NULL;
667 if (priv->next_row_reference) {
668 gtk_tree_row_reference_free (priv->next_row_reference);
669 priv->next_row_reference = NULL;
673 g_free (priv->msg_uid);
674 priv->msg_uid = NULL;
677 G_OBJECT_CLASS(parent_class)->finalize (obj);
681 select_next_valid_row (GtkTreeModel *model,
682 GtkTreeRowReference **row_reference,
686 GtkTreeIter tmp_iter;
688 GtkTreePath *next = NULL;
689 gboolean retval = FALSE, finished;
691 g_return_val_if_fail (gtk_tree_row_reference_valid (*row_reference), FALSE);
693 path = gtk_tree_row_reference_get_path (*row_reference);
694 gtk_tree_model_get_iter (model, &tmp_iter, path);
695 gtk_tree_row_reference_free (*row_reference);
696 *row_reference = NULL;
700 TnyHeader *header = NULL;
702 if (gtk_tree_model_iter_next (model, &tmp_iter)) {
703 gtk_tree_model_get (model, &tmp_iter,
704 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
708 if (msg_is_visible (header, is_outbox)) {
709 next = gtk_tree_model_get_path (model, &tmp_iter);
710 *row_reference = gtk_tree_row_reference_new (model, next);
711 gtk_tree_path_free (next);
715 g_object_unref (header);
718 } else if (cycle && gtk_tree_model_get_iter_first (model, &tmp_iter)) {
719 next = gtk_tree_model_get_path (model, &tmp_iter);
721 /* Ensure that we are not selecting the same */
722 if (gtk_tree_path_compare (path, next) != 0) {
723 gtk_tree_model_get (model, &tmp_iter,
724 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
727 if (msg_is_visible (header, is_outbox)) {
728 *row_reference = gtk_tree_row_reference_new (model, next);
732 g_object_unref (header);
736 /* If we ended up in the same message
737 then there is no valid next
741 gtk_tree_path_free (next);
743 /* If there are no more messages and we don't
744 want to start again in the first one then
745 there is no valid next message */
751 gtk_tree_path_free (path);
756 /* TODO: This should be in _init(), with the parameters as properties. */
758 modest_msg_view_window_construct (ModestMsgViewWindow *self,
759 const gchar *modest_account_name,
760 const gchar *mailbox,
761 const gchar *msg_uid)
764 ModestMsgViewWindowPrivate *priv = NULL;
765 ModestWindowPrivate *parent_priv = NULL;
766 ModestDimmingRulesGroup *toolbar_rules_group = NULL;
767 ModestDimmingRulesGroup *clipboard_rules_group = NULL;
769 obj = G_OBJECT (self);
770 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(obj);
771 parent_priv = MODEST_WINDOW_GET_PRIVATE(obj);
773 priv->msg_uid = g_strdup (msg_uid);
776 parent_priv->menubar = NULL;
778 toolbar_rules_group = modest_dimming_rules_group_new (MODEST_DIMMING_RULES_TOOLBAR, TRUE);
779 clipboard_rules_group = modest_dimming_rules_group_new (MODEST_DIMMING_RULES_CLIPBOARD, FALSE);
782 /* Add common dimming rules */
783 modest_dimming_rules_group_add_rules (toolbar_rules_group,
784 modest_msg_view_toolbar_dimming_entries,
785 G_N_ELEMENTS (modest_msg_view_toolbar_dimming_entries),
786 MODEST_WINDOW (self));
787 modest_dimming_rules_group_add_rules (clipboard_rules_group,
788 modest_msg_view_clipboard_dimming_entries,
789 G_N_ELEMENTS (modest_msg_view_clipboard_dimming_entries),
790 MODEST_WINDOW (self));
792 /* Insert dimming rules group for this window */
793 modest_ui_dimming_manager_insert_rules_group (parent_priv->ui_dimming_manager, toolbar_rules_group);
794 modest_ui_dimming_manager_insert_rules_group (parent_priv->ui_dimming_manager, clipboard_rules_group);
795 g_object_unref (toolbar_rules_group);
796 g_object_unref (clipboard_rules_group);
798 /* g_signal_connect (G_OBJECT(obj), "delete-event", G_CALLBACK(on_delete_event), obj); */
800 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);
801 g_signal_connect (G_OBJECT(priv->msg_view), "activate_link",
802 G_CALLBACK (modest_ui_actions_on_msg_link_clicked), obj);
803 g_signal_connect (G_OBJECT(priv->msg_view), "link_hover",
804 G_CALLBACK (modest_ui_actions_on_msg_link_hover), obj);
805 g_signal_connect (G_OBJECT(priv->msg_view), "attachment_clicked",
806 G_CALLBACK (modest_ui_actions_on_msg_attachment_clicked), obj);
807 g_signal_connect (G_OBJECT(priv->msg_view), "recpt_activated",
808 G_CALLBACK (modest_ui_actions_on_msg_recpt_activated), obj);
809 g_signal_connect (G_OBJECT(priv->msg_view), "show_details",
810 G_CALLBACK (modest_ui_actions_on_details), obj);
811 g_signal_connect (G_OBJECT(priv->msg_view), "link_contextual",
812 G_CALLBACK (modest_ui_actions_on_msg_link_contextual), obj);
813 g_signal_connect (G_OBJECT(priv->msg_view), "limit_error",
814 G_CALLBACK (modest_ui_actions_on_limit_error), obj);
815 g_signal_connect (G_OBJECT (priv->msg_view), "fetch_image",
816 G_CALLBACK (on_fetch_image), obj);
818 g_signal_connect (G_OBJECT (obj), "key-release-event",
819 G_CALLBACK (modest_msg_view_window_key_event),
822 g_signal_connect (G_OBJECT (obj), "key-press-event",
823 G_CALLBACK (modest_msg_view_window_key_event),
826 g_signal_connect (G_OBJECT (obj), "move-focus",
827 G_CALLBACK (on_move_focus), obj);
829 g_signal_connect (G_OBJECT (obj), "map-event",
830 G_CALLBACK (_modest_msg_view_window_map_event),
833 /* Mail Operation Queue */
834 priv->queue_change_handler = g_signal_connect (G_OBJECT (modest_runtime_get_mail_operation_queue ()),
836 G_CALLBACK (on_queue_changed),
839 /* Account manager */
840 priv->account_removed_handler = g_signal_connect (G_OBJECT (modest_runtime_get_account_store ()),
842 G_CALLBACK(on_account_removed),
845 modest_window_set_active_account (MODEST_WINDOW(obj), modest_account_name);
846 modest_window_set_active_mailbox (MODEST_WINDOW(obj), mailbox);
848 /* First add out toolbar ... */
849 modest_msg_view_window_show_toolbar (MODEST_WINDOW (obj), TRUE);
851 priv->isearch_toolbar = modest_toolkit_factory_create_isearch_toolbar (modest_runtime_get_toolkit_factory (),
853 modest_window_add_toolbar (MODEST_WINDOW (obj), GTK_TOOLBAR (priv->isearch_toolbar));
854 gtk_widget_set_no_show_all (priv->isearch_toolbar, TRUE);
855 g_signal_connect (G_OBJECT (priv->isearch_toolbar), "isearch-close",
856 G_CALLBACK (modest_msg_view_window_isearch_toolbar_close), obj);
857 g_signal_connect (G_OBJECT (priv->isearch_toolbar), "isearch-search",
858 G_CALLBACK (modest_msg_view_window_isearch_toolbar_search), obj);
859 priv->last_search = NULL;
861 /* Init the clipboard actions dim status */
862 modest_msg_view_grab_focus(MODEST_MSG_VIEW (priv->msg_view));
864 update_window_title (MODEST_MSG_VIEW_WINDOW (obj));
869 /* FIXME: parameter checks */
871 modest_msg_view_window_new_with_header_model (TnyMsg *msg,
872 const gchar *modest_account_name,
873 const gchar *mailbox,
874 const gchar *msg_uid,
876 GtkTreeRowReference *row_reference)
878 ModestMsgViewWindow *window = NULL;
879 ModestMsgViewWindowPrivate *priv = NULL;
880 TnyFolder *header_folder = NULL;
881 ModestHeaderView *header_view = NULL;
882 ModestWindowMgr *mgr = NULL;
885 modest_tny_mime_part_to_string (TNY_MIME_PART (msg), 0);
888 mgr = modest_runtime_get_window_mgr ();
889 window = MODEST_MSG_VIEW_WINDOW (modest_window_mgr_get_msg_view_window (mgr));
890 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), NULL);
892 modest_msg_view_window_construct (window, modest_account_name, mailbox, msg_uid);
894 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
896 /* Remember the message list's TreeModel so we can detect changes
897 * and change the list selection when necessary: */
898 header_folder = modest_header_view_get_folder (header_view);
900 priv->is_outbox = (modest_tny_folder_guess_folder_type (header_folder) ==
901 TNY_FOLDER_TYPE_OUTBOX);
902 priv->header_folder_id = tny_folder_get_id (header_folder);
903 g_object_unref(header_folder);
906 /* Setup row references and connect signals */
907 priv->header_model = g_object_ref (model);
909 if (row_reference && gtk_tree_row_reference_valid (row_reference)) {
910 priv->row_reference = gtk_tree_row_reference_copy (row_reference);
911 priv->next_row_reference = gtk_tree_row_reference_copy (row_reference);
912 select_next_valid_row (model, &(priv->next_row_reference), TRUE, priv->is_outbox);
914 priv->row_reference = NULL;
915 priv->next_row_reference = NULL;
918 /* Connect signals */
919 priv->row_changed_handler =
920 g_signal_connect (GTK_TREE_MODEL(model), "row-changed",
921 G_CALLBACK(modest_msg_view_window_on_row_changed),
923 priv->row_deleted_handler =
924 g_signal_connect (GTK_TREE_MODEL(model), "row-deleted",
925 G_CALLBACK(modest_msg_view_window_on_row_deleted),
927 priv->row_inserted_handler =
928 g_signal_connect (GTK_TREE_MODEL(model), "row-inserted",
929 G_CALLBACK(modest_msg_view_window_on_row_inserted),
931 priv->rows_reordered_handler =
932 g_signal_connect(GTK_TREE_MODEL(model), "rows-reordered",
933 G_CALLBACK(modest_msg_view_window_on_row_reordered),
936 if (header_view != NULL){
937 modest_header_view_add_observer(header_view,
938 MODEST_HEADER_VIEW_OBSERVER(window));
941 tny_msg_view_set_msg (TNY_MSG_VIEW (priv->msg_view), msg);
942 update_window_title (MODEST_MSG_VIEW_WINDOW (window));
943 update_branding (MODEST_MSG_VIEW_WINDOW (window));
945 /* gtk_widget_show_all (GTK_WIDGET (window)); */
946 modest_msg_view_window_update_priority (window);
947 /* Check dimming rules */
948 modest_ui_actions_check_toolbar_dimming_rules (MODEST_WINDOW (window));
949 modest_ui_actions_check_menu_dimming_rules (MODEST_WINDOW (window));
950 modest_window_check_dimming_rules_group (MODEST_WINDOW (window), MODEST_DIMMING_RULES_CLIPBOARD);
952 return MODEST_WINDOW(window);
956 modest_msg_view_window_new_from_uid (const gchar *modest_account_name,
957 const gchar *mailbox,
958 const gchar *msg_uid)
960 ModestMsgViewWindow *window = NULL;
961 ModestMsgViewWindowPrivate *priv = NULL;
962 ModestWindowMgr *mgr = NULL;
964 TnyAccount *account = NULL;
966 mgr = modest_runtime_get_window_mgr ();
967 window = MODEST_MSG_VIEW_WINDOW (modest_window_mgr_get_msg_view_window (mgr));
968 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), NULL);
970 modest_msg_view_window_construct (window, modest_account_name, mailbox, msg_uid);
972 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
974 is_merge = g_str_has_prefix (msg_uid, "merge:");
976 /* Get the account */
978 account = tny_account_store_find_account (TNY_ACCOUNT_STORE (modest_runtime_get_account_store ()),
981 if (is_merge || account) {
982 TnyFolder *folder = NULL;
984 /* Try to get the message, if it's already downloaded
985 we don't need to connect */
987 folder = modest_tny_folder_store_find_folder_from_uri (TNY_FOLDER_STORE (account), msg_uid);
989 ModestTnyAccountStore *account_store;
990 ModestTnyLocalFoldersAccount *local_folders_account;
992 account_store = modest_runtime_get_account_store ();
993 local_folders_account = MODEST_TNY_LOCAL_FOLDERS_ACCOUNT (
994 modest_tny_account_store_get_local_folders_account (account_store));
995 folder = modest_tny_local_folders_account_get_merged_outbox (local_folders_account);
996 g_object_unref (local_folders_account);
1000 gboolean device_online;
1002 device = modest_runtime_get_device();
1003 device_online = tny_device_is_online (device);
1004 if (device_online) {
1005 message_reader (window, priv, NULL, msg_uid, folder, NULL);
1007 TnyMsg *msg = tny_folder_find_msg (folder, msg_uid, NULL);
1009 tny_msg_view_set_msg (TNY_MSG_VIEW (priv->msg_view), msg);
1010 update_window_title (MODEST_MSG_VIEW_WINDOW (window));
1011 update_branding (MODEST_MSG_VIEW_WINDOW (window));
1012 g_object_unref (msg);
1013 /* Sync flags to server */
1014 sync_flags (MODEST_MSG_VIEW_WINDOW (window));
1016 message_reader (window, priv, NULL, msg_uid, folder, NULL);
1019 g_object_unref (folder);
1024 /* Check dimming rules */
1025 modest_ui_actions_check_toolbar_dimming_rules (MODEST_WINDOW (window));
1026 modest_ui_actions_check_menu_dimming_rules (MODEST_WINDOW (window));
1027 modest_window_check_dimming_rules_group (MODEST_WINDOW (window), MODEST_DIMMING_RULES_CLIPBOARD);
1029 return MODEST_WINDOW(window);
1033 modest_msg_view_window_new_from_header_view (ModestHeaderView *header_view,
1034 const gchar *modest_account_name,
1035 const gchar *mailbox,
1036 const gchar *msg_uid,
1037 GtkTreeRowReference *row_reference)
1039 ModestMsgViewWindow *window = NULL;
1040 ModestMsgViewWindowPrivate *priv = NULL;
1041 TnyFolder *header_folder = NULL;
1042 ModestWindowMgr *mgr = NULL;
1046 mgr = modest_runtime_get_window_mgr ();
1047 window = MODEST_MSG_VIEW_WINDOW (modest_window_mgr_get_msg_view_window (mgr));
1048 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), NULL);
1050 modest_msg_view_window_construct (window, modest_account_name, mailbox, msg_uid);
1052 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1054 /* Remember the message list's TreeModel so we can detect changes
1055 * and change the list selection when necessary: */
1057 if (header_view != NULL){
1058 header_folder = modest_header_view_get_folder(header_view);
1059 /* This could happen if the header folder was
1060 unseleted before opening this msg window (for
1061 example if the user selects an account in the
1062 folder view of the main window */
1063 if (header_folder) {
1064 priv->is_outbox = (modest_tny_folder_guess_folder_type (header_folder) ==
1065 TNY_FOLDER_TYPE_OUTBOX);
1066 priv->header_folder_id = tny_folder_get_id(header_folder);
1067 g_object_unref(header_folder);
1071 /* Setup row references and connect signals */
1072 priv->header_model = gtk_tree_view_get_model (GTK_TREE_VIEW (header_view));
1073 g_object_ref (priv->header_model);
1075 if (row_reference && gtk_tree_row_reference_valid (row_reference)) {
1076 priv->row_reference = gtk_tree_row_reference_copy (row_reference);
1077 priv->next_row_reference = gtk_tree_row_reference_copy (row_reference);
1078 select_next_valid_row (priv->header_model, &(priv->next_row_reference), TRUE, priv->is_outbox);
1080 priv->row_reference = NULL;
1081 priv->next_row_reference = NULL;
1084 /* Connect signals */
1085 priv->row_changed_handler =
1086 g_signal_connect (GTK_TREE_MODEL(priv->header_model), "row-changed",
1087 G_CALLBACK(modest_msg_view_window_on_row_changed),
1089 priv->row_deleted_handler =
1090 g_signal_connect (GTK_TREE_MODEL(priv->header_model), "row-deleted",
1091 G_CALLBACK(modest_msg_view_window_on_row_deleted),
1093 priv->row_inserted_handler =
1094 g_signal_connect (GTK_TREE_MODEL(priv->header_model), "row-inserted",
1095 G_CALLBACK(modest_msg_view_window_on_row_inserted),
1097 priv->rows_reordered_handler =
1098 g_signal_connect(GTK_TREE_MODEL(priv->header_model), "rows-reordered",
1099 G_CALLBACK(modest_msg_view_window_on_row_reordered),
1102 if (header_view != NULL){
1103 modest_header_view_add_observer(header_view,
1104 MODEST_HEADER_VIEW_OBSERVER(window));
1107 tny_msg_view_set_msg (TNY_MSG_VIEW (priv->msg_view), NULL);
1108 update_branding (MODEST_MSG_VIEW_WINDOW (window));
1110 if (priv->row_reference) {
1111 path = gtk_tree_row_reference_get_path (priv->row_reference);
1112 if (gtk_tree_model_get_iter (priv->header_model, &iter, path)) {
1114 gtk_tree_model_get (priv->header_model, &iter,
1115 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
1117 message_reader (window, priv, header, NULL, NULL, priv->row_reference);
1118 g_object_unref (header);
1120 gtk_tree_path_free (path);
1122 /* Check dimming rules */
1123 modest_ui_actions_check_toolbar_dimming_rules (MODEST_WINDOW (window));
1124 modest_ui_actions_check_menu_dimming_rules (MODEST_WINDOW (window));
1125 modest_window_check_dimming_rules_group (MODEST_WINDOW (window), MODEST_DIMMING_RULES_CLIPBOARD);
1127 return MODEST_WINDOW(window);
1131 modest_msg_view_window_new_for_search_result (TnyMsg *msg,
1132 const gchar *modest_account_name,
1133 const gchar *mailbox,
1134 const gchar *msg_uid)
1136 ModestMsgViewWindow *window = NULL;
1137 ModestMsgViewWindowPrivate *priv = NULL;
1138 ModestWindowMgr *mgr = NULL;
1140 mgr = modest_runtime_get_window_mgr ();
1141 window = MODEST_MSG_VIEW_WINDOW (modest_window_mgr_get_msg_view_window (mgr));
1142 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), NULL);
1143 modest_msg_view_window_construct (window, modest_account_name, mailbox, msg_uid);
1145 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1147 /* Remember that this is a search result,
1148 * so we can disable some UI appropriately: */
1149 priv->is_search_result = TRUE;
1151 tny_msg_view_set_msg (TNY_MSG_VIEW (priv->msg_view), msg);
1152 update_branding (MODEST_MSG_VIEW_WINDOW (window));
1154 update_window_title (window);
1155 /* gtk_widget_show_all (GTK_WIDGET (window));*/
1156 modest_msg_view_window_update_priority (window);
1158 /* Check dimming rules */
1159 modest_ui_actions_check_toolbar_dimming_rules (MODEST_WINDOW (window));
1160 modest_ui_actions_check_menu_dimming_rules (MODEST_WINDOW (window));
1161 modest_window_check_dimming_rules_group (MODEST_WINDOW (window), MODEST_DIMMING_RULES_CLIPBOARD);
1163 return MODEST_WINDOW(window);
1167 modest_msg_view_window_is_other_body (ModestMsgViewWindow *self)
1169 ModestMsgViewWindowPrivate *priv = NULL;
1171 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self), FALSE);
1172 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
1174 return (priv->other_body != NULL);
1178 modest_msg_view_window_new_with_other_body (TnyMsg *msg,
1179 TnyMimePart *other_body,
1180 const gchar *modest_account_name,
1181 const gchar *mailbox,
1182 const gchar *msg_uid)
1184 GObject *obj = NULL;
1185 ModestMsgViewWindowPrivate *priv;
1186 ModestWindowMgr *mgr = NULL;
1188 g_return_val_if_fail (msg, NULL);
1189 mgr = modest_runtime_get_window_mgr ();
1190 obj = G_OBJECT (modest_window_mgr_get_msg_view_window (mgr));
1191 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (obj);
1192 modest_msg_view_window_construct (MODEST_MSG_VIEW_WINDOW (obj),
1193 modest_account_name, mailbox, msg_uid);
1196 priv->other_body = g_object_ref (other_body);
1197 modest_msg_view_set_msg_with_other_body (MODEST_MSG_VIEW (priv->msg_view), msg, other_body);
1199 tny_msg_view_set_msg (TNY_MSG_VIEW (priv->msg_view), msg);
1201 update_window_title (MODEST_MSG_VIEW_WINDOW (obj));
1202 update_branding (MODEST_MSG_VIEW_WINDOW (obj));
1204 /* gtk_widget_show_all (GTK_WIDGET (obj)); */
1206 /* Check dimming rules */
1207 modest_ui_actions_check_toolbar_dimming_rules (MODEST_WINDOW (obj));
1208 modest_ui_actions_check_menu_dimming_rules (MODEST_WINDOW (obj));
1209 modest_window_check_dimming_rules_group (MODEST_WINDOW (obj), MODEST_DIMMING_RULES_CLIPBOARD);
1211 return MODEST_WINDOW(obj);
1215 modest_msg_view_window_new_for_attachment (TnyMsg *msg,
1216 const gchar *modest_account_name,
1217 const gchar *mailbox,
1218 const gchar *msg_uid)
1220 return modest_msg_view_window_new_with_other_body (msg, NULL, modest_account_name, mailbox, msg_uid);
1224 modest_msg_view_window_on_row_changed (GtkTreeModel *header_model,
1227 ModestMsgViewWindow *window)
1229 check_dimming_rules_after_change (window);
1233 modest_msg_view_window_on_row_deleted(GtkTreeModel *header_model,
1235 ModestMsgViewWindow *window)
1237 check_dimming_rules_after_change (window);
1239 /* The window could have dissapeared */
1242 check_dimming_rules_after_change (ModestMsgViewWindow *window)
1244 modest_ui_actions_check_menu_dimming_rules (MODEST_WINDOW (window));
1245 modest_ui_actions_check_toolbar_dimming_rules (MODEST_WINDOW (window));
1249 /* On insertions we check if the folder still has the message we are
1250 * showing or do not. If do not, we do nothing. Which means we are still
1251 * not attached to any header folder and thus next/prev buttons are
1252 * still dimmed. Once the message that is shown by msg-view is found, the
1253 * new model of header-view will be attached and the references will be set.
1254 * On each further insertions dimming rules will be checked. However
1255 * this requires extra CPU time at least works.
1256 * (An message might be deleted from TnyFolder and thus will not be
1257 * inserted into the model again for example if it is removed by the
1258 * imap server and the header view is refreshed.)
1261 modest_msg_view_window_on_row_inserted (GtkTreeModel *model,
1262 GtkTreePath *tree_path,
1263 GtkTreeIter *tree_iter,
1264 ModestMsgViewWindow *window)
1266 ModestMsgViewWindowPrivate *priv = NULL;
1267 TnyHeader *header = NULL;
1269 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window));
1270 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1272 g_assert (model == priv->header_model);
1274 /* Check if the newly inserted message is the same we are actually
1275 * showing. IF not, we should remain detached from the header model
1276 * and thus prev and next toolbar buttons should remain dimmed. */
1277 gtk_tree_model_get (model, tree_iter,
1278 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
1281 if (TNY_IS_HEADER (header)) {
1284 uid = modest_tny_folder_get_header_unique_id (header);
1285 if (!g_str_equal(priv->msg_uid, uid)) {
1286 check_dimming_rules_after_change (window);
1288 g_object_unref (G_OBJECT(header));
1292 g_object_unref(G_OBJECT(header));
1295 if (priv->row_reference) {
1296 gtk_tree_row_reference_free (priv->row_reference);
1299 /* Setup row_reference for the actual msg. */
1300 priv->row_reference = gtk_tree_row_reference_new (priv->header_model, tree_path);
1301 if (priv->row_reference == NULL) {
1302 g_warning("%s: No reference for msg header item.", __FUNCTION__);
1306 /* Now set up next_row_reference. */
1307 if (priv->next_row_reference) {
1308 gtk_tree_row_reference_free (priv->next_row_reference);
1311 priv->next_row_reference = gtk_tree_row_reference_copy (priv->row_reference);
1312 select_next_valid_row (priv->header_model,
1313 &(priv->next_row_reference), FALSE, priv->is_outbox);
1315 /* Connect the remaining callbacks to become able to detect
1316 * changes in header-view. */
1317 priv->row_changed_handler =
1318 g_signal_connect (priv->header_model, "row-changed",
1319 G_CALLBACK (modest_msg_view_window_on_row_changed),
1321 priv->row_deleted_handler =
1322 g_signal_connect (priv->header_model, "row-deleted",
1323 G_CALLBACK (modest_msg_view_window_on_row_deleted),
1325 priv->rows_reordered_handler =
1326 g_signal_connect (priv->header_model, "rows-reordered",
1327 G_CALLBACK (modest_msg_view_window_on_row_reordered),
1330 check_dimming_rules_after_change (window);
1334 modest_msg_view_window_on_row_reordered (GtkTreeModel *header_model,
1338 ModestMsgViewWindow *window)
1340 ModestMsgViewWindowPrivate *priv = NULL;
1341 gboolean already_changed = FALSE;
1343 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(window);
1345 /* If the current row was reordered select the proper next
1346 valid row. The same if the next row reference changes */
1347 if (!priv->row_reference ||
1348 !gtk_tree_row_reference_valid (priv->row_reference))
1351 if (priv->next_row_reference &&
1352 gtk_tree_row_reference_valid (priv->next_row_reference)) {
1353 GtkTreePath *cur, *next;
1354 /* Check that the order is still the correct one */
1355 cur = gtk_tree_row_reference_get_path (priv->row_reference);
1356 next = gtk_tree_row_reference_get_path (priv->next_row_reference);
1357 gtk_tree_path_next (cur);
1358 if (gtk_tree_path_compare (cur, next) != 0) {
1359 gtk_tree_row_reference_free (priv->next_row_reference);
1360 priv->next_row_reference = gtk_tree_row_reference_copy (priv->row_reference);
1361 select_next_valid_row (header_model, &(priv->next_row_reference), FALSE, priv->is_outbox);
1362 already_changed = TRUE;
1364 gtk_tree_path_free (cur);
1365 gtk_tree_path_free (next);
1367 if (priv->next_row_reference)
1368 gtk_tree_row_reference_free (priv->next_row_reference);
1369 /* Update next row reference */
1370 priv->next_row_reference = gtk_tree_row_reference_copy (priv->row_reference);
1371 select_next_valid_row (header_model, &(priv->next_row_reference), FALSE, priv->is_outbox);
1372 already_changed = TRUE;
1375 check_dimming_rules_after_change (window);
1378 /* The modest_msg_view_window_update_model_replaced implements update
1379 * function for ModestHeaderViewObserver. Checks whether the TnyFolder
1380 * actually belongs to the header-view is the same as the TnyFolder of
1381 * the message of msg-view or not. If they are different, there is
1382 * nothing to do. If they are the same, then the model has replaced and
1383 * the reference in msg-view shall be replaced from the old model to
1384 * the new model. In this case the view will be detached from it's
1385 * header folder. From this point the next/prev buttons are dimmed.
1388 modest_msg_view_window_update_model_replaced (ModestHeaderViewObserver *observer,
1389 GtkTreeModel *model,
1390 const gchar *tny_folder_id)
1392 ModestMsgViewWindowPrivate *priv = NULL;
1393 ModestMsgViewWindow *window = NULL;
1395 g_assert(MODEST_IS_HEADER_VIEW_OBSERVER(observer));
1396 g_assert(MODEST_IS_MSG_VIEW_WINDOW(observer));
1398 window = MODEST_MSG_VIEW_WINDOW(observer);
1399 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(window);
1401 /* If there is an other folder in the header-view then we do
1402 * not care about it's model (msg list). Else if the
1403 * header-view shows the folder the msg shown by us is in, we
1404 * shall replace our model reference and make some check. */
1405 if(model == NULL || tny_folder_id == NULL ||
1406 (priv->header_folder_id && !g_str_equal(tny_folder_id, priv->header_folder_id)))
1409 /* Model is changed(replaced), so we should forget the old
1410 * one. Because there might be other references and there
1411 * might be some change on the model even if we unreferenced
1412 * it, we need to disconnect our signals here. */
1413 if (priv->header_model) {
1414 if (g_signal_handler_is_connected(G_OBJECT (priv->header_model),
1415 priv->row_changed_handler))
1416 g_signal_handler_disconnect(G_OBJECT (priv->header_model),
1417 priv->row_changed_handler);
1418 if (g_signal_handler_is_connected(G_OBJECT (priv->header_model),
1419 priv->row_deleted_handler))
1420 g_signal_handler_disconnect(G_OBJECT (priv->header_model),
1421 priv->row_deleted_handler);
1422 if (g_signal_handler_is_connected(G_OBJECT (priv->header_model),
1423 priv->row_inserted_handler))
1424 g_signal_handler_disconnect(G_OBJECT (priv->header_model),
1425 priv->row_inserted_handler);
1426 if (g_signal_handler_is_connected(G_OBJECT (priv->header_model),
1427 priv->rows_reordered_handler))
1428 g_signal_handler_disconnect(G_OBJECT (priv->header_model),
1429 priv->rows_reordered_handler);
1432 if (priv->row_reference)
1433 gtk_tree_row_reference_free (priv->row_reference);
1434 if (priv->next_row_reference)
1435 gtk_tree_row_reference_free (priv->next_row_reference);
1436 g_object_unref(priv->header_model);
1439 priv->row_changed_handler = 0;
1440 priv->row_deleted_handler = 0;
1441 priv->row_inserted_handler = 0;
1442 priv->rows_reordered_handler = 0;
1443 priv->next_row_reference = NULL;
1444 priv->row_reference = NULL;
1445 priv->header_model = NULL;
1448 priv->header_model = g_object_ref (model);
1450 /* Also we must connect to the new model for row insertions.
1451 * Only for insertions now. We will need other ones only after
1452 * the msg is show by msg-view is added to the new model. */
1453 priv->row_inserted_handler =
1454 g_signal_connect (priv->header_model, "row-inserted",
1455 G_CALLBACK(modest_msg_view_window_on_row_inserted),
1458 modest_ui_actions_check_menu_dimming_rules(MODEST_WINDOW(window));
1459 modest_ui_actions_check_toolbar_dimming_rules(MODEST_WINDOW(window));
1463 modest_msg_view_window_toolbar_on_transfer_mode (ModestMsgViewWindow *self)
1465 ModestMsgViewWindowPrivate *priv= NULL;
1467 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self), FALSE);
1468 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
1470 return priv->progress_hint;
1474 modest_msg_view_window_get_header (ModestMsgViewWindow *self)
1476 ModestMsgViewWindowPrivate *priv= NULL;
1478 TnyHeader *header = NULL;
1479 GtkTreePath *path = NULL;
1482 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self), NULL);
1483 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
1485 /* If the message was not obtained from a treemodel,
1486 * for instance if it was opened directly by the search UI:
1488 if (priv->header_model == NULL ||
1489 priv->row_reference == NULL ||
1490 !gtk_tree_row_reference_valid (priv->row_reference)) {
1491 msg = modest_msg_view_window_get_message (self);
1493 header = tny_msg_get_header (msg);
1494 g_object_unref (msg);
1499 /* Get iter of the currently selected message in the header view: */
1500 path = gtk_tree_row_reference_get_path (priv->row_reference);
1501 g_return_val_if_fail (path != NULL, NULL);
1502 gtk_tree_model_get_iter (priv->header_model,
1506 /* Get current message header */
1507 gtk_tree_model_get (priv->header_model, &iter,
1508 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
1511 gtk_tree_path_free (path);
1516 modest_msg_view_window_get_message (ModestMsgViewWindow *self)
1518 ModestMsgViewWindowPrivate *priv;
1520 g_return_val_if_fail (self, NULL);
1522 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(self);
1524 return tny_msg_view_get_msg (TNY_MSG_VIEW (priv->msg_view));
1528 modest_msg_view_window_get_message_uid (ModestMsgViewWindow *self)
1530 ModestMsgViewWindowPrivate *priv;
1532 g_return_val_if_fail (self, NULL);
1534 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
1536 return (const gchar*) priv->msg_uid;
1539 /* Used for the Ctrl+F accelerator */
1541 modest_msg_view_window_toggle_isearch_toolbar (GtkWidget *obj,
1544 ModestMsgViewWindow *window = MODEST_MSG_VIEW_WINDOW (data);
1545 ModestMsgViewWindowPrivate *priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1547 if (GTK_WIDGET_VISIBLE (priv->isearch_toolbar)) {
1548 modest_msg_view_window_isearch_toolbar_close (obj, data);
1550 modest_msg_view_window_show_isearch_toolbar (obj, data);
1554 /* Handler for menu option */
1556 modest_msg_view_window_show_isearch_toolbar (GtkWidget *obj,
1559 ModestMsgViewWindow *window = MODEST_MSG_VIEW_WINDOW (data);
1560 ModestMsgViewWindowPrivate *priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1562 gtk_widget_show (priv->isearch_toolbar);
1563 modest_isearch_toolbar_highlight_entry (MODEST_ISEARCH_TOOLBAR (priv->isearch_toolbar), TRUE);
1566 /* Handler for click on the "X" close button in isearch toolbar */
1568 modest_msg_view_window_isearch_toolbar_close (GtkWidget *widget,
1569 ModestMsgViewWindow *obj)
1571 ModestMsgViewWindowPrivate *priv;
1573 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (obj);
1576 gtk_widget_hide (priv->isearch_toolbar);
1577 modest_msg_view_grab_focus (MODEST_MSG_VIEW (priv->msg_view));
1581 modest_msg_view_window_isearch_toolbar_search (GtkWidget *widget,
1582 ModestMsgViewWindow *obj)
1584 const gchar *current_search;
1585 ModestMsgViewWindowPrivate *priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (obj);
1587 if (modest_mime_part_view_is_empty (MODEST_MIME_PART_VIEW (priv->msg_view))) {
1588 modest_platform_system_banner (NULL, NULL, _("mail_ib_nothing_to_find"));
1592 current_search = modest_isearch_toolbar_get_search (MODEST_ISEARCH_TOOLBAR (widget));
1594 if ((current_search == NULL) || (strcmp (current_search, "") == 0)) {
1595 modest_platform_system_banner (NULL, NULL, _CS_FIND_REP_ENTER_TEXT);
1599 if ((priv->last_search == NULL) || (strcmp (priv->last_search, current_search) != 0)) {
1601 g_free (priv->last_search);
1602 priv->last_search = g_strdup (current_search);
1603 result = modest_isearch_view_search (MODEST_ISEARCH_VIEW (priv->msg_view),
1606 modest_platform_system_banner (NULL, NULL,
1607 _HL_IB_FIND_NO_MATCHES);
1608 g_free (priv->last_search);
1609 priv->last_search = NULL;
1611 modest_isearch_toolbar_highlight_entry (MODEST_ISEARCH_TOOLBAR (priv->isearch_toolbar), TRUE);
1614 if (!modest_isearch_view_search_next (MODEST_ISEARCH_VIEW (priv->msg_view))) {
1615 modest_platform_system_banner (NULL, NULL,
1616 _HL_IB_FIND_COMPLETE);
1617 g_free (priv->last_search);
1618 priv->last_search = NULL;
1620 modest_isearch_toolbar_highlight_entry (MODEST_ISEARCH_TOOLBAR (priv->isearch_toolbar), TRUE);
1627 modest_msg_view_window_set_zoom (ModestWindow *window,
1630 ModestMsgViewWindowPrivate *priv;
1631 ModestWindowPrivate *parent_priv;
1633 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window));
1635 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1636 parent_priv = MODEST_WINDOW_GET_PRIVATE (window);
1637 modest_zoomable_set_zoom (MODEST_ZOOMABLE (priv->msg_view), zoom);
1642 modest_msg_view_window_get_zoom (ModestWindow *window)
1644 ModestMsgViewWindowPrivate *priv;
1646 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), 1.0);
1648 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1649 return modest_zoomable_get_zoom (MODEST_ZOOMABLE (priv->msg_view));
1653 modest_msg_view_window_zoom_plus (ModestWindow *window)
1656 ModestMsgViewWindowPrivate *priv;
1660 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), 1.0);
1661 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1663 zoom_level = modest_zoomable_get_zoom (MODEST_ZOOMABLE (priv->msg_view));
1665 if (zoom_level >= 2.0) {
1666 modest_platform_system_banner (NULL, NULL,
1667 _CS_MAX_ZOOM_LEVEL_REACHED);
1669 } else if (zoom_level >= 1.5) {
1671 } else if (zoom_level >= 1.2) {
1673 } else if (zoom_level >= 1.0) {
1675 } else if (zoom_level >= 0.8) {
1677 } else if (zoom_level >= 0.5) {
1683 /* set zoom level */
1684 int_zoom = (gint) rint (zoom_level*100.0+0.1);
1685 banner_text = g_strdup_printf (_HL_IB_ZOOM, int_zoom);
1686 modest_platform_information_banner (GTK_WIDGET (window), NULL, banner_text);
1687 g_free (banner_text);
1688 modest_zoomable_set_zoom (MODEST_ZOOMABLE (priv->msg_view), zoom_level);
1694 modest_msg_view_window_zoom_minus (ModestWindow *window)
1697 ModestMsgViewWindowPrivate *priv;
1701 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), 1.0);
1702 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1704 zoom_level = modest_zoomable_get_zoom (MODEST_ZOOMABLE (priv->msg_view));
1706 if (zoom_level <= 0.5) {
1707 modest_platform_system_banner (NULL, NULL,
1708 _CS_MIN_ZOOM_LEVEL_REACHED);
1710 } else if (zoom_level <= 0.8) {
1712 } else if (zoom_level <= 1.0) {
1714 } else if (zoom_level <= 1.2) {
1716 } else if (zoom_level <= 1.5) {
1718 } else if (zoom_level <= 2.0) {
1724 /* set zoom level */
1725 int_zoom = (gint) rint (zoom_level*100.0+0.1);
1726 banner_text = g_strdup_printf (_HL_IB_ZOOM, int_zoom);
1727 modest_platform_information_banner (GTK_WIDGET (window), NULL, banner_text);
1728 g_free (banner_text);
1729 modest_zoomable_set_zoom (MODEST_ZOOMABLE (priv->msg_view), zoom_level);
1735 modest_msg_view_window_key_event (GtkWidget *window,
1741 focus = gtk_window_get_focus (GTK_WINDOW (window));
1743 /* for the isearch toolbar case */
1744 if (focus && GTK_IS_ENTRY (focus)) {
1745 if (event->keyval == GDK_BackSpace) {
1747 copy = gdk_event_copy ((GdkEvent *) event);
1748 gtk_widget_event (focus, copy);
1749 gdk_event_free (copy);
1759 modest_msg_view_window_last_message_selected (ModestMsgViewWindow *window)
1762 ModestMsgViewWindowPrivate *priv;
1763 GtkTreeIter tmp_iter;
1764 gboolean is_last_selected;
1766 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), TRUE);
1767 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1769 /*if no model (so no rows at all), then virtually we are the last*/
1770 if (!priv->header_model || !priv->row_reference)
1773 if (!gtk_tree_row_reference_valid (priv->row_reference))
1776 path = gtk_tree_row_reference_get_path (priv->row_reference);
1780 is_last_selected = TRUE;
1781 while (is_last_selected) {
1783 gtk_tree_path_next (path);
1784 if (!gtk_tree_model_get_iter (priv->header_model, &tmp_iter, path))
1786 gtk_tree_model_get (priv->header_model, &tmp_iter,
1787 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
1790 if (msg_is_visible (header, priv->is_outbox))
1791 is_last_selected = FALSE;
1792 g_object_unref(G_OBJECT(header));
1795 gtk_tree_path_free (path);
1796 return is_last_selected;
1800 modest_msg_view_window_has_headers_model (ModestMsgViewWindow *window)
1802 ModestMsgViewWindowPrivate *priv;
1804 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), TRUE);
1805 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1807 return priv->header_model != NULL;
1811 modest_msg_view_window_is_search_result (ModestMsgViewWindow *window)
1813 ModestMsgViewWindowPrivate *priv;
1815 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), TRUE);
1816 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1818 return priv->is_search_result;
1822 msg_is_visible (TnyHeader *header, gboolean check_outbox)
1824 if ((tny_header_get_flags(header) & TNY_HEADER_FLAG_DELETED))
1826 if (!check_outbox) {
1829 ModestTnySendQueueStatus status;
1830 status = modest_tny_all_send_queues_get_msg_status (header);
1831 return ((status != MODEST_TNY_SEND_QUEUE_FAILED) &&
1832 (status != MODEST_TNY_SEND_QUEUE_SENDING));
1837 modest_msg_view_window_first_message_selected (ModestMsgViewWindow *window)
1840 ModestMsgViewWindowPrivate *priv;
1841 gboolean is_first_selected;
1842 GtkTreeIter tmp_iter;
1844 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), TRUE);
1845 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1847 /*if no model (so no rows at all), then virtually we are the first*/
1848 if (!priv->header_model || !priv->row_reference)
1851 if (!gtk_tree_row_reference_valid (priv->row_reference))
1854 path = gtk_tree_row_reference_get_path (priv->row_reference);
1858 is_first_selected = TRUE;
1859 while (is_first_selected) {
1861 if(!gtk_tree_path_prev (path))
1863 /* Here the 'if' is needless for logic, but let make sure
1864 * iter is valid for gtk_tree_model_get. */
1865 if (!gtk_tree_model_get_iter (priv->header_model, &tmp_iter, path))
1867 gtk_tree_model_get (priv->header_model, &tmp_iter,
1868 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
1871 if (msg_is_visible (header, priv->is_outbox))
1872 is_first_selected = FALSE;
1873 g_object_unref(G_OBJECT(header));
1876 gtk_tree_path_free (path);
1877 return is_first_selected;
1884 GtkTreeRowReference *row_reference;
1888 message_reader_performer (gboolean canceled,
1890 GtkWindow *parent_window,
1891 TnyAccount *account,
1894 ModestMailOperation *mail_op = NULL;
1895 MsgReaderInfo *info;
1897 info = (MsgReaderInfo *) user_data;
1898 if (canceled || err) {
1899 update_window_title (MODEST_MSG_VIEW_WINDOW (parent_window));
1900 modest_ui_actions_on_close_window (NULL, MODEST_WINDOW (parent_window));
1904 /* Register the header - it'll be unregistered in the callback */
1906 modest_window_mgr_register_header (modest_runtime_get_window_mgr (), info->header, NULL);
1908 /* New mail operation */
1909 mail_op = modest_mail_operation_new_with_error_handling (G_OBJECT(parent_window),
1910 modest_ui_actions_disk_operations_error_handler,
1913 modest_mail_operation_queue_add (modest_runtime_get_mail_operation_queue (), mail_op);
1915 modest_mail_operation_get_msg (mail_op, info->header, TRUE, view_msg_cb, info->row_reference);
1917 modest_mail_operation_find_msg (mail_op, info->folder, info->msg_uid, TRUE, view_msg_cb, NULL);
1918 g_object_unref (mail_op);
1920 /* Update dimming rules */
1921 modest_ui_actions_check_toolbar_dimming_rules (MODEST_WINDOW (parent_window));
1922 modest_ui_actions_check_menu_dimming_rules (MODEST_WINDOW (parent_window));
1925 /* Frees. The row_reference will be freed by the view_msg_cb callback */
1926 g_free (info->msg_uid);
1928 g_object_unref (info->folder);
1930 g_object_unref (info->header);
1931 g_slice_free (MsgReaderInfo, info);
1936 * Reads the message whose summary item is @header. It takes care of
1937 * several things, among others:
1939 * If the message was not previously downloaded then ask the user
1940 * before downloading. If there is no connection launch the connection
1941 * dialog. Update toolbar dimming rules.
1943 * Returns: TRUE if the mail operation was started, otherwise if the
1944 * user do not want to download the message, or if the user do not
1945 * want to connect, then the operation is not issued
1948 message_reader (ModestMsgViewWindow *window,
1949 ModestMsgViewWindowPrivate *priv,
1951 const gchar *msg_uid,
1953 GtkTreeRowReference *row_reference)
1955 ModestWindowMgr *mgr;
1956 TnyAccount *account = NULL;
1957 MsgReaderInfo *info;
1959 /* We set the header from model while we're loading */
1960 tny_header_view_set_header (TNY_HEADER_VIEW (priv->msg_view), header);
1961 modest_window_set_title (MODEST_WINDOW (window), _CS_UPDATING);
1967 g_object_ref (folder);
1969 mgr = modest_runtime_get_window_mgr ();
1970 /* Msg download completed */
1971 if (!header || !(tny_header_get_flags (header) & TNY_HEADER_FLAG_CACHED)) {
1973 /* Ask the user if he wants to download the message if
1975 if (!tny_device_is_online (modest_runtime_get_device())) {
1976 GtkResponseType response;
1978 response = modest_platform_run_confirmation_dialog (GTK_WINDOW (window),
1979 _("mcen_nc_get_msg"));
1980 if (response == GTK_RESPONSE_CANCEL) {
1981 update_window_title (window);
1986 folder = tny_header_get_folder (header);
1988 info = g_slice_new (MsgReaderInfo);
1989 info->msg_uid = g_strdup (msg_uid);
1991 info->header = g_object_ref (header);
1993 info->header = NULL;
1995 info->folder = g_object_ref (folder);
1997 info->folder = NULL;
1998 if (row_reference) {
1999 info->row_reference = gtk_tree_row_reference_copy (row_reference);
2001 info->row_reference = NULL;
2004 /* Offer the connection dialog if necessary */
2005 modest_platform_connect_if_remote_and_perform ((GtkWindow *) window,
2007 TNY_FOLDER_STORE (folder),
2008 message_reader_performer,
2011 g_object_unref (folder);
2017 folder = tny_header_get_folder (header);
2020 account = tny_folder_get_account (folder);
2022 info = g_slice_new (MsgReaderInfo);
2023 info->msg_uid = g_strdup (msg_uid);
2025 info->folder = g_object_ref (folder);
2027 info->folder = NULL;
2029 info->header = g_object_ref (header);
2031 info->header = NULL;
2033 info->row_reference = gtk_tree_row_reference_copy (row_reference);
2035 info->row_reference = NULL;
2037 message_reader_performer (FALSE, NULL, (GtkWindow *) window, account, info);
2039 g_object_unref (account);
2041 g_object_unref (folder);
2047 modest_msg_view_window_select_next_message (ModestMsgViewWindow *window)
2049 ModestMsgViewWindowPrivate *priv;
2050 GtkTreePath *path= NULL;
2051 GtkTreeIter tmp_iter;
2053 gboolean retval = TRUE;
2054 GtkTreeRowReference *row_reference = NULL;
2056 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), FALSE);
2057 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
2059 if (!priv->row_reference)
2062 /* Update the next row reference if it's not valid. This could
2063 happen if for example the header which it was pointing to,
2064 was deleted. The best place to do it is in the row-deleted
2065 handler but the tinymail model do not work like the glib
2066 tree models and reports the deletion when the row is still
2068 if (!gtk_tree_row_reference_valid (priv->next_row_reference)) {
2069 if (priv->next_row_reference) {
2070 gtk_tree_row_reference_free (priv->next_row_reference);
2072 if (gtk_tree_row_reference_valid (priv->row_reference)) {
2073 priv->next_row_reference = gtk_tree_row_reference_copy (priv->row_reference);
2074 select_next_valid_row (priv->header_model, &(priv->next_row_reference), FALSE, priv->is_outbox);
2076 priv->next_row_reference = NULL;
2079 if (priv->next_row_reference)
2080 path = gtk_tree_row_reference_get_path (priv->next_row_reference);
2084 row_reference = gtk_tree_row_reference_copy (priv->next_row_reference);
2086 gtk_tree_model_get_iter (priv->header_model,
2089 gtk_tree_path_free (path);
2091 gtk_tree_model_get (priv->header_model, &tmp_iter,
2092 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
2095 /* Read the message & show it */
2096 if (!message_reader (window, priv, header, NULL, NULL, row_reference)) {
2099 gtk_tree_row_reference_free (row_reference);
2102 g_object_unref (header);
2108 modest_msg_view_window_select_previous_message (ModestMsgViewWindow *window)
2110 ModestMsgViewWindowPrivate *priv = NULL;
2112 gboolean finished = FALSE;
2113 gboolean retval = FALSE;
2115 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), FALSE);
2116 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
2118 if (priv->row_reference && !gtk_tree_row_reference_valid (priv->row_reference)) {
2119 gtk_tree_row_reference_free (priv->row_reference);
2120 priv->row_reference = NULL;
2123 /* Return inmediatly if there is no header model */
2124 if (!priv->header_model || !priv->row_reference)
2127 path = gtk_tree_row_reference_get_path (priv->row_reference);
2128 while (!finished && gtk_tree_path_prev (path)) {
2132 gtk_tree_model_get_iter (priv->header_model, &iter, path);
2133 gtk_tree_model_get (priv->header_model, &iter,
2134 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
2138 if (msg_is_visible (header, priv->is_outbox)) {
2139 GtkTreeRowReference *row_reference;
2140 row_reference = gtk_tree_row_reference_new (priv->header_model, path);
2141 /* Read the message & show it */
2142 retval = message_reader (window, priv, header, NULL, NULL, row_reference);
2143 gtk_tree_row_reference_free (row_reference);
2147 g_object_unref (header);
2151 gtk_tree_path_free (path);
2156 view_msg_cb (ModestMailOperation *mail_op,
2163 ModestMsgViewWindow *self = NULL;
2164 ModestMsgViewWindowPrivate *priv = NULL;
2165 GtkTreeRowReference *row_reference = NULL;
2167 /* Unregister the header (it was registered before creating the mail operation) */
2168 modest_window_mgr_unregister_header (modest_runtime_get_window_mgr (), header);
2170 row_reference = (GtkTreeRowReference *) user_data;
2173 gtk_tree_row_reference_free (row_reference);
2174 self = (ModestMsgViewWindow *) modest_mail_operation_get_source (mail_op);
2176 /* Restore window title */
2177 update_window_title (self);
2178 modest_ui_actions_on_close_window (NULL, MODEST_WINDOW (self));
2179 g_object_unref (self);
2184 /* If there was any error */
2185 if (!modest_ui_actions_msg_retrieval_check (mail_op, header, msg)) {
2187 gtk_tree_row_reference_free (row_reference);
2188 self = (ModestMsgViewWindow *) modest_mail_operation_get_source (mail_op);
2190 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
2191 /* First we check if the parent is a folder window */
2192 if (priv->msg_uid && !modest_window_mgr_get_folder_window (MODEST_WINDOW_MGR (modest_runtime_get_window_mgr ()))) {
2194 TnyAccount *account = NULL;
2195 GtkWidget *header_window = NULL;
2197 is_merge = g_str_has_prefix (priv->msg_uid, "merge:");
2199 /* Get the account */
2201 account = tny_account_store_find_account (TNY_ACCOUNT_STORE (modest_runtime_get_account_store ()),
2204 if (is_merge || account) {
2205 TnyFolder *folder = NULL;
2207 /* Try to get the message, if it's already downloaded
2208 we don't need to connect */
2210 folder = modest_tny_folder_store_find_folder_from_uri (TNY_FOLDER_STORE (account),
2213 ModestTnyAccountStore *account_store;
2214 ModestTnyLocalFoldersAccount *local_folders_account;
2216 account_store = modest_runtime_get_account_store ();
2217 local_folders_account = MODEST_TNY_LOCAL_FOLDERS_ACCOUNT (
2218 modest_tny_account_store_get_local_folders_account (account_store));
2219 folder = modest_tny_local_folders_account_get_merged_outbox (local_folders_account);
2220 g_object_unref (local_folders_account);
2222 if (account) g_object_unref (account);
2225 header_window = (GtkWidget *)
2226 modest_header_window_new (
2228 modest_window_get_active_account (MODEST_WINDOW (self)),
2229 modest_window_get_active_mailbox (MODEST_WINDOW (self)));
2230 if (!modest_window_mgr_register_window (modest_runtime_get_window_mgr (),
2231 MODEST_WINDOW (header_window),
2233 gtk_widget_destroy (GTK_WIDGET (header_window));
2235 gtk_widget_show_all (GTK_WIDGET (header_window));
2237 g_object_unref (folder);
2243 /* Restore window title */
2244 update_window_title (self);
2245 modest_ui_actions_on_close_window (NULL, MODEST_WINDOW (self));
2246 g_object_unref (self);
2251 /* Get the window */
2252 self = (ModestMsgViewWindow *) modest_mail_operation_get_source (mail_op);
2253 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self));
2254 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
2256 /* Update the row reference */
2257 if (priv->row_reference != NULL) {
2258 gtk_tree_row_reference_free (priv->row_reference);
2259 priv->row_reference = (row_reference && gtk_tree_row_reference_valid (row_reference))?gtk_tree_row_reference_copy (row_reference):NULL;
2260 if (priv->next_row_reference != NULL) {
2261 gtk_tree_row_reference_free (priv->next_row_reference);
2263 if (priv->row_reference) {
2264 priv->next_row_reference = gtk_tree_row_reference_copy (priv->row_reference);
2265 select_next_valid_row (priv->header_model, &(priv->next_row_reference), TRUE, priv->is_outbox);
2267 priv->next_row_reference = NULL;
2271 /* Mark header as read */
2272 if (!(tny_header_get_flags (header) & TNY_HEADER_FLAG_SEEN))
2273 tny_header_set_flag (header, TNY_HEADER_FLAG_SEEN);
2275 /* Set new message */
2276 if (priv->msg_view != NULL && TNY_IS_MSG_VIEW (priv->msg_view)) {
2277 tny_msg_view_set_msg (TNY_MSG_VIEW (priv->msg_view), msg);
2278 modest_msg_view_window_update_priority (self);
2279 update_window_title (MODEST_MSG_VIEW_WINDOW (self));
2280 update_branding (MODEST_MSG_VIEW_WINDOW (self));
2281 modest_msg_view_grab_focus (MODEST_MSG_VIEW (priv->msg_view));
2284 /* Set the new message uid of the window */
2285 if (priv->msg_uid) {
2286 g_free (priv->msg_uid);
2287 priv->msg_uid = modest_tny_folder_get_header_unique_id (header);
2290 /* Notify the observers */
2291 g_signal_emit (G_OBJECT (self), signals[MSG_CHANGED_SIGNAL],
2292 0, priv->header_model, priv->row_reference);
2294 /* Sync the flags if the message is not opened from a header
2295 model, i.e, if it's opened from a notification */
2296 if (!priv->header_model)
2300 g_object_unref (self);
2302 gtk_tree_row_reference_free (row_reference);
2306 modest_msg_view_window_get_folder_type (ModestMsgViewWindow *window)
2308 ModestMsgViewWindowPrivate *priv;
2310 TnyFolderType folder_type;
2312 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
2314 folder_type = TNY_FOLDER_TYPE_UNKNOWN;
2316 msg = tny_msg_view_get_msg (TNY_MSG_VIEW (priv->msg_view));
2320 folder = tny_msg_get_folder (msg);
2322 folder_type = modest_tny_folder_guess_folder_type (folder);
2323 g_object_unref (folder);
2325 g_object_unref (msg);
2333 modest_msg_view_window_update_priority (ModestMsgViewWindow *window)
2335 ModestMsgViewWindowPrivate *priv;
2336 TnyHeader *header = NULL;
2337 TnyHeaderFlags flags = 0;
2339 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
2341 if (priv->header_model && priv->row_reference && gtk_tree_row_reference_valid (priv->row_reference)) {
2343 GtkTreePath *path = NULL;
2345 path = gtk_tree_row_reference_get_path (priv->row_reference);
2346 g_return_if_fail (path != NULL);
2347 gtk_tree_model_get_iter (priv->header_model,
2349 gtk_tree_row_reference_get_path (priv->row_reference));
2351 gtk_tree_model_get (priv->header_model, &iter, TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
2353 gtk_tree_path_free (path);
2356 msg = tny_msg_view_get_msg (TNY_MSG_VIEW (priv->msg_view));
2358 header = tny_msg_get_header (msg);
2359 g_object_unref (msg);
2364 flags = tny_header_get_flags (header);
2365 g_object_unref(G_OBJECT(header));
2368 modest_msg_view_set_priority (MODEST_MSG_VIEW(priv->msg_view), flags);
2373 toolbar_resize (ModestMsgViewWindow *self)
2375 ModestMsgViewWindowPrivate *priv = NULL;
2376 ModestWindowPrivate *parent_priv = NULL;
2378 gint static_button_size;
2379 ModestWindowMgr *mgr;
2381 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self));
2382 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
2383 parent_priv = MODEST_WINDOW_GET_PRIVATE(self);
2385 mgr = modest_runtime_get_window_mgr ();
2386 static_button_size = modest_window_mgr_get_fullscreen_mode (mgr)?120:120;
2388 if (parent_priv->toolbar) {
2389 /* Set expandable and homogeneous tool buttons */
2390 widget = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/ToolbarMessageReply");
2391 gtk_tool_item_set_expand (GTK_TOOL_ITEM (widget), TRUE);
2392 gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (widget), TRUE);
2393 widget = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/ToolbarMessageReplyAll");
2394 gtk_tool_item_set_expand (GTK_TOOL_ITEM (widget), TRUE);
2395 gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (widget), TRUE);
2396 widget = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/ToolbarMessageForward");
2397 gtk_tool_item_set_expand (GTK_TOOL_ITEM (widget), TRUE);
2398 gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (widget), TRUE);
2399 widget = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/ToolbarDeleteMessage");
2400 gtk_tool_item_set_expand (GTK_TOOL_ITEM (widget), TRUE);
2401 gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (widget), TRUE);
2402 widget = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/ToolbarDownloadExternalImages");
2403 gtk_tool_item_set_expand (GTK_TOOL_ITEM (widget), TRUE);
2404 gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (widget), TRUE);
2405 gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (priv->next_toolitem), TRUE);
2406 gtk_tool_item_set_expand (GTK_TOOL_ITEM (priv->next_toolitem), TRUE);
2407 gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (priv->prev_toolitem), TRUE);
2408 gtk_tool_item_set_expand (GTK_TOOL_ITEM (priv->prev_toolitem), TRUE);
2413 modest_msg_view_window_show_toolbar (ModestWindow *self,
2414 gboolean show_toolbar)
2416 ModestMsgViewWindowPrivate *priv = NULL;
2417 ModestWindowPrivate *parent_priv;
2419 parent_priv = MODEST_WINDOW_GET_PRIVATE(self);
2420 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(self);
2422 /* Set optimized view status */
2423 priv->optimized_view = !show_toolbar;
2425 if (!parent_priv->toolbar) {
2426 parent_priv->toolbar = gtk_ui_manager_get_widget (parent_priv->ui_manager,
2429 #ifdef MODEST_TOOLKIT_HILDON2
2430 gtk_toolbar_set_icon_size (GTK_TOOLBAR (parent_priv->toolbar), HILDON_ICON_SIZE_FINGER);
2432 gtk_toolbar_set_icon_size (GTK_TOOLBAR (parent_priv->toolbar), GTK_ICON_SIZE_LARGE_TOOLBAR);
2434 gtk_widget_set_no_show_all (parent_priv->toolbar, TRUE);
2436 priv->next_toolitem = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/ToolbarMessageNext");
2437 priv->prev_toolitem = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/ToolbarMessageBack");
2438 toolbar_resize (MODEST_MSG_VIEW_WINDOW (self));
2440 modest_window_add_toolbar (MODEST_WINDOW (self),
2441 GTK_TOOLBAR (parent_priv->toolbar));
2446 /* Quick hack: this prevents toolbar icons "dance" when progress bar show status is changed */
2447 /* TODO: resize mode migth be GTK_RESIZE_QUEUE, in order to avoid unneccesary shows */
2448 gtk_container_set_resize_mode (GTK_CONTAINER(parent_priv->toolbar), GTK_RESIZE_IMMEDIATE);
2450 gtk_widget_show (GTK_WIDGET (parent_priv->toolbar));
2451 if (modest_msg_view_window_transfer_mode_enabled (MODEST_MSG_VIEW_WINDOW (self)))
2452 set_progress_hint (MODEST_MSG_VIEW_WINDOW (self), TRUE);
2454 set_progress_hint (MODEST_MSG_VIEW_WINDOW (self), FALSE);
2457 gtk_widget_set_no_show_all (parent_priv->toolbar, TRUE);
2458 gtk_widget_hide (GTK_WIDGET (parent_priv->toolbar));
2463 modest_msg_view_window_clipboard_owner_change (GtkClipboard *clipboard,
2465 ModestMsgViewWindow *window)
2467 if (!GTK_WIDGET_VISIBLE (window))
2470 modest_window_check_dimming_rules_group (MODEST_WINDOW (window), MODEST_DIMMING_RULES_CLIPBOARD);
2474 modest_msg_view_window_transfer_mode_enabled (ModestMsgViewWindow *self)
2476 ModestMsgViewWindowPrivate *priv;
2478 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self), FALSE);
2479 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(self);
2481 return priv->progress_hint;
2485 observers_empty (ModestMsgViewWindow *self)
2488 ModestMsgViewWindowPrivate *priv;
2489 gboolean is_empty = TRUE;
2490 guint pending_ops = 0;
2492 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(self);
2493 tmp = priv->progress_widgets;
2495 /* Check all observers */
2496 while (tmp && is_empty) {
2497 pending_ops = modest_progress_object_num_pending_operations (MODEST_PROGRESS_OBJECT(tmp->data));
2498 is_empty = pending_ops == 0;
2500 tmp = g_slist_next(tmp);
2507 on_account_removed (TnyAccountStore *account_store,
2508 TnyAccount *account,
2511 /* Do nothing if it's a transport account, because we only
2512 show the messages of a store account */
2513 if (tny_account_get_account_type(account) == TNY_ACCOUNT_TYPE_STORE) {
2514 const gchar *parent_acc = NULL;
2515 const gchar *our_acc = NULL;
2517 our_acc = modest_window_get_active_account (MODEST_WINDOW (user_data));
2518 parent_acc = modest_tny_account_get_parent_modest_account_name_for_server_account (account);
2520 /* Close this window if I'm showing a message of the removed account */
2521 if (our_acc && parent_acc && strcmp (parent_acc, our_acc) == 0)
2522 modest_ui_actions_on_close_window (NULL, MODEST_WINDOW (user_data));
2527 on_mail_operation_started (ModestMailOperation *mail_op,
2530 ModestMsgViewWindow *self;
2531 ModestMailOperationTypeOperation op_type;
2533 ModestMsgViewWindowPrivate *priv;
2534 GObject *source = NULL;
2536 self = MODEST_MSG_VIEW_WINDOW (user_data);
2537 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
2538 op_type = modest_mail_operation_get_type_operation (mail_op);
2539 tmp = priv->progress_widgets;
2540 source = modest_mail_operation_get_source(mail_op);
2541 if (G_OBJECT (self) == source) {
2542 if (op_type == MODEST_MAIL_OPERATION_TYPE_RECEIVE ||
2543 op_type == MODEST_MAIL_OPERATION_TYPE_OPEN ||
2544 op_type == MODEST_MAIL_OPERATION_TYPE_DELETE) {
2545 set_progress_hint (self, TRUE);
2547 modest_progress_object_add_operation (
2548 MODEST_PROGRESS_OBJECT (tmp->data),
2550 tmp = g_slist_next (tmp);
2554 g_object_unref (source);
2556 /* Update dimming rules */
2557 check_dimming_rules_after_change (self);
2561 on_mail_operation_finished (ModestMailOperation *mail_op,
2564 ModestMsgViewWindow *self;
2565 ModestMailOperationTypeOperation op_type;
2567 ModestMsgViewWindowPrivate *priv;
2569 self = MODEST_MSG_VIEW_WINDOW (user_data);
2570 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
2571 op_type = modest_mail_operation_get_type_operation (mail_op);
2572 tmp = priv->progress_widgets;
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) {
2578 modest_progress_object_remove_operation (MODEST_PROGRESS_OBJECT (tmp->data),
2580 tmp = g_slist_next (tmp);
2583 /* If no more operations are being observed, NORMAL mode is enabled again */
2584 if (observers_empty (self)) {
2585 set_progress_hint (self, FALSE);
2589 /* Update dimming rules. We have to do this right here
2590 and not in view_msg_cb because at that point the
2591 transfer mode is still enabled so the dimming rule
2592 won't let the user delete the message that has been
2593 readed for example */
2594 check_dimming_rules_after_change (self);
2598 on_queue_changed (ModestMailOperationQueue *queue,
2599 ModestMailOperation *mail_op,
2600 ModestMailOperationQueueNotification type,
2601 ModestMsgViewWindow *self)
2603 ModestMsgViewWindowPrivate *priv;
2605 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
2607 /* If this operations was created by another window, do nothing */
2608 if (!modest_mail_operation_is_mine (mail_op, G_OBJECT(self)))
2611 if (type == MODEST_MAIL_OPERATION_QUEUE_OPERATION_ADDED) {
2612 priv->sighandlers = modest_signal_mgr_connect (priv->sighandlers,
2614 "operation-started",
2615 G_CALLBACK (on_mail_operation_started),
2617 priv->sighandlers = modest_signal_mgr_connect (priv->sighandlers,
2619 "operation-finished",
2620 G_CALLBACK (on_mail_operation_finished),
2622 } else if (type == MODEST_MAIL_OPERATION_QUEUE_OPERATION_REMOVED) {
2623 priv->sighandlers = modest_signal_mgr_disconnect (priv->sighandlers,
2625 "operation-started");
2626 priv->sighandlers = modest_signal_mgr_disconnect (priv->sighandlers,
2628 "operation-finished");
2633 modest_msg_view_window_get_attachments (ModestMsgViewWindow *win)
2635 ModestMsgViewWindowPrivate *priv;
2636 TnyList *selected_attachments = NULL;
2638 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (win), NULL);
2639 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (win);
2641 /* In Hildon 2.2 as there's no selection we assume we have all attachments selected */
2642 selected_attachments = modest_msg_view_get_attachments (MODEST_MSG_VIEW (priv->msg_view));
2644 return selected_attachments;
2648 ModestMsgViewWindow *self;
2650 gchar *attachment_uid;
2651 } DecodeAsyncHelper;
2654 on_decode_to_stream_async_handler (TnyMimePart *mime_part,
2660 DecodeAsyncHelper *helper = (DecodeAsyncHelper *) user_data;
2661 const gchar *content_type;
2663 if (cancelled || err) {
2666 if ((err->domain == TNY_ERROR_DOMAIN) &&
2667 (err->code == TNY_IO_ERROR_WRITE) &&
2668 (errno == ENOSPC)) {
2669 msg = g_strdup_printf (_KR("cerm_device_memory_full"), "");
2671 msg = g_strdup (_("mail_ib_file_operation_failed"));
2673 modest_platform_information_banner (NULL, NULL, msg);
2679 /* It could happen that the window was closed. So we
2680 assume it is a cancelation */
2681 if (!GTK_WIDGET_VISIBLE (helper->self))
2684 /* Remove the progress hint */
2685 set_progress_hint (helper->self, FALSE);
2687 content_type = tny_mime_part_get_content_type (mime_part);
2688 if (g_str_has_prefix (content_type, "message/rfc822")) {
2689 ModestWindowMgr *mgr;
2690 ModestWindow *msg_win = NULL;
2693 const gchar *mailbox;
2694 TnyStream *file_stream;
2697 fd = g_open (helper->file_path, O_RDONLY, 0644);
2699 file_stream = tny_fs_stream_new (fd);
2701 mgr = modest_runtime_get_window_mgr ();
2703 account = g_strdup (modest_window_get_active_account (MODEST_WINDOW (helper->self)));
2704 mailbox = modest_window_get_active_mailbox (MODEST_WINDOW (helper->self));
2707 account = modest_account_mgr_get_default_account (modest_runtime_get_account_mgr ());
2709 msg = tny_camel_msg_new ();
2710 tny_camel_msg_parse (msg, file_stream);
2711 msg_win = modest_msg_view_window_new_for_attachment (TNY_MSG (msg), account, mailbox, helper->attachment_uid);
2712 modest_window_set_zoom (MODEST_WINDOW (msg_win),
2713 modest_window_get_zoom (MODEST_WINDOW (helper->self)));
2714 if (modest_window_mgr_register_window (mgr, msg_win, MODEST_WINDOW (helper->self)))
2715 gtk_widget_show_all (GTK_WIDGET (msg_win));
2717 gtk_widget_destroy (GTK_WIDGET (msg_win));
2718 g_object_unref (msg);
2719 g_object_unref (file_stream);
2721 modest_platform_information_banner (NULL, NULL, _("mail_ib_file_operation_failed"));
2726 /* make the file read-only */
2727 g_chmod(helper->file_path, 0444);
2729 /* Activate the file */
2730 modest_platform_activate_file (helper->file_path, tny_mime_part_get_content_type (mime_part));
2735 g_object_unref (helper->self);
2736 g_free (helper->file_path);
2737 g_free (helper->attachment_uid);
2738 g_slice_free (DecodeAsyncHelper, helper);
2742 view_attachment_connect_handler (gboolean canceled,
2744 GtkWindow *parent_window,
2745 TnyAccount *account,
2749 if (canceled || err) {
2750 g_object_unref (part);
2754 modest_msg_view_window_view_attachment (MODEST_MSG_VIEW_WINDOW (parent_window),
2756 g_object_unref (part);
2760 modest_msg_view_window_view_attachment (ModestMsgViewWindow *window,
2761 TnyMimePart *mime_part)
2763 ModestMsgViewWindowPrivate *priv;
2764 const gchar *msg_uid;
2765 gchar *attachment_uid = NULL;
2766 gint attachment_index = 0;
2767 TnyList *attachments;
2769 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window));
2770 g_return_if_fail (TNY_IS_MIME_PART (mime_part) || (mime_part == NULL));
2771 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
2773 msg_uid = modest_msg_view_window_get_message_uid (MODEST_MSG_VIEW_WINDOW (window));
2774 attachments = modest_msg_view_get_attachments (MODEST_MSG_VIEW (priv->msg_view));
2775 attachment_index = modest_list_index (attachments, (GObject *) mime_part);
2776 g_object_unref (attachments);
2778 if (msg_uid && attachment_index >= 0) {
2779 attachment_uid = g_strdup_printf ("%s/%d", msg_uid, attachment_index);
2782 if (mime_part == NULL) {
2783 gboolean error = FALSE;
2784 TnyList *selected_attachments = modest_msg_view_get_selected_attachments (MODEST_MSG_VIEW (priv->msg_view));
2785 if (selected_attachments == NULL || tny_list_get_length (selected_attachments) == 0) {
2787 } else if (tny_list_get_length (selected_attachments) > 1) {
2788 modest_platform_system_banner (NULL, NULL, _("mcen_ib_unable_to_display_more"));
2792 iter = tny_list_create_iterator (selected_attachments);
2793 mime_part = (TnyMimePart *) tny_iterator_get_current (iter);
2794 g_object_unref (iter);
2796 if (selected_attachments)
2797 g_object_unref (selected_attachments);
2802 g_object_ref (mime_part);
2805 if (tny_mime_part_is_purged (mime_part))
2808 if (TNY_IS_CAMEL_BS_MIME_PART (mime_part) &&
2809 !tny_camel_bs_mime_part_is_fetched (TNY_CAMEL_BS_MIME_PART (mime_part))) {
2811 TnyAccount *account;
2813 is_merge = g_str_has_prefix (priv->msg_uid, "merge:");
2815 /* Get the account */
2817 account = tny_account_store_find_account (TNY_ACCOUNT_STORE (modest_runtime_get_account_store ()),
2820 if (!tny_device_is_online (modest_runtime_get_device())) {
2821 modest_platform_connect_and_perform (GTK_WINDOW (window),
2823 TNY_ACCOUNT (account),
2824 (ModestConnectedPerformer) view_attachment_connect_handler,
2825 g_object_ref (mime_part));
2830 if (!modest_tny_mime_part_is_msg (mime_part) && tny_mime_part_get_filename (mime_part)) {
2831 gchar *filepath = NULL;
2832 const gchar *att_filename = tny_mime_part_get_filename (mime_part);
2833 gboolean show_error_banner = FALSE;
2834 TnyFsStream *temp_stream = NULL;
2835 temp_stream = modest_utils_create_temp_stream (att_filename, attachment_uid,
2838 if (temp_stream != NULL) {
2839 ModestAccountMgr *mgr;
2840 DecodeAsyncHelper *helper;
2841 gboolean decode_in_provider;
2842 ModestProtocol *protocol;
2843 const gchar *account;
2845 /* Activate progress hint */
2846 set_progress_hint (window, TRUE);
2848 helper = g_slice_new0 (DecodeAsyncHelper);
2849 helper->self = g_object_ref (window);
2850 helper->file_path = g_strdup (filepath);
2851 helper->attachment_uid = g_strdup (attachment_uid);
2853 decode_in_provider = FALSE;
2854 mgr = modest_runtime_get_account_mgr ();
2855 account = modest_window_get_active_account (MODEST_WINDOW (window));
2856 if (modest_account_mgr_account_is_multimailbox (mgr, account, &protocol)) {
2857 if (MODEST_IS_ACCOUNT_PROTOCOL (protocol)) {
2859 uri = g_strconcat ("file://", filepath, NULL);
2860 decode_in_provider =
2861 modest_account_protocol_decode_part_to_stream_async (
2862 MODEST_ACCOUNT_PROTOCOL (protocol),
2865 TNY_STREAM (temp_stream),
2866 on_decode_to_stream_async_handler,
2873 if (!decode_in_provider)
2874 tny_mime_part_decode_to_stream_async (mime_part, TNY_STREAM (temp_stream),
2875 on_decode_to_stream_async_handler,
2878 g_object_unref (temp_stream);
2879 /* NOTE: files in the temporary area will be automatically
2880 * cleaned after some time if they are no longer in use */
2883 const gchar *content_type;
2884 /* the file may already exist but it isn't writable,
2885 * let's try to open it anyway */
2886 content_type = tny_mime_part_get_content_type (mime_part);
2887 modest_platform_activate_file (filepath, content_type);
2889 g_warning ("%s: modest_utils_create_temp_stream failed", __FUNCTION__);
2890 show_error_banner = TRUE;
2895 if (show_error_banner)
2896 modest_platform_information_banner (NULL, NULL, _("mail_ib_file_operation_failed"));
2897 } else if (!modest_tny_mime_part_is_msg (mime_part)) {
2898 ModestWindowMgr *mgr;
2899 ModestWindow *msg_win = NULL;
2900 TnyMsg *current_msg;
2904 current_msg = modest_msg_view_window_get_message (MODEST_MSG_VIEW_WINDOW (window));
2905 mgr = modest_runtime_get_window_mgr ();
2906 header = tny_msg_get_header (TNY_MSG (current_msg));
2907 found = modest_window_mgr_find_registered_message_uid (mgr,
2912 g_debug ("window for this body is already being created");
2915 /* it's not found, so create a new window for it */
2916 modest_window_mgr_register_header (mgr, header, attachment_uid); /* register the uid before building the window */
2917 gchar *account = g_strdup (modest_window_get_active_account (MODEST_WINDOW (window)));
2918 const gchar *mailbox = modest_window_get_active_mailbox (MODEST_WINDOW (window));
2920 account = modest_account_mgr_get_default_account (modest_runtime_get_account_mgr ());
2922 msg_win = modest_msg_view_window_new_with_other_body (TNY_MSG (current_msg), TNY_MIME_PART (mime_part),
2923 account, mailbox, attachment_uid);
2925 modest_window_set_zoom (MODEST_WINDOW (msg_win),
2926 modest_window_get_zoom (MODEST_WINDOW (window)));
2927 if (modest_window_mgr_register_window (mgr, msg_win, MODEST_WINDOW (window)))
2928 gtk_widget_show_all (GTK_WIDGET (msg_win));
2930 gtk_widget_destroy (GTK_WIDGET (msg_win));
2932 g_object_unref (current_msg);
2934 /* message attachment */
2935 TnyHeader *header = NULL;
2936 ModestWindowMgr *mgr;
2937 ModestWindow *msg_win = NULL;
2940 header = tny_msg_get_header (TNY_MSG (mime_part));
2941 mgr = modest_runtime_get_window_mgr ();
2942 found = modest_window_mgr_find_registered_header (mgr, header, &msg_win);
2945 /* if it's found, but there is no msg_win, it's probably in the process of being created;
2946 * thus, we don't do anything */
2947 g_debug ("window for is already being created");
2949 /* it's not found, so create a new window for it */
2950 modest_window_mgr_register_header (mgr, header, attachment_uid); /* register the uid before building the window */
2951 gchar *account = g_strdup (modest_window_get_active_account (MODEST_WINDOW (window)));
2952 const gchar *mailbox = modest_window_get_active_mailbox (MODEST_WINDOW (window));
2954 account = modest_account_mgr_get_default_account (modest_runtime_get_account_mgr ());
2955 msg_win = modest_msg_view_window_new_for_attachment (TNY_MSG (mime_part), account,
2956 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));
2968 g_free (attachment_uid);
2970 g_object_unref (mime_part);
2982 GnomeVFSResult result;
2984 ModestMsgViewWindow *window;
2987 static void save_mime_part_info_free (SaveMimePartInfo *info, gboolean with_struct);
2988 static gboolean idle_save_mime_part_show_result (SaveMimePartInfo *info);
2989 static gpointer save_mime_part_to_file (SaveMimePartInfo *info);
2990 static void save_mime_parts_to_file_with_checks (GtkWindow *parent, SaveMimePartInfo *info);
2993 save_mime_part_info_free (SaveMimePartInfo *info, gboolean with_struct)
2997 for (node = info->pairs; node != NULL; node = g_list_next (node)) {
2998 SaveMimePartPair *pair = (SaveMimePartPair *) node->data;
2999 g_free (pair->filename);
3000 g_object_unref (pair->part);
3001 g_slice_free (SaveMimePartPair, pair);
3003 g_list_free (info->pairs);
3006 g_object_unref (info->window);
3007 info->window = NULL;
3009 g_slice_free (SaveMimePartInfo, info);
3014 idle_save_mime_part_show_result (SaveMimePartInfo *info)
3016 /* This is a GDK lock because we are an idle callback and
3017 * modest_platform_system_banner is or does Gtk+ code */
3019 gdk_threads_enter (); /* CHECKED */
3020 if (info->result == GNOME_VFS_ERROR_CANCELLED) {
3022 } else if (info->result == GNOME_VFS_OK) {
3023 modest_platform_system_banner (NULL, NULL, _CS_SAVED);
3024 } else if (info->result == GNOME_VFS_ERROR_NO_SPACE) {
3027 /* Check if the uri belongs to the external mmc */
3028 if (g_str_has_prefix (info->uri, g_getenv (MODEST_MMC1_VOLUMEPATH_ENV)))
3029 msg = g_strdup_printf (_KR("cerm_device_memory_full"), "");
3031 msg = g_strdup (_KR("cerm_memory_card_full"));
3032 modest_platform_information_banner (NULL, NULL, msg);
3035 modest_platform_system_banner (NULL, NULL, _("mail_ib_file_operation_failed"));
3037 save_mime_part_info_free (info, FALSE);
3038 gdk_threads_leave (); /* CHECKED */
3044 save_mime_part_to_file_connect_handler (gboolean canceled,
3046 GtkWindow *parent_window,
3047 TnyAccount *account,
3048 SaveMimePartInfo *info)
3050 if (canceled || err) {
3051 g_idle_add ((GSourceFunc) idle_save_mime_part_show_result, info);
3053 g_thread_create ((GThreadFunc)save_mime_part_to_file, info, FALSE, NULL);
3058 save_mime_part_to_file_connect_idle (SaveMimePartInfo *info)
3061 TnyAccount *account;
3062 ModestMsgViewWindowPrivate *priv;
3064 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (info->window);
3066 is_merge = g_str_has_prefix (priv->msg_uid, "merge:");
3069 /* Get the account */
3071 account = tny_account_store_find_account (TNY_ACCOUNT_STORE (modest_runtime_get_account_store ()),
3074 modest_platform_connect_and_perform (GTK_WINDOW (info->window),
3076 TNY_ACCOUNT (account),
3077 (ModestConnectedPerformer) save_mime_part_to_file_connect_handler,
3083 save_mime_part_to_file (SaveMimePartInfo *info)
3085 GnomeVFSHandle *handle;
3087 SaveMimePartPair *pair = (SaveMimePartPair *) info->pairs->data;
3089 if (TNY_IS_CAMEL_BS_MIME_PART (pair->part) &&
3090 !tny_camel_bs_mime_part_is_fetched (TNY_CAMEL_BS_MIME_PART (pair->part))) {
3091 g_idle_add ((GSourceFunc) save_mime_part_to_file_connect_idle, info);
3095 info->result = gnome_vfs_create (&handle, pair->filename, GNOME_VFS_OPEN_WRITE, FALSE, 0644);
3096 if (info->result == GNOME_VFS_OK) {
3097 GError *error = NULL;
3098 gboolean decode_in_provider;
3100 ModestAccountMgr *mgr;
3101 const gchar *account;
3102 ModestProtocol *protocol = NULL;
3104 stream = tny_vfs_stream_new (handle);
3106 decode_in_provider = FALSE;
3107 mgr = modest_runtime_get_account_mgr ();
3108 account = modest_window_get_active_account (MODEST_WINDOW (info->window));
3109 if (modest_account_mgr_account_is_multimailbox (mgr, account, &protocol)) {
3110 if (MODEST_IS_ACCOUNT_PROTOCOL (protocol)) {
3111 decode_in_provider =
3112 modest_account_protocol_decode_part_to_stream (
3113 MODEST_ACCOUNT_PROTOCOL (protocol),
3121 if (!decode_in_provider)
3122 written = tny_mime_part_decode_to_stream (pair->part, stream, &error);
3125 g_warning ("modest: could not save attachment %s: %d (%s)\n", pair->filename, error?error->code:-1, error?error->message:"Unknown error");
3127 if ((error->domain == TNY_ERROR_DOMAIN) &&
3128 (error->code == TNY_IO_ERROR_WRITE) &&
3129 (errno == ENOSPC)) {
3130 info->result = GNOME_VFS_ERROR_NO_SPACE;
3132 info->result = GNOME_VFS_ERROR_IO;
3135 g_object_unref (G_OBJECT (stream));
3137 g_warning ("Could not create save attachment %s: %s\n",
3138 pair->filename, gnome_vfs_result_to_string (info->result));
3141 /* Go on saving remaining files */
3142 info->pairs = g_list_remove_link (info->pairs, info->pairs);
3143 if (info->pairs != NULL) {
3144 save_mime_part_to_file (info);
3146 g_idle_add ((GSourceFunc) idle_save_mime_part_show_result, info);
3153 save_mime_parts_to_file_with_checks (GtkWindow *parent,
3154 SaveMimePartInfo *info)
3156 gboolean is_ok = TRUE;
3157 gint replaced_files = 0;
3158 const GList *files = info->pairs;
3159 const GList *iter, *to_replace = NULL;
3161 for (iter = files; (iter != NULL) && (replaced_files < 2); iter = g_list_next(iter)) {
3162 SaveMimePartPair *pair = iter->data;
3163 gchar *unescaped = g_uri_unescape_string (pair->filename, NULL);
3165 if (modest_utils_file_exists (unescaped)) {
3167 if (replaced_files == 1)
3172 if (replaced_files) {
3175 if (replaced_files == 1) {
3176 SaveMimePartPair *pair = to_replace->data;
3177 const gchar *basename = strrchr (pair->filename, G_DIR_SEPARATOR) + 1;
3178 gchar *escaped_basename, *message;
3180 escaped_basename = g_uri_unescape_string (basename, NULL);
3181 message = g_strdup_printf ("%s\n%s",
3183 (escaped_basename) ? escaped_basename : "");
3184 response = modest_platform_run_confirmation_dialog (parent, message);
3186 g_free (escaped_basename);
3188 response = modest_platform_run_confirmation_dialog (parent,
3189 _FM_REPLACE_MULTIPLE);
3191 if (response != GTK_RESPONSE_OK)
3196 save_mime_part_info_free (info, TRUE);
3198 g_thread_create ((GThreadFunc)save_mime_part_to_file, info, FALSE, NULL);
3203 typedef struct _SaveAttachmentsInfo {
3204 TnyList *attachments_list;
3205 ModestMsgViewWindow *window;
3206 } SaveAttachmentsInfo;
3209 save_attachments_response (GtkDialog *dialog,
3213 TnyList *mime_parts;
3215 GList *files_to_save = NULL;
3216 gchar *current_folder;
3217 SaveAttachmentsInfo *sa_info = (SaveAttachmentsInfo *) user_data;
3219 mime_parts = TNY_LIST (sa_info->attachments_list);
3221 if (arg1 != GTK_RESPONSE_OK)
3224 chooser_uri = gtk_file_chooser_get_uri (GTK_FILE_CHOOSER (dialog));
3225 current_folder = gtk_file_chooser_get_current_folder_uri (GTK_FILE_CHOOSER (dialog));
3226 if (current_folder && *current_folder != '\0') {
3228 modest_conf_set_string (modest_runtime_get_conf (), MODEST_CONF_LATEST_SAVE_ATTACHMENT_PATH,
3229 current_folder,&err);
3231 g_debug ("Error storing latest used folder: %s", err->message);
3235 g_free (current_folder);
3237 if (!modest_utils_folder_writable (chooser_uri)) {
3238 const gchar *err_msg;
3240 #ifdef MODEST_PLATFORM_MAEMO
3241 if (modest_maemo_utils_in_usb_mode ()) {
3242 err_msg = dgettext ("hildon-status-bar-usb", "usbh_ib_mmc_usb_connected");
3244 err_msg = _FM_READ_ONLY_LOCATION;
3247 err_msg = _FM_READ_ONLY_LOCATION;
3249 modest_platform_system_banner (NULL, NULL, err_msg);
3253 iter = tny_list_create_iterator (mime_parts);
3254 while (!tny_iterator_is_done (iter)) {
3255 TnyMimePart *mime_part = (TnyMimePart *) tny_iterator_get_current (iter);
3257 if ((modest_tny_mime_part_is_attachment_for_modest (mime_part)) &&
3258 !tny_mime_part_is_purged (mime_part) &&
3259 (tny_mime_part_get_filename (mime_part) != NULL)) {
3260 SaveMimePartPair *pair;
3262 pair = g_slice_new0 (SaveMimePartPair);
3264 if (tny_list_get_length (mime_parts) > 1) {
3266 gnome_vfs_escape_slashes (tny_mime_part_get_filename (mime_part));
3267 pair->filename = g_build_filename (chooser_uri, escaped, NULL);
3270 pair->filename = g_strdup (chooser_uri);
3272 pair->part = mime_part;
3273 files_to_save = g_list_prepend (files_to_save, pair);
3275 tny_iterator_next (iter);
3277 g_object_unref (iter);
3280 if (files_to_save != NULL) {
3281 SaveMimePartInfo *info = g_slice_new0 (SaveMimePartInfo);
3282 info->pairs = files_to_save;
3283 info->result = TRUE;
3284 info->uri = g_strdup (chooser_uri);
3285 info->window = g_object_ref (sa_info->window);
3286 save_mime_parts_to_file_with_checks ((GtkWindow *) dialog, info);
3288 g_free (chooser_uri);
3291 /* Free and close the dialog */
3292 g_object_unref (mime_parts);
3293 g_object_unref (sa_info->window);
3294 g_slice_free (SaveAttachmentsInfo, sa_info);
3295 gtk_widget_destroy (GTK_WIDGET (dialog));
3299 modest_msg_view_window_save_attachments (ModestMsgViewWindow *window,
3300 TnyList *mime_parts)
3302 ModestMsgViewWindowPrivate *priv;
3303 GtkWidget *save_dialog = NULL;
3304 gchar *conf_folder = NULL;
3305 gchar *filename = NULL;
3306 gchar *save_multiple_str = NULL;
3307 const gchar *root_folder = "file:///";
3309 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window));
3310 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
3312 if (mime_parts == NULL) {
3313 /* In Hildon 2.2 save and delete operate over all the attachments as there's no
3314 * selection available */
3315 mime_parts = modest_msg_view_get_attachments (MODEST_MSG_VIEW (priv->msg_view));
3316 if (mime_parts && !modest_toolkit_utils_select_attachments (GTK_WINDOW (gtk_widget_get_toplevel (window)), mime_parts, FALSE)) {
3317 g_object_unref (mime_parts);
3320 if (mime_parts == NULL || tny_list_get_length (mime_parts) == 0) {
3322 g_object_unref (mime_parts);
3328 g_object_ref (mime_parts);
3331 /* prepare dialog */
3332 if (tny_list_get_length (mime_parts) == 1) {
3334 /* only one attachment selected */
3335 iter = tny_list_create_iterator (mime_parts);
3336 TnyMimePart *mime_part = (TnyMimePart *) tny_iterator_get_current (iter);
3337 g_object_unref (iter);
3338 if (!modest_tny_mime_part_is_msg (mime_part) &&
3339 modest_tny_mime_part_is_attachment_for_modest (mime_part) &&
3340 !tny_mime_part_is_purged (mime_part)) {
3341 filename = g_strdup (tny_mime_part_get_filename (mime_part));
3343 /* TODO: show any error? */
3344 g_warning ("%s: Tried to save a non-file attachment", __FUNCTION__);
3345 g_object_unref (mime_parts);
3348 g_object_unref (mime_part);
3350 gint num = tny_list_get_length (mime_parts);
3351 save_multiple_str = g_strdup_printf (dngettext("hildon-fm",
3352 "sfil_va_number_of_objects_attachment",
3353 "sfil_va_number_of_objects_attachments",
3357 /* Creation of hildon file chooser dialog for saving */
3358 save_dialog = modest_toolkit_factory_create_file_chooser_dialog (modest_runtime_get_toolkit_factory (),
3360 (GtkWindow *) window,
3361 GTK_FILE_CHOOSER_ACTION_SAVE);
3363 /* Get last used folder */
3364 conf_folder = modest_conf_get_string (modest_runtime_get_conf (),
3365 MODEST_CONF_LATEST_SAVE_ATTACHMENT_PATH, NULL);
3367 /* File chooser stops working if we select "file:///" as current folder */
3368 if (conf_folder && g_ascii_strcasecmp (root_folder, conf_folder) != 0) {
3369 g_free (conf_folder);
3373 if (conf_folder && conf_folder[0] != '\0') {
3374 gtk_file_chooser_set_current_folder_uri (GTK_FILE_CHOOSER (save_dialog), conf_folder);
3377 /* Set the default folder to documents folder */
3378 docs_folder = (gchar *) g_strdup(g_get_user_special_dir (G_USER_DIRECTORY_DOCUMENTS));
3381 docs_folder = g_build_filename (g_getenv (MYDOCS_ENV), DOCS_FOLDER, NULL);
3383 gtk_file_chooser_set_current_folder (GTK_FILE_CHOOSER (save_dialog), docs_folder);
3384 g_free (docs_folder);
3386 g_free (conf_folder);
3390 gtk_file_chooser_set_current_name (GTK_FILE_CHOOSER (save_dialog),
3395 /* if multiple, set multiple string */
3396 if (save_multiple_str) {
3397 g_object_set (G_OBJECT (save_dialog), "save-multiple", save_multiple_str, NULL);
3398 gtk_window_set_title (GTK_WINDOW (save_dialog), _FM_SAVE_OBJECT_FILES);
3399 g_free (save_multiple_str);
3402 /* We must run this asynchronously, because the hildon dialog
3403 performs a gtk_dialog_run by itself which leads to gdk
3405 SaveAttachmentsInfo *sa_info;
3406 sa_info = g_slice_new (SaveAttachmentsInfo);
3407 sa_info->attachments_list = mime_parts;
3408 sa_info->window = g_object_ref (window);
3409 g_signal_connect (save_dialog, "response",
3410 G_CALLBACK (save_attachments_response), sa_info);
3412 gtk_widget_show_all (save_dialog);
3416 show_remove_attachment_information (gpointer userdata)
3418 ModestMsgViewWindow *window = (ModestMsgViewWindow *) userdata;
3419 ModestMsgViewWindowPrivate *priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
3421 /* We're outside the main lock */
3422 gdk_threads_enter ();
3424 if (priv->remove_attachment_banner != NULL) {
3425 gtk_widget_destroy (priv->remove_attachment_banner);
3426 g_object_unref (priv->remove_attachment_banner);
3429 priv->remove_attachment_banner = g_object_ref (
3430 modest_platform_animation_banner (NULL, NULL, _("mcen_me_inbox_remove_attachments")));
3432 gdk_threads_leave ();
3438 modest_msg_view_window_remove_attachments (ModestMsgViewWindow *window, gboolean get_all)
3440 ModestMsgViewWindowPrivate *priv;
3441 TnyList *mime_parts = NULL, *tmp;
3442 gchar *confirmation_message;
3448 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window));
3449 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
3451 /* In hildon 2.2 we ignore the get_all flag as we always get all attachments. This is
3452 * because we don't have selection
3454 mime_parts = modest_msg_view_get_attachments (MODEST_MSG_VIEW (priv->msg_view));
3456 /* Remove already purged messages from mime parts list. We use
3457 a copy of the list to remove items in the original one */
3458 tmp = tny_list_copy (mime_parts);
3459 iter = tny_list_create_iterator (tmp);
3460 while (!tny_iterator_is_done (iter)) {
3461 TnyMimePart *part = TNY_MIME_PART (tny_iterator_get_current (iter));
3462 if (tny_mime_part_is_purged (part))
3463 tny_list_remove (mime_parts, (GObject *) part);
3465 g_object_unref (part);
3466 tny_iterator_next (iter);
3468 g_object_unref (tmp);
3469 g_object_unref (iter);
3471 if (!modest_toolkit_utils_select_attachments (GTK_WINDOW (window), mime_parts, TRUE) ||
3472 tny_list_get_length (mime_parts) == 0) {
3473 g_object_unref (mime_parts);
3477 n_attachments = tny_list_get_length (mime_parts);
3478 if (n_attachments == 1) {
3482 iter = tny_list_create_iterator (mime_parts);
3483 part = (TnyMimePart *) tny_iterator_get_current (iter);
3484 g_object_unref (iter);
3485 if (modest_tny_mime_part_is_msg (part)) {
3487 header = tny_msg_get_header (TNY_MSG (part));
3488 filename = tny_header_dup_subject (header);
3489 g_object_unref (header);
3490 if (filename == NULL)
3491 filename = g_strdup (_("mail_va_no_subject"));
3493 filename = g_strdup (tny_mime_part_get_filename (TNY_MIME_PART (part)));
3495 confirmation_message = g_strdup_printf (_("mcen_nc_purge_file_text"), filename);
3497 g_object_unref (part);
3499 confirmation_message = g_strdup_printf (ngettext("mcen_nc_purge_file_text",
3500 "mcen_nc_purge_files_text",
3501 n_attachments), n_attachments);
3503 response = modest_platform_run_confirmation_dialog (GTK_WINDOW (window),
3504 confirmation_message);
3505 g_free (confirmation_message);
3507 if (response != GTK_RESPONSE_OK) {
3508 g_object_unref (mime_parts);
3512 priv->purge_timeout = g_timeout_add (2000, show_remove_attachment_information, window);
3514 iter = tny_list_create_iterator (mime_parts);
3515 while (!tny_iterator_is_done (iter)) {
3518 part = (TnyMimePart *) tny_iterator_get_current (iter);
3519 tny_mime_part_set_purged (TNY_MIME_PART (part));
3520 g_object_unref (part);
3521 tny_iterator_next (iter);
3523 g_object_unref (iter);
3525 msg = tny_msg_view_get_msg (TNY_MSG_VIEW (priv->msg_view));
3526 tny_msg_view_clear (TNY_MSG_VIEW (priv->msg_view));
3527 tny_msg_rewrite_cache (msg);
3528 tny_msg_view_set_msg (TNY_MSG_VIEW (priv->msg_view), msg);
3529 g_object_unref (msg);
3530 update_branding (MODEST_MSG_VIEW_WINDOW (window));
3532 g_object_unref (mime_parts);
3534 if (priv->purge_timeout > 0) {
3535 g_source_remove (priv->purge_timeout);
3536 priv->purge_timeout = 0;
3539 if (priv->remove_attachment_banner) {
3540 gtk_widget_destroy (priv->remove_attachment_banner);
3541 g_object_unref (priv->remove_attachment_banner);
3542 priv->remove_attachment_banner = NULL;
3548 update_window_title (ModestMsgViewWindow *window)
3550 ModestMsgViewWindowPrivate *priv;
3552 TnyHeader *header = NULL;
3553 gchar *subject = NULL;
3555 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
3557 /* Note that if the window is closed while we're retrieving
3558 the message, this widget could de deleted */
3559 if (!priv->msg_view)
3562 msg = tny_msg_view_get_msg (TNY_MSG_VIEW (priv->msg_view));
3564 if (priv->other_body) {
3567 description = modest_tny_mime_part_get_header_value (priv->other_body, "Content-Description");
3569 g_strstrip (description);
3570 subject = description;
3572 } else if (msg != NULL) {
3573 header = tny_msg_get_header (msg);
3574 subject = tny_header_dup_subject (header);
3575 g_object_unref (header);
3576 g_object_unref (msg);
3579 if ((subject == NULL)||(subject[0] == '\0')) {
3581 subject = g_strdup (_("mail_va_no_subject"));
3584 modest_window_set_title (MODEST_WINDOW (window), subject);
3589 on_move_focus (GtkWidget *widget,
3590 GtkDirectionType direction,
3593 g_signal_stop_emission_by_name (G_OBJECT (widget), "move-focus");
3597 fetch_image_open_stream (TnyStreamCache *self, gint64 *expected_size, gchar *uri)
3599 GnomeVFSResult result;
3600 GnomeVFSHandle *handle = NULL;
3601 GnomeVFSFileInfo *info = NULL;
3604 result = gnome_vfs_open (&handle, uri, GNOME_VFS_OPEN_READ);
3605 if (result != GNOME_VFS_OK) {
3610 info = gnome_vfs_file_info_new ();
3611 result = gnome_vfs_get_file_info_from_handle (handle, info, GNOME_VFS_FILE_INFO_DEFAULT);
3612 if (result != GNOME_VFS_OK || ! (info->valid_fields & GNOME_VFS_FILE_INFO_FIELDS_SIZE)) {
3613 /* We put a "safe" default size for going to cache */
3614 *expected_size = (300*1024);
3616 *expected_size = info->size;
3618 gnome_vfs_file_info_unref (info);
3620 stream = tny_vfs_stream_new (handle);
3629 TnyStream *output_stream;
3630 GtkWidget *msg_view;
3635 on_fetch_image_idle_refresh_view (gpointer userdata)
3638 FetchImageData *fidata = (FetchImageData *) userdata;
3640 gdk_threads_enter ();
3641 if (GTK_WIDGET_DRAWABLE (fidata->msg_view)) {
3642 ModestMsgViewWindowPrivate *priv;
3644 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (fidata->window);
3645 priv->fetching_images--;
3646 gtk_widget_queue_draw (fidata->msg_view);
3647 update_progress_hint (MODEST_MSG_VIEW_WINDOW (fidata->window));
3649 gdk_threads_leave ();
3651 g_object_unref (fidata->msg_view);
3652 g_object_unref (fidata->window);
3653 g_slice_free (FetchImageData, fidata);
3658 on_fetch_image_thread (gpointer userdata)
3660 FetchImageData *fidata = (FetchImageData *) userdata;
3661 TnyStreamCache *cache;
3662 TnyStream *cache_stream;
3664 cache = modest_runtime_get_images_cache ();
3666 tny_stream_cache_get_stream (cache,
3668 (TnyStreamCacheOpenStreamFetcher) fetch_image_open_stream,
3669 (gpointer) fidata->uri);
3670 g_free (fidata->cache_id);
3671 g_free (fidata->uri);
3673 if (cache_stream != NULL) {
3676 while (G_LIKELY (!tny_stream_is_eos (cache_stream))) {
3679 nb_read = tny_stream_read (cache_stream, buffer, sizeof (buffer));
3680 if (G_UNLIKELY (nb_read < 0)) {
3682 } else if (G_LIKELY (nb_read > 0)) {
3683 gssize nb_written = 0;
3685 while (G_UNLIKELY (nb_written < nb_read)) {
3688 len = tny_stream_write (fidata->output_stream, buffer + nb_written,
3689 nb_read - nb_written);
3690 if (G_UNLIKELY (len < 0))
3696 tny_stream_close (cache_stream);
3697 g_object_unref (cache_stream);
3700 tny_stream_close (fidata->output_stream);
3701 g_object_unref (fidata->output_stream);
3703 g_idle_add (on_fetch_image_idle_refresh_view, fidata);
3709 on_fetch_image (ModestMsgView *msgview,
3712 ModestMsgViewWindow *window)
3714 const gchar *current_account;
3715 ModestMsgViewWindowPrivate *priv;
3716 FetchImageData *fidata;
3718 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
3720 current_account = modest_window_get_active_account (MODEST_WINDOW (window));
3722 fidata = g_slice_new0 (FetchImageData);
3723 fidata->msg_view = g_object_ref (msgview);
3724 fidata->window = g_object_ref (window);
3725 fidata->uri = g_strdup (uri);
3726 fidata->cache_id = modest_images_cache_get_id (current_account, uri);
3727 fidata->output_stream = g_object_ref (stream);
3729 priv->fetching_images++;
3730 if (g_thread_create (on_fetch_image_thread, fidata, FALSE, NULL) == NULL) {
3731 g_object_unref (fidata->output_stream);
3732 g_free (fidata->cache_id);
3733 g_free (fidata->uri);
3734 g_object_unref (fidata->msg_view);
3735 g_slice_free (FetchImageData, fidata);
3736 tny_stream_close (stream);
3737 priv->fetching_images--;
3738 update_progress_hint (window);
3741 update_progress_hint (window);
3747 setup_menu (ModestMsgViewWindow *self)
3749 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW(self));
3751 /* Settings menu buttons */
3752 modest_window_add_to_menu (MODEST_WINDOW (self), _("mcen_me_viewer_find"), NULL,
3753 MODEST_WINDOW_MENU_CALLBACK (modest_msg_view_window_show_isearch_toolbar),
3754 MODEST_DIMMING_CALLBACK (modest_ui_dimming_rules_on_find_in_msg));
3756 modest_window_add_to_menu (MODEST_WINDOW (self),
3757 dngettext(GETTEXT_PACKAGE,
3758 "mcen_me_move_message",
3759 "mcen_me_move_messages",
3762 MODEST_WINDOW_MENU_CALLBACK (modest_ui_actions_on_move_to),
3763 MODEST_DIMMING_CALLBACK (modest_ui_dimming_rules_on_move_to));
3765 modest_window_add_to_menu (MODEST_WINDOW (self), _("mcen_me_inbox_mark_as_read"), NULL,
3766 MODEST_WINDOW_MENU_CALLBACK (modest_ui_actions_on_mark_as_read),
3767 MODEST_DIMMING_CALLBACK (modest_ui_dimming_rules_on_mark_as_read_msg_in_view));
3769 modest_window_add_to_menu (MODEST_WINDOW (self), _("mcen_me_inbox_mark_as_unread"), NULL,
3770 MODEST_WINDOW_MENU_CALLBACK (modest_ui_actions_on_mark_as_unread),
3771 MODEST_DIMMING_CALLBACK (modest_ui_dimming_rules_on_mark_as_unread_msg_in_view));
3773 modest_window_add_to_menu (MODEST_WINDOW (self), _("mcen_me_viewer_save_attachments"), NULL,
3774 MODEST_WINDOW_MENU_CALLBACK (modest_ui_actions_save_attachments),
3775 MODEST_DIMMING_CALLBACK (modest_ui_dimming_rules_on_save_attachments));
3776 modest_window_add_to_menu (MODEST_WINDOW (self), _("mcen_me_inbox_remove_attachments"), NULL,
3777 MODEST_WINDOW_MENU_CALLBACK (modest_ui_actions_remove_attachments),
3778 MODEST_DIMMING_CALLBACK (modest_ui_dimming_rules_on_remove_attachments));
3780 modest_window_add_to_menu (MODEST_WINDOW (self), _("mcen_me_new_message"), "<Control>n",
3781 MODEST_WINDOW_MENU_CALLBACK (modest_ui_actions_on_new_msg),
3782 MODEST_DIMMING_CALLBACK (modest_ui_dimming_rules_on_new_msg));
3783 modest_window_add_to_menu (MODEST_WINDOW (self), _("mcen_me_viewer_addtocontacts"), NULL,
3784 MODEST_WINDOW_MENU_CALLBACK (modest_ui_actions_add_to_contacts),
3785 MODEST_DIMMING_CALLBACK (modest_ui_dimming_rules_on_add_to_contacts));
3787 modest_window_add_to_menu (MODEST_WINDOW (self), _("mcen_ti_message_properties"), NULL,
3788 MODEST_WINDOW_MENU_CALLBACK (modest_ui_actions_on_details),
3789 MODEST_DIMMING_CALLBACK (modest_ui_dimming_rules_on_details));
3793 modest_msg_view_window_add_to_contacts (ModestMsgViewWindow *self)
3795 ModestMsgViewWindowPrivate *priv;
3796 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
3797 GSList *recipients = NULL;
3800 msg = tny_msg_view_get_msg (TNY_MSG_VIEW (priv->msg_view));
3804 header = modest_msg_view_window_get_header (self);
3807 recipients = modest_tny_msg_header_get_all_recipients_list (header);
3808 g_object_unref (header);
3810 recipients = modest_tny_msg_get_all_recipients_list (msg);
3811 g_object_unref (msg);
3815 /* Offer the user to add recipients to the address book */
3816 modest_address_book_add_address_list_with_selector (recipients, (GtkWindow *) self);
3817 g_slist_foreach (recipients, (GFunc) g_free, NULL); g_slist_free (recipients);
3822 _modest_msg_view_window_map_event (GtkWidget *widget,
3826 ModestMsgViewWindow *self = (ModestMsgViewWindow *) userdata;
3828 update_progress_hint (self);
3834 modest_msg_view_window_fetch_images (ModestMsgViewWindow *self)
3836 ModestMsgViewWindowPrivate *priv;
3837 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
3839 modest_msg_view_request_fetch_images (MODEST_MSG_VIEW (priv->msg_view));
3843 modest_msg_view_window_has_blocked_external_images (ModestMsgViewWindow *self)
3845 ModestMsgViewWindowPrivate *priv;
3846 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
3848 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self), FALSE);
3850 return modest_msg_view_has_blocked_external_images (MODEST_MSG_VIEW (priv->msg_view));
3854 modest_msg_view_window_reload (ModestMsgViewWindow *self)
3856 ModestMsgViewWindowPrivate *priv;
3857 const gchar *msg_uid;
3858 TnyHeader *header = NULL;
3859 TnyFolder *folder = NULL;
3861 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self));
3863 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
3865 header = modest_msg_view_window_get_header (MODEST_MSG_VIEW_WINDOW (self));
3869 folder = tny_header_get_folder (header);
3870 g_object_unref (header);
3875 msg_uid = modest_msg_view_window_get_message_uid (self);
3877 GtkTreeRowReference *row_reference;
3879 if (priv->row_reference && gtk_tree_row_reference_valid (priv->row_reference)) {
3880 row_reference = priv->row_reference;
3882 row_reference = NULL;
3884 if (!message_reader (self, priv, NULL, msg_uid, folder, row_reference))
3885 g_warning ("Shouldn't happen, trying to reload a message failed");
3888 g_object_unref (folder);
3892 update_branding (ModestMsgViewWindow *self)
3894 const gchar *account;
3895 const gchar *mailbox;
3896 ModestAccountMgr *mgr;
3897 ModestProtocol *protocol = NULL;
3898 gchar *service_name = NULL;
3899 const GdkPixbuf *service_icon = NULL;
3900 ModestMsgViewWindowPrivate *priv;
3902 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
3904 account = modest_window_get_active_account (MODEST_WINDOW (self));
3905 mailbox = modest_window_get_active_mailbox (MODEST_WINDOW (self));
3907 mgr = modest_runtime_get_account_mgr ();
3909 if (modest_account_mgr_account_is_multimailbox (mgr, account, &protocol)) {
3910 if (MODEST_IS_ACCOUNT_PROTOCOL (protocol)) {
3911 service_name = modest_account_protocol_get_service_name (MODEST_ACCOUNT_PROTOCOL (protocol),
3913 service_icon = modest_account_protocol_get_service_icon (MODEST_ACCOUNT_PROTOCOL (protocol),
3914 account, mailbox, MODEST_ICON_SIZE_SMALL);
3918 modest_msg_view_set_branding (MODEST_MSG_VIEW (priv->msg_view), service_name, service_icon);
3919 g_free (service_name);
3923 sync_flags (ModestMsgViewWindow *self)
3925 TnyHeader *header = NULL;
3927 header = modest_msg_view_window_get_header (self);
3929 TnyMsg *msg = modest_msg_view_window_get_message (self);
3931 header = tny_msg_get_header (msg);
3932 g_object_unref (msg);
3937 TnyFolder *folder = tny_header_get_folder (header);
3940 ModestMailOperation *mail_op;
3942 /* Sync folder, we need this to save the seen flag */
3943 mail_op = modest_mail_operation_new (NULL);
3944 modest_mail_operation_queue_add (modest_runtime_get_mail_operation_queue (),
3946 modest_mail_operation_sync_folder (mail_op, folder, FALSE, NULL, NULL);
3947 g_object_unref (mail_op);
3948 g_object_unref (folder);
3950 g_object_unref (header);