1 /* Copyright (c) 2006, Nokia Corporation
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions are
8 * * Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 * * Redistributions in binary form must reproduce the above copyright
11 * notice, this list of conditions and the following disclaimer in the
12 * documentation and/or other materials provided with the distribution.
13 * * Neither the name of the Nokia Corporation nor the names of its
14 * contributors may be used to endorse or promote products derived from
15 * this software without specific prior written permission.
17 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
18 * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
19 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
20 * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
21 * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
22 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
23 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
24 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
25 * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
26 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
27 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29 #include <glib/gi18n.h>
31 #include <tny-account-store.h>
32 #include <tny-simple-list.h>
34 #include <tny-mime-part.h>
35 #include <tny-vfs-stream.h>
36 #include <tny-error.h>
37 #include "modest-marshal.h"
38 #include "modest-platform.h"
39 #include <modest-utils.h>
40 #include <modest-maemo-utils.h>
41 #include <modest-tny-msg.h>
42 #include <modest-msg-view-window.h>
43 #include "modest-msg-view-window-ui-dimming.h"
44 #include <modest-widget-memory.h>
45 #include <modest-progress-object.h>
46 #include <modest-runtime.h>
47 #include <modest-window-priv.h>
48 #include <modest-tny-folder.h>
49 #include <modest-text-utils.h>
50 #include <modest-account-mgr-helpers.h>
51 #include <modest-toolkit-factory.h>
52 #include <modest-scrollable.h>
53 #include <modest-isearch-toolbar.h>
54 #include "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 <tny-camel-msg.h>
72 #define MYDOCS_ENV "MYDOCSDIR"
73 #define DOCS_FOLDER ".documents"
75 typedef struct _ModestMsgViewWindowPrivate ModestMsgViewWindowPrivate;
76 struct _ModestMsgViewWindowPrivate {
79 GtkWidget *main_scroll;
80 GtkWidget *isearch_toolbar;
83 /* Progress observers */
84 GSList *progress_widgets;
87 GtkWidget *prev_toolitem;
88 GtkWidget *next_toolitem;
89 gboolean progress_hint;
92 /* Optimized view enabled */
93 gboolean optimized_view;
95 /* Whether this was created via the *_new_for_search_result() function. */
96 gboolean is_search_result;
98 /* Whether the message is in outbox */
101 /* A reference to the @model of the header view
102 * to allow selecting previous/next messages,
103 * if the message is currently selected in the header view.
105 const gchar *header_folder_id;
106 GtkTreeModel *header_model;
107 GtkTreeRowReference *row_reference;
108 GtkTreeRowReference *next_row_reference;
110 gulong clipboard_change_handler;
111 gulong queue_change_handler;
112 gulong account_removed_handler;
113 gulong row_changed_handler;
114 gulong row_deleted_handler;
115 gulong row_inserted_handler;
116 gulong rows_reordered_handler;
119 GtkWidget *remove_attachment_banner;
122 TnyMimePart *other_body;
127 static void modest_msg_view_window_class_init (ModestMsgViewWindowClass *klass);
128 static void modest_msg_view_window_init (ModestMsgViewWindow *obj);
129 static void modest_header_view_observer_init (ModestHeaderViewObserverIface *iface_class);
130 static void modest_msg_view_window_finalize (GObject *obj);
131 static void modest_msg_view_window_show_isearch_toolbar (GtkWidget *obj, gpointer data);
132 static void modest_msg_view_window_isearch_toolbar_close (GtkWidget *widget,
133 ModestMsgViewWindow *obj);
134 static void modest_msg_view_window_isearch_toolbar_search (GtkWidget *widget,
135 ModestMsgViewWindow *obj);
136 static void modest_msg_view_window_toggle_isearch_toolbar (GtkWidget *obj,
138 static void modest_msg_view_window_disconnect_signals (ModestWindow *self);
140 static gdouble modest_msg_view_window_get_zoom (ModestWindow *window);
141 static void modest_msg_view_window_set_zoom (ModestWindow *window,
143 static gboolean modest_msg_view_window_zoom_minus (ModestWindow *window);
144 static gboolean modest_msg_view_window_zoom_plus (ModestWindow *window);
145 static gboolean modest_msg_view_window_key_event (GtkWidget *window,
148 static void modest_msg_view_window_update_priority (ModestMsgViewWindow *window);
150 static void modest_msg_view_window_show_toolbar (ModestWindow *window,
151 gboolean show_toolbar);
153 static void modest_msg_view_window_clipboard_owner_change (GtkClipboard *clipboard,
155 ModestMsgViewWindow *window);
157 static void modest_msg_view_window_on_row_changed (GtkTreeModel *header_model,
160 ModestMsgViewWindow *window);
162 static void modest_msg_view_window_on_row_deleted (GtkTreeModel *header_model,
164 ModestMsgViewWindow *window);
166 static void modest_msg_view_window_on_row_inserted (GtkTreeModel *header_model,
167 GtkTreePath *tree_path,
168 GtkTreeIter *tree_iter,
169 ModestMsgViewWindow *window);
171 static void modest_msg_view_window_on_row_reordered (GtkTreeModel *header_model,
175 ModestMsgViewWindow *window);
177 static void modest_msg_view_window_update_model_replaced (ModestHeaderViewObserver *window,
179 const gchar *tny_folder_id);
181 static void on_queue_changed (ModestMailOperationQueue *queue,
182 ModestMailOperation *mail_op,
183 ModestMailOperationQueueNotification type,
184 ModestMsgViewWindow *self);
186 static void on_account_removed (TnyAccountStore *account_store,
190 static void on_move_focus (GtkWidget *widget,
191 GtkDirectionType direction,
194 static void view_msg_cb (ModestMailOperation *mail_op,
201 static void set_progress_hint (ModestMsgViewWindow *self,
204 static void update_window_title (ModestMsgViewWindow *window);
206 static void init_window (ModestMsgViewWindow *obj);
208 static gboolean msg_is_visible (TnyHeader *header, gboolean check_outbox);
210 static void check_dimming_rules_after_change (ModestMsgViewWindow *window);
212 static gboolean on_fetch_image (ModestMsgView *msgview,
215 ModestMsgViewWindow *window);
217 static gboolean modest_msg_view_window_scroll_child (ModestMsgViewWindow *self,
218 GtkScrollType scroll_type,
221 static gboolean message_reader (ModestMsgViewWindow *window,
222 ModestMsgViewWindowPrivate *priv,
224 const gchar *msg_uid,
226 GtkTreeRowReference *row_reference);
228 static void setup_menu (ModestMsgViewWindow *self);
229 static gboolean _modest_msg_view_window_map_event (GtkWidget *widget,
232 static void update_branding (ModestMsgViewWindow *self);
233 static void sync_flags (ModestMsgViewWindow *self);
235 /* list my signals */
242 static const GtkActionEntry msg_view_toolbar_action_entries [] = {
245 { "ToolbarMessageReply", MODEST_STOCK_REPLY, N_("mcen_me_inbox_reply"), "<CTRL>R", NULL, G_CALLBACK (modest_ui_actions_on_reply) },
246 { "ToolbarMessageReplyAll", MODEST_STOCK_REPLY_ALL, N_("mcen_me_inbox_replytoall"), NULL, NULL, G_CALLBACK (modest_ui_actions_on_reply_all) },
247 { "ToolbarMessageForward", MODEST_STOCK_FORWARD, N_("mcen_me_inbox_forward"), NULL, NULL, G_CALLBACK (modest_ui_actions_on_forward) },
248 { "ToolbarDeleteMessage", MODEST_STOCK_DELETE, N_("qgn_toolb_gene_deletebutton"), NULL, NULL, G_CALLBACK (modest_ui_actions_on_delete_message_or_folder) },
249 { "ToolbarMessageBack", MODEST_TOOLBAR_ICON_PREV, N_("qgn_toolb_gene_back"), NULL, NULL, G_CALLBACK (modest_ui_actions_on_prev) },
250 { "ToolbarMessageNext", MODEST_TOOLBAR_ICON_NEXT, N_("qgn_toolb_gene_forward"), NULL, NULL, G_CALLBACK (modest_ui_actions_on_next) },
251 { "ToolbarDownloadExternalImages", MODEST_TOOLBAR_ICON_DOWNLOAD_IMAGES, N_("mail_bd_external_images"), NULL, NULL, G_CALLBACK (modest_ui_actions_on_fetch_images) },
254 static const GtkToggleActionEntry msg_view_toggle_action_entries [] = {
255 { "FindInMessage", MODEST_TOOLBAR_ICON_FIND, N_("qgn_toolb_gene_find"), "<CTRL>F", NULL, G_CALLBACK (modest_msg_view_window_toggle_isearch_toolbar), FALSE },
258 #define MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE((o), \
259 MODEST_TYPE_MSG_VIEW_WINDOW, \
260 ModestMsgViewWindowPrivate))
262 static GtkWindowClass *parent_class = NULL;
264 /* uncomment the following if you have defined any signals */
265 static guint signals[LAST_SIGNAL] = {0};
268 modest_msg_view_window_get_type (void)
270 static GType my_type = 0;
272 static const GTypeInfo my_info = {
273 sizeof(ModestMsgViewWindowClass),
274 NULL, /* base init */
275 NULL, /* base finalize */
276 (GClassInitFunc) modest_msg_view_window_class_init,
277 NULL, /* class finalize */
278 NULL, /* class data */
279 sizeof(ModestMsgViewWindow),
281 (GInstanceInitFunc) modest_msg_view_window_init,
284 #ifdef MODEST_TOOLKIT_GTK
285 my_type = g_type_register_static (MODEST_TYPE_WINDOW,
286 "ModestMsgViewWindow",
289 my_type = g_type_register_static (MODEST_TYPE_HILDON2_WINDOW,
290 "ModestMsgViewWindow",
294 static const GInterfaceInfo modest_header_view_observer_info =
296 (GInterfaceInitFunc) modest_header_view_observer_init,
297 NULL, /* interface_finalize */
298 NULL /* interface_data */
301 g_type_add_interface_static (my_type,
302 MODEST_TYPE_HEADER_VIEW_OBSERVER,
303 &modest_header_view_observer_info);
309 save_state (ModestWindow *self)
311 modest_widget_memory_save (modest_runtime_get_conf (),
313 MODEST_CONF_MSG_VIEW_WINDOW_KEY);
317 modest_msg_view_window_scroll_child (ModestMsgViewWindow *self,
318 GtkScrollType scroll_type,
322 ModestMsgViewWindowPrivate *priv;
325 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(self);
327 switch (scroll_type) {
328 case GTK_SCROLL_STEP_UP:
331 case GTK_SCROLL_STEP_DOWN:
334 case GTK_SCROLL_PAGE_UP:
337 case GTK_SCROLL_PAGE_DOWN:
340 case GTK_SCROLL_START:
351 modest_scrollable_scroll ((ModestScrollable *) priv->main_scroll, 0, step);
353 return (gboolean) step;
357 add_scroll_binding (GtkBindingSet *binding_set,
359 GtkScrollType scroll)
361 guint keypad_keyval = keyval - GDK_Left + GDK_KP_Left;
363 gtk_binding_entry_add_signal (binding_set, keyval, 0,
365 GTK_TYPE_SCROLL_TYPE, scroll,
366 G_TYPE_BOOLEAN, FALSE);
367 gtk_binding_entry_add_signal (binding_set, keypad_keyval, 0,
369 GTK_TYPE_SCROLL_TYPE, scroll,
370 G_TYPE_BOOLEAN, FALSE);
374 modest_msg_view_window_class_init (ModestMsgViewWindowClass *klass)
376 GObjectClass *gobject_class;
377 ModestWindowClass *modest_window_class;
378 GtkBindingSet *binding_set;
380 gobject_class = (GObjectClass*) klass;
381 modest_window_class = (ModestWindowClass *) klass;
383 parent_class = g_type_class_peek_parent (klass);
384 gobject_class->finalize = modest_msg_view_window_finalize;
386 modest_window_class->set_zoom_func = modest_msg_view_window_set_zoom;
387 modest_window_class->get_zoom_func = modest_msg_view_window_get_zoom;
388 modest_window_class->zoom_plus_func = modest_msg_view_window_zoom_plus;
389 modest_window_class->zoom_minus_func = modest_msg_view_window_zoom_minus;
390 modest_window_class->show_toolbar_func = modest_msg_view_window_show_toolbar;
391 modest_window_class->disconnect_signals_func = modest_msg_view_window_disconnect_signals;
393 modest_window_class->save_state_func = save_state;
395 klass->scroll_child = modest_msg_view_window_scroll_child;
397 signals[MSG_CHANGED_SIGNAL] =
398 g_signal_new ("msg-changed",
399 G_TYPE_FROM_CLASS (gobject_class),
401 G_STRUCT_OFFSET (ModestMsgViewWindowClass, msg_changed),
403 modest_marshal_VOID__POINTER_POINTER,
404 G_TYPE_NONE, 2, G_TYPE_POINTER, G_TYPE_POINTER);
406 signals[SCROLL_CHILD_SIGNAL] =
407 g_signal_new ("scroll-child",
408 G_TYPE_FROM_CLASS (gobject_class),
409 G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
410 G_STRUCT_OFFSET (ModestMsgViewWindowClass, scroll_child),
412 modest_marshal_BOOLEAN__ENUM_BOOLEAN,
413 G_TYPE_BOOLEAN, 2, GTK_TYPE_SCROLL_TYPE, G_TYPE_BOOLEAN);
415 binding_set = gtk_binding_set_by_class (klass);
416 add_scroll_binding (binding_set, GDK_Up, GTK_SCROLL_STEP_UP);
417 add_scroll_binding (binding_set, GDK_Down, GTK_SCROLL_STEP_DOWN);
418 add_scroll_binding (binding_set, GDK_Page_Up, GTK_SCROLL_PAGE_UP);
419 add_scroll_binding (binding_set, GDK_Page_Down, GTK_SCROLL_PAGE_DOWN);
420 add_scroll_binding (binding_set, GDK_Home, GTK_SCROLL_START);
421 add_scroll_binding (binding_set, GDK_End, GTK_SCROLL_END);
423 g_type_class_add_private (gobject_class, sizeof(ModestMsgViewWindowPrivate));
427 static void modest_header_view_observer_init(
428 ModestHeaderViewObserverIface *iface_class)
430 iface_class->update_func = modest_msg_view_window_update_model_replaced;
434 modest_msg_view_window_init (ModestMsgViewWindow *obj)
436 ModestMsgViewWindowPrivate *priv;
437 ModestWindowPrivate *parent_priv = NULL;
438 GtkActionGroup *action_group = NULL;
439 GError *error = NULL;
441 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(obj);
442 parent_priv = MODEST_WINDOW_GET_PRIVATE(obj);
443 parent_priv->ui_manager = gtk_ui_manager_new();
445 action_group = gtk_action_group_new ("ModestMsgViewWindowActions");
446 gtk_action_group_set_translation_domain (action_group, GETTEXT_PACKAGE);
448 /* Add common actions */
449 gtk_action_group_add_actions (action_group,
450 msg_view_toolbar_action_entries,
451 G_N_ELEMENTS (msg_view_toolbar_action_entries),
453 gtk_action_group_add_toggle_actions (action_group,
454 msg_view_toggle_action_entries,
455 G_N_ELEMENTS (msg_view_toggle_action_entries),
458 gtk_ui_manager_insert_action_group (parent_priv->ui_manager, action_group, 0);
459 g_object_unref (action_group);
461 /* Load the UI definition */
462 gtk_ui_manager_add_ui_from_file (parent_priv->ui_manager, MODEST_UIDIR "modest-msg-view-window-ui.xml",
465 g_printerr ("modest: could not merge modest-msg-view-window-ui.xml: %s\n", error->message);
466 g_error_free (error);
471 /* Add accelerators */
472 gtk_window_add_accel_group (GTK_WINDOW (obj),
473 gtk_ui_manager_get_accel_group (parent_priv->ui_manager));
475 priv->is_search_result = FALSE;
476 priv->is_outbox = FALSE;
478 priv->msg_view = NULL;
479 priv->header_model = NULL;
480 priv->header_folder_id = NULL;
481 priv->clipboard_change_handler = 0;
482 priv->queue_change_handler = 0;
483 priv->account_removed_handler = 0;
484 priv->row_changed_handler = 0;
485 priv->row_deleted_handler = 0;
486 priv->row_inserted_handler = 0;
487 priv->rows_reordered_handler = 0;
488 priv->progress_hint = FALSE;
489 priv->fetching_images = 0;
491 priv->optimized_view = FALSE;
492 priv->purge_timeout = 0;
493 priv->remove_attachment_banner = NULL;
494 priv->msg_uid = NULL;
495 priv->other_body = NULL;
497 priv->sighandlers = NULL;
500 init_window (MODEST_MSG_VIEW_WINDOW(obj));
505 update_progress_hint (ModestMsgViewWindow *self)
507 ModestMsgViewWindowPrivate *priv;
508 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(self);
510 if (GTK_WIDGET_VISIBLE (self)) {
511 modest_window_show_progress (MODEST_WINDOW (self),
512 (priv->progress_hint || (priv->fetching_images > 0))?1:0);
517 set_progress_hint (ModestMsgViewWindow *self,
520 ModestWindowPrivate *parent_priv;
521 ModestMsgViewWindowPrivate *priv;
523 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self));
525 parent_priv = MODEST_WINDOW_GET_PRIVATE(self);
526 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(self);
528 /* Sets current progress hint */
529 priv->progress_hint = enabled;
531 update_progress_hint (self);
537 init_window (ModestMsgViewWindow *obj)
539 GtkWidget *main_vbox;
540 ModestMsgViewWindowPrivate *priv;
542 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(obj);
544 priv->msg_view = GTK_WIDGET (tny_platform_factory_new_msg_view (modest_tny_platform_factory_get_instance ()));
545 modest_msg_view_set_shadow_type (MODEST_MSG_VIEW (priv->msg_view), GTK_SHADOW_NONE);
546 main_vbox = gtk_vbox_new (FALSE, 6);
548 priv->main_scroll = modest_toolkit_factory_create_scrollable (modest_runtime_get_toolkit_factory ());
549 modest_scrollable_set_horizontal_policy (MODEST_SCROLLABLE (priv->main_scroll), GTK_POLICY_AUTOMATIC);
550 g_object_set (G_OBJECT (priv->main_scroll),
551 "movement-mode", MODEST_MOVEMENT_MODE_BOTH,
552 "horizontal-max-overshoot", 0,
554 gtk_container_add (GTK_CONTAINER (priv->main_scroll), priv->msg_view);
555 gtk_box_pack_start (GTK_BOX(main_vbox), priv->main_scroll, TRUE, TRUE, 0);
556 gtk_container_add (GTK_CONTAINER(obj), main_vbox);
558 /* NULL-ize fields if the window is destroyed */
559 g_signal_connect (priv->msg_view, "destroy", G_CALLBACK (gtk_widget_destroyed), &(priv->msg_view));
561 gtk_widget_show_all (GTK_WIDGET(main_vbox));
565 modest_msg_view_window_disconnect_signals (ModestWindow *self)
567 ModestMsgViewWindowPrivate *priv;
568 GtkWidget *header_view = NULL;
569 GtkWindow *parent_window = NULL;
571 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
573 if (gtk_clipboard_get (GDK_SELECTION_PRIMARY) &&
574 g_signal_handler_is_connected (gtk_clipboard_get (GDK_SELECTION_PRIMARY),
575 priv->clipboard_change_handler))
576 g_signal_handler_disconnect (gtk_clipboard_get (GDK_SELECTION_PRIMARY),
577 priv->clipboard_change_handler);
579 if (g_signal_handler_is_connected (G_OBJECT (modest_runtime_get_mail_operation_queue ()),
580 priv->queue_change_handler))
581 g_signal_handler_disconnect (G_OBJECT (modest_runtime_get_mail_operation_queue ()),
582 priv->queue_change_handler);
584 if (g_signal_handler_is_connected (G_OBJECT (modest_runtime_get_account_store ()),
585 priv->account_removed_handler))
586 g_signal_handler_disconnect (G_OBJECT (modest_runtime_get_account_store ()),
587 priv->account_removed_handler);
589 if (priv->header_model) {
590 if (g_signal_handler_is_connected(G_OBJECT (priv->header_model),
591 priv->row_changed_handler))
592 g_signal_handler_disconnect(G_OBJECT (priv->header_model),
593 priv->row_changed_handler);
595 if (g_signal_handler_is_connected(G_OBJECT (priv->header_model),
596 priv->row_deleted_handler))
597 g_signal_handler_disconnect(G_OBJECT (priv->header_model),
598 priv->row_deleted_handler);
600 if (g_signal_handler_is_connected(G_OBJECT (priv->header_model),
601 priv->row_inserted_handler))
602 g_signal_handler_disconnect(G_OBJECT (priv->header_model),
603 priv->row_inserted_handler);
605 if (g_signal_handler_is_connected(G_OBJECT (priv->header_model),
606 priv->rows_reordered_handler))
607 g_signal_handler_disconnect(G_OBJECT (priv->header_model),
608 priv->rows_reordered_handler);
611 modest_signal_mgr_disconnect_all_and_destroy (priv->sighandlers);
612 priv->sighandlers = NULL;
614 parent_window = gtk_window_get_transient_for (GTK_WINDOW (self));
615 if (parent_window && MODEST_IS_HEADER_WINDOW (parent_window)) {
616 header_view = GTK_WIDGET (modest_header_window_get_header_view (MODEST_HEADER_WINDOW (parent_window)));
618 modest_header_view_remove_observer(MODEST_HEADER_VIEW (header_view),
619 MODEST_HEADER_VIEW_OBSERVER(self));
625 modest_msg_view_window_finalize (GObject *obj)
627 ModestMsgViewWindowPrivate *priv;
629 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (obj);
631 /* Sanity check: shouldn't be needed, the window mgr should
632 call this function before */
633 modest_msg_view_window_disconnect_signals (MODEST_WINDOW (obj));
635 if (priv->other_body != NULL) {
636 g_object_unref (priv->other_body);
637 priv->other_body = NULL;
640 if (priv->header_model != NULL) {
641 g_object_unref (priv->header_model);
642 priv->header_model = NULL;
645 if (priv->remove_attachment_banner) {
646 gtk_widget_destroy (priv->remove_attachment_banner);
647 g_object_unref (priv->remove_attachment_banner);
648 priv->remove_attachment_banner = NULL;
651 if (priv->purge_timeout > 0) {
652 g_source_remove (priv->purge_timeout);
653 priv->purge_timeout = 0;
656 if (priv->row_reference) {
657 gtk_tree_row_reference_free (priv->row_reference);
658 priv->row_reference = NULL;
661 if (priv->next_row_reference) {
662 gtk_tree_row_reference_free (priv->next_row_reference);
663 priv->next_row_reference = NULL;
667 g_free (priv->msg_uid);
668 priv->msg_uid = NULL;
671 G_OBJECT_CLASS(parent_class)->finalize (obj);
675 select_next_valid_row (GtkTreeModel *model,
676 GtkTreeRowReference **row_reference,
680 GtkTreeIter tmp_iter;
682 GtkTreePath *next = NULL;
683 gboolean retval = FALSE, finished;
685 g_return_val_if_fail (gtk_tree_row_reference_valid (*row_reference), FALSE);
687 path = gtk_tree_row_reference_get_path (*row_reference);
688 gtk_tree_model_get_iter (model, &tmp_iter, path);
689 gtk_tree_row_reference_free (*row_reference);
690 *row_reference = NULL;
694 TnyHeader *header = NULL;
696 if (gtk_tree_model_iter_next (model, &tmp_iter)) {
697 gtk_tree_model_get (model, &tmp_iter,
698 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
702 if (msg_is_visible (header, is_outbox)) {
703 next = gtk_tree_model_get_path (model, &tmp_iter);
704 *row_reference = gtk_tree_row_reference_new (model, next);
705 gtk_tree_path_free (next);
709 g_object_unref (header);
712 } else if (cycle && gtk_tree_model_get_iter_first (model, &tmp_iter)) {
713 next = gtk_tree_model_get_path (model, &tmp_iter);
715 /* Ensure that we are not selecting the same */
716 if (gtk_tree_path_compare (path, next) != 0) {
717 gtk_tree_model_get (model, &tmp_iter,
718 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
721 if (msg_is_visible (header, is_outbox)) {
722 *row_reference = gtk_tree_row_reference_new (model, next);
726 g_object_unref (header);
730 /* If we ended up in the same message
731 then there is no valid next
735 gtk_tree_path_free (next);
737 /* If there are no more messages and we don't
738 want to start again in the first one then
739 there is no valid next message */
745 gtk_tree_path_free (path);
750 /* TODO: This should be in _init(), with the parameters as properties. */
752 modest_msg_view_window_construct (ModestMsgViewWindow *self,
753 const gchar *modest_account_name,
754 const gchar *mailbox,
755 const gchar *msg_uid)
758 ModestMsgViewWindowPrivate *priv = NULL;
759 ModestWindowPrivate *parent_priv = NULL;
760 ModestDimmingRulesGroup *toolbar_rules_group = NULL;
761 ModestDimmingRulesGroup *clipboard_rules_group = NULL;
763 obj = G_OBJECT (self);
764 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(obj);
765 parent_priv = MODEST_WINDOW_GET_PRIVATE(obj);
767 priv->msg_uid = g_strdup (msg_uid);
770 parent_priv->menubar = NULL;
772 toolbar_rules_group = modest_dimming_rules_group_new (MODEST_DIMMING_RULES_TOOLBAR, TRUE);
773 clipboard_rules_group = modest_dimming_rules_group_new (MODEST_DIMMING_RULES_CLIPBOARD, FALSE);
776 /* Add common dimming rules */
777 modest_dimming_rules_group_add_rules (toolbar_rules_group,
778 modest_msg_view_toolbar_dimming_entries,
779 G_N_ELEMENTS (modest_msg_view_toolbar_dimming_entries),
780 MODEST_WINDOW (self));
781 modest_dimming_rules_group_add_rules (clipboard_rules_group,
782 modest_msg_view_clipboard_dimming_entries,
783 G_N_ELEMENTS (modest_msg_view_clipboard_dimming_entries),
784 MODEST_WINDOW (self));
786 /* Insert dimming rules group for this window */
787 modest_ui_dimming_manager_insert_rules_group (parent_priv->ui_dimming_manager, toolbar_rules_group);
788 modest_ui_dimming_manager_insert_rules_group (parent_priv->ui_dimming_manager, clipboard_rules_group);
789 g_object_unref (toolbar_rules_group);
790 g_object_unref (clipboard_rules_group);
792 /* g_signal_connect (G_OBJECT(obj), "delete-event", G_CALLBACK(on_delete_event), obj); */
794 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);
795 g_signal_connect (G_OBJECT(priv->msg_view), "activate_link",
796 G_CALLBACK (modest_ui_actions_on_msg_link_clicked), obj);
797 g_signal_connect (G_OBJECT(priv->msg_view), "link_hover",
798 G_CALLBACK (modest_ui_actions_on_msg_link_hover), obj);
799 g_signal_connect (G_OBJECT(priv->msg_view), "attachment_clicked",
800 G_CALLBACK (modest_ui_actions_on_msg_attachment_clicked), obj);
801 g_signal_connect (G_OBJECT(priv->msg_view), "recpt_activated",
802 G_CALLBACK (modest_ui_actions_on_msg_recpt_activated), obj);
803 g_signal_connect (G_OBJECT(priv->msg_view), "show_details",
804 G_CALLBACK (modest_ui_actions_on_details), obj);
805 g_signal_connect (G_OBJECT(priv->msg_view), "link_contextual",
806 G_CALLBACK (modest_ui_actions_on_msg_link_contextual), obj);
807 g_signal_connect (G_OBJECT(priv->msg_view), "limit_error",
808 G_CALLBACK (modest_ui_actions_on_limit_error), obj);
809 g_signal_connect (G_OBJECT (priv->msg_view), "fetch_image",
810 G_CALLBACK (on_fetch_image), obj);
812 g_signal_connect (G_OBJECT (obj), "key-release-event",
813 G_CALLBACK (modest_msg_view_window_key_event),
816 g_signal_connect (G_OBJECT (obj), "key-press-event",
817 G_CALLBACK (modest_msg_view_window_key_event),
820 g_signal_connect (G_OBJECT (obj), "move-focus",
821 G_CALLBACK (on_move_focus), obj);
823 g_signal_connect (G_OBJECT (obj), "map-event",
824 G_CALLBACK (_modest_msg_view_window_map_event),
827 /* Mail Operation Queue */
828 priv->queue_change_handler = g_signal_connect (G_OBJECT (modest_runtime_get_mail_operation_queue ()),
830 G_CALLBACK (on_queue_changed),
833 /* Account manager */
834 priv->account_removed_handler = g_signal_connect (G_OBJECT (modest_runtime_get_account_store ()),
836 G_CALLBACK(on_account_removed),
839 modest_window_set_active_account (MODEST_WINDOW(obj), modest_account_name);
840 modest_window_set_active_mailbox (MODEST_WINDOW(obj), mailbox);
842 /* First add out toolbar ... */
843 modest_msg_view_window_show_toolbar (MODEST_WINDOW (obj), TRUE);
845 priv->isearch_toolbar = modest_toolkit_factory_create_isearch_toolbar (modest_runtime_get_toolkit_factory (),
847 modest_window_add_toolbar (MODEST_WINDOW (obj), GTK_TOOLBAR (priv->isearch_toolbar));
848 gtk_widget_set_no_show_all (priv->isearch_toolbar, TRUE);
849 g_signal_connect (G_OBJECT (priv->isearch_toolbar), "isearch-close",
850 G_CALLBACK (modest_msg_view_window_isearch_toolbar_close), obj);
851 g_signal_connect (G_OBJECT (priv->isearch_toolbar), "isearch-search",
852 G_CALLBACK (modest_msg_view_window_isearch_toolbar_search), obj);
853 priv->last_search = NULL;
855 /* Init the clipboard actions dim status */
856 modest_msg_view_grab_focus(MODEST_MSG_VIEW (priv->msg_view));
858 update_window_title (MODEST_MSG_VIEW_WINDOW (obj));
863 /* FIXME: parameter checks */
865 modest_msg_view_window_new_with_header_model (TnyMsg *msg,
866 const gchar *modest_account_name,
867 const gchar *mailbox,
868 const gchar *msg_uid,
870 GtkTreeRowReference *row_reference)
872 ModestMsgViewWindow *window = NULL;
873 ModestMsgViewWindowPrivate *priv = NULL;
874 TnyFolder *header_folder = NULL;
875 ModestHeaderView *header_view = NULL;
876 ModestWindowMgr *mgr = NULL;
879 modest_tny_mime_part_to_string (TNY_MIME_PART (msg), 0);
882 mgr = modest_runtime_get_window_mgr ();
883 window = MODEST_MSG_VIEW_WINDOW (modest_window_mgr_get_msg_view_window (mgr));
884 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), NULL);
886 modest_msg_view_window_construct (window, modest_account_name, mailbox, msg_uid);
888 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
890 /* Remember the message list's TreeModel so we can detect changes
891 * and change the list selection when necessary: */
892 header_folder = modest_header_view_get_folder (header_view);
894 priv->is_outbox = (modest_tny_folder_guess_folder_type (header_folder) ==
895 TNY_FOLDER_TYPE_OUTBOX);
896 priv->header_folder_id = tny_folder_get_id (header_folder);
897 g_object_unref(header_folder);
900 /* Setup row references and connect signals */
901 priv->header_model = g_object_ref (model);
903 if (row_reference && gtk_tree_row_reference_valid (row_reference)) {
904 priv->row_reference = gtk_tree_row_reference_copy (row_reference);
905 priv->next_row_reference = gtk_tree_row_reference_copy (row_reference);
906 select_next_valid_row (model, &(priv->next_row_reference), TRUE, priv->is_outbox);
908 priv->row_reference = NULL;
909 priv->next_row_reference = NULL;
912 /* Connect signals */
913 priv->row_changed_handler =
914 g_signal_connect (GTK_TREE_MODEL(model), "row-changed",
915 G_CALLBACK(modest_msg_view_window_on_row_changed),
917 priv->row_deleted_handler =
918 g_signal_connect (GTK_TREE_MODEL(model), "row-deleted",
919 G_CALLBACK(modest_msg_view_window_on_row_deleted),
921 priv->row_inserted_handler =
922 g_signal_connect (GTK_TREE_MODEL(model), "row-inserted",
923 G_CALLBACK(modest_msg_view_window_on_row_inserted),
925 priv->rows_reordered_handler =
926 g_signal_connect(GTK_TREE_MODEL(model), "rows-reordered",
927 G_CALLBACK(modest_msg_view_window_on_row_reordered),
930 if (header_view != NULL){
931 modest_header_view_add_observer(header_view,
932 MODEST_HEADER_VIEW_OBSERVER(window));
935 tny_msg_view_set_msg (TNY_MSG_VIEW (priv->msg_view), msg);
936 update_window_title (MODEST_MSG_VIEW_WINDOW (window));
937 update_branding (MODEST_MSG_VIEW_WINDOW (window));
939 /* gtk_widget_show_all (GTK_WIDGET (window)); */
940 modest_msg_view_window_update_priority (window);
941 /* Check dimming rules */
942 modest_ui_actions_check_toolbar_dimming_rules (MODEST_WINDOW (window));
943 modest_ui_actions_check_menu_dimming_rules (MODEST_WINDOW (window));
944 modest_window_check_dimming_rules_group (MODEST_WINDOW (window), MODEST_DIMMING_RULES_CLIPBOARD);
946 return MODEST_WINDOW(window);
950 modest_msg_view_window_new_from_uid (const gchar *modest_account_name,
951 const gchar *mailbox,
952 const gchar *msg_uid)
954 ModestMsgViewWindow *window = NULL;
955 ModestMsgViewWindowPrivate *priv = NULL;
956 ModestWindowMgr *mgr = NULL;
958 TnyAccount *account = NULL;
960 mgr = modest_runtime_get_window_mgr ();
961 window = MODEST_MSG_VIEW_WINDOW (modest_window_mgr_get_msg_view_window (mgr));
962 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), NULL);
964 modest_msg_view_window_construct (window, modest_account_name, mailbox, msg_uid);
966 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
968 is_merge = g_str_has_prefix (msg_uid, "merge:");
970 /* Get the account */
972 account = tny_account_store_find_account (TNY_ACCOUNT_STORE (modest_runtime_get_account_store ()),
975 if (is_merge || account) {
976 TnyFolder *folder = NULL;
978 /* Try to get the message, if it's already downloaded
979 we don't need to connect */
981 folder = modest_tny_folder_store_find_folder_from_uri (TNY_FOLDER_STORE (account), msg_uid);
983 ModestTnyAccountStore *account_store;
984 ModestTnyLocalFoldersAccount *local_folders_account;
986 account_store = modest_runtime_get_account_store ();
987 local_folders_account = MODEST_TNY_LOCAL_FOLDERS_ACCOUNT (
988 modest_tny_account_store_get_local_folders_account (account_store));
989 folder = modest_tny_local_folders_account_get_merged_outbox (local_folders_account);
990 g_object_unref (local_folders_account);
994 gboolean device_online;
996 device = modest_runtime_get_device();
997 device_online = tny_device_is_online (device);
999 message_reader (window, priv, NULL, msg_uid, folder, NULL);
1001 TnyMsg *msg = tny_folder_find_msg (folder, msg_uid, NULL);
1003 tny_msg_view_set_msg (TNY_MSG_VIEW (priv->msg_view), msg);
1004 update_window_title (MODEST_MSG_VIEW_WINDOW (window));
1005 update_branding (MODEST_MSG_VIEW_WINDOW (window));
1006 g_object_unref (msg);
1007 /* Sync flags to server */
1008 sync_flags (MODEST_MSG_VIEW_WINDOW (window));
1010 message_reader (window, priv, NULL, msg_uid, folder, NULL);
1013 g_object_unref (folder);
1018 /* Check dimming rules */
1019 modest_ui_actions_check_toolbar_dimming_rules (MODEST_WINDOW (window));
1020 modest_ui_actions_check_menu_dimming_rules (MODEST_WINDOW (window));
1021 modest_window_check_dimming_rules_group (MODEST_WINDOW (window), MODEST_DIMMING_RULES_CLIPBOARD);
1023 return MODEST_WINDOW(window);
1027 modest_msg_view_window_new_from_header_view (ModestHeaderView *header_view,
1028 const gchar *modest_account_name,
1029 const gchar *mailbox,
1030 const gchar *msg_uid,
1031 GtkTreeRowReference *row_reference)
1033 ModestMsgViewWindow *window = NULL;
1034 ModestMsgViewWindowPrivate *priv = NULL;
1035 TnyFolder *header_folder = NULL;
1036 ModestWindowMgr *mgr = NULL;
1040 mgr = modest_runtime_get_window_mgr ();
1041 window = MODEST_MSG_VIEW_WINDOW (modest_window_mgr_get_msg_view_window (mgr));
1042 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), NULL);
1044 modest_msg_view_window_construct (window, modest_account_name, mailbox, msg_uid);
1046 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1048 /* Remember the message list's TreeModel so we can detect changes
1049 * and change the list selection when necessary: */
1051 if (header_view != NULL){
1052 header_folder = modest_header_view_get_folder(header_view);
1053 /* This could happen if the header folder was
1054 unseleted before opening this msg window (for
1055 example if the user selects an account in the
1056 folder view of the main window */
1057 if (header_folder) {
1058 priv->is_outbox = (modest_tny_folder_guess_folder_type (header_folder) ==
1059 TNY_FOLDER_TYPE_OUTBOX);
1060 priv->header_folder_id = tny_folder_get_id(header_folder);
1061 g_object_unref(header_folder);
1065 /* Setup row references and connect signals */
1066 priv->header_model = gtk_tree_view_get_model (GTK_TREE_VIEW (header_view));
1067 g_object_ref (priv->header_model);
1069 if (row_reference && gtk_tree_row_reference_valid (row_reference)) {
1070 priv->row_reference = gtk_tree_row_reference_copy (row_reference);
1071 priv->next_row_reference = gtk_tree_row_reference_copy (row_reference);
1072 select_next_valid_row (priv->header_model, &(priv->next_row_reference), TRUE, priv->is_outbox);
1074 priv->row_reference = NULL;
1075 priv->next_row_reference = NULL;
1078 /* Connect signals */
1079 priv->row_changed_handler =
1080 g_signal_connect (GTK_TREE_MODEL(priv->header_model), "row-changed",
1081 G_CALLBACK(modest_msg_view_window_on_row_changed),
1083 priv->row_deleted_handler =
1084 g_signal_connect (GTK_TREE_MODEL(priv->header_model), "row-deleted",
1085 G_CALLBACK(modest_msg_view_window_on_row_deleted),
1087 priv->row_inserted_handler =
1088 g_signal_connect (GTK_TREE_MODEL(priv->header_model), "row-inserted",
1089 G_CALLBACK(modest_msg_view_window_on_row_inserted),
1091 priv->rows_reordered_handler =
1092 g_signal_connect(GTK_TREE_MODEL(priv->header_model), "rows-reordered",
1093 G_CALLBACK(modest_msg_view_window_on_row_reordered),
1096 if (header_view != NULL){
1097 modest_header_view_add_observer(header_view,
1098 MODEST_HEADER_VIEW_OBSERVER(window));
1101 tny_msg_view_set_msg (TNY_MSG_VIEW (priv->msg_view), NULL);
1102 update_branding (MODEST_MSG_VIEW_WINDOW (window));
1104 if (priv->row_reference) {
1105 path = gtk_tree_row_reference_get_path (priv->row_reference);
1106 if (gtk_tree_model_get_iter (priv->header_model, &iter, path)) {
1108 gtk_tree_model_get (priv->header_model, &iter,
1109 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
1111 message_reader (window, priv, header, NULL, NULL, priv->row_reference);
1112 g_object_unref (header);
1114 gtk_tree_path_free (path);
1116 /* Check dimming rules */
1117 modest_ui_actions_check_toolbar_dimming_rules (MODEST_WINDOW (window));
1118 modest_ui_actions_check_menu_dimming_rules (MODEST_WINDOW (window));
1119 modest_window_check_dimming_rules_group (MODEST_WINDOW (window), MODEST_DIMMING_RULES_CLIPBOARD);
1121 return MODEST_WINDOW(window);
1125 modest_msg_view_window_new_for_search_result (TnyMsg *msg,
1126 const gchar *modest_account_name,
1127 const gchar *mailbox,
1128 const gchar *msg_uid)
1130 ModestMsgViewWindow *window = NULL;
1131 ModestMsgViewWindowPrivate *priv = NULL;
1132 ModestWindowMgr *mgr = NULL;
1134 mgr = modest_runtime_get_window_mgr ();
1135 window = MODEST_MSG_VIEW_WINDOW (modest_window_mgr_get_msg_view_window (mgr));
1136 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), NULL);
1137 modest_msg_view_window_construct (window, modest_account_name, mailbox, msg_uid);
1139 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1141 /* Remember that this is a search result,
1142 * so we can disable some UI appropriately: */
1143 priv->is_search_result = TRUE;
1145 tny_msg_view_set_msg (TNY_MSG_VIEW (priv->msg_view), msg);
1146 update_branding (MODEST_MSG_VIEW_WINDOW (window));
1148 update_window_title (window);
1149 /* gtk_widget_show_all (GTK_WIDGET (window));*/
1150 modest_msg_view_window_update_priority (window);
1152 /* Check dimming rules */
1153 modest_ui_actions_check_toolbar_dimming_rules (MODEST_WINDOW (window));
1154 modest_ui_actions_check_menu_dimming_rules (MODEST_WINDOW (window));
1155 modest_window_check_dimming_rules_group (MODEST_WINDOW (window), MODEST_DIMMING_RULES_CLIPBOARD);
1157 return MODEST_WINDOW(window);
1161 modest_msg_view_window_is_other_body (ModestMsgViewWindow *self)
1163 ModestMsgViewWindowPrivate *priv = NULL;
1165 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self), FALSE);
1166 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
1168 return (priv->other_body != NULL);
1172 modest_msg_view_window_new_with_other_body (TnyMsg *msg,
1173 TnyMimePart *other_body,
1174 const gchar *modest_account_name,
1175 const gchar *mailbox,
1176 const gchar *msg_uid)
1178 GObject *obj = NULL;
1179 ModestMsgViewWindowPrivate *priv;
1180 ModestWindowMgr *mgr = NULL;
1182 g_return_val_if_fail (msg, NULL);
1183 mgr = modest_runtime_get_window_mgr ();
1184 obj = G_OBJECT (modest_window_mgr_get_msg_view_window (mgr));
1185 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (obj);
1186 modest_msg_view_window_construct (MODEST_MSG_VIEW_WINDOW (obj),
1187 modest_account_name, mailbox, msg_uid);
1190 priv->other_body = g_object_ref (other_body);
1191 modest_msg_view_set_msg_with_other_body (MODEST_MSG_VIEW (priv->msg_view), msg, other_body);
1193 tny_msg_view_set_msg (TNY_MSG_VIEW (priv->msg_view), msg);
1195 update_window_title (MODEST_MSG_VIEW_WINDOW (obj));
1196 update_branding (MODEST_MSG_VIEW_WINDOW (obj));
1198 /* gtk_widget_show_all (GTK_WIDGET (obj)); */
1200 /* Check dimming rules */
1201 modest_ui_actions_check_toolbar_dimming_rules (MODEST_WINDOW (obj));
1202 modest_ui_actions_check_menu_dimming_rules (MODEST_WINDOW (obj));
1203 modest_window_check_dimming_rules_group (MODEST_WINDOW (obj), MODEST_DIMMING_RULES_CLIPBOARD);
1205 return MODEST_WINDOW(obj);
1209 modest_msg_view_window_new_for_attachment (TnyMsg *msg,
1210 const gchar *modest_account_name,
1211 const gchar *mailbox,
1212 const gchar *msg_uid)
1214 return modest_msg_view_window_new_with_other_body (msg, NULL, modest_account_name, mailbox, msg_uid);
1218 modest_msg_view_window_on_row_changed (GtkTreeModel *header_model,
1221 ModestMsgViewWindow *window)
1223 check_dimming_rules_after_change (window);
1227 modest_msg_view_window_on_row_deleted(GtkTreeModel *header_model,
1229 ModestMsgViewWindow *window)
1231 check_dimming_rules_after_change (window);
1233 /* The window could have dissapeared */
1236 check_dimming_rules_after_change (ModestMsgViewWindow *window)
1238 modest_ui_actions_check_menu_dimming_rules (MODEST_WINDOW (window));
1239 modest_ui_actions_check_toolbar_dimming_rules (MODEST_WINDOW (window));
1243 /* On insertions we check if the folder still has the message we are
1244 * showing or do not. If do not, we do nothing. Which means we are still
1245 * not attached to any header folder and thus next/prev buttons are
1246 * still dimmed. Once the message that is shown by msg-view is found, the
1247 * new model of header-view will be attached and the references will be set.
1248 * On each further insertions dimming rules will be checked. However
1249 * this requires extra CPU time at least works.
1250 * (An message might be deleted from TnyFolder and thus will not be
1251 * inserted into the model again for example if it is removed by the
1252 * imap server and the header view is refreshed.)
1255 modest_msg_view_window_on_row_inserted (GtkTreeModel *model,
1256 GtkTreePath *tree_path,
1257 GtkTreeIter *tree_iter,
1258 ModestMsgViewWindow *window)
1260 ModestMsgViewWindowPrivate *priv = NULL;
1261 TnyHeader *header = NULL;
1263 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window));
1264 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1266 g_assert (model == priv->header_model);
1268 /* Check if the newly inserted message is the same we are actually
1269 * showing. IF not, we should remain detached from the header model
1270 * and thus prev and next toolbar buttons should remain dimmed. */
1271 gtk_tree_model_get (model, tree_iter,
1272 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
1275 if (TNY_IS_HEADER (header)) {
1278 uid = modest_tny_folder_get_header_unique_id (header);
1279 if (!g_str_equal(priv->msg_uid, uid)) {
1280 check_dimming_rules_after_change (window);
1282 g_object_unref (G_OBJECT(header));
1286 g_object_unref(G_OBJECT(header));
1289 if (priv->row_reference) {
1290 gtk_tree_row_reference_free (priv->row_reference);
1293 /* Setup row_reference for the actual msg. */
1294 priv->row_reference = gtk_tree_row_reference_new (priv->header_model, tree_path);
1295 if (priv->row_reference == NULL) {
1296 g_warning("%s: No reference for msg header item.", __FUNCTION__);
1300 /* Now set up next_row_reference. */
1301 if (priv->next_row_reference) {
1302 gtk_tree_row_reference_free (priv->next_row_reference);
1305 priv->next_row_reference = gtk_tree_row_reference_copy (priv->row_reference);
1306 select_next_valid_row (priv->header_model,
1307 &(priv->next_row_reference), FALSE, priv->is_outbox);
1309 /* Connect the remaining callbacks to become able to detect
1310 * changes in header-view. */
1311 priv->row_changed_handler =
1312 g_signal_connect (priv->header_model, "row-changed",
1313 G_CALLBACK (modest_msg_view_window_on_row_changed),
1315 priv->row_deleted_handler =
1316 g_signal_connect (priv->header_model, "row-deleted",
1317 G_CALLBACK (modest_msg_view_window_on_row_deleted),
1319 priv->rows_reordered_handler =
1320 g_signal_connect (priv->header_model, "rows-reordered",
1321 G_CALLBACK (modest_msg_view_window_on_row_reordered),
1324 check_dimming_rules_after_change (window);
1328 modest_msg_view_window_on_row_reordered (GtkTreeModel *header_model,
1332 ModestMsgViewWindow *window)
1334 ModestMsgViewWindowPrivate *priv = NULL;
1335 gboolean already_changed = FALSE;
1337 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(window);
1339 /* If the current row was reordered select the proper next
1340 valid row. The same if the next row reference changes */
1341 if (!priv->row_reference ||
1342 !gtk_tree_row_reference_valid (priv->row_reference))
1345 if (priv->next_row_reference &&
1346 gtk_tree_row_reference_valid (priv->next_row_reference)) {
1347 GtkTreePath *cur, *next;
1348 /* Check that the order is still the correct one */
1349 cur = gtk_tree_row_reference_get_path (priv->row_reference);
1350 next = gtk_tree_row_reference_get_path (priv->next_row_reference);
1351 gtk_tree_path_next (cur);
1352 if (gtk_tree_path_compare (cur, next) != 0) {
1353 gtk_tree_row_reference_free (priv->next_row_reference);
1354 priv->next_row_reference = gtk_tree_row_reference_copy (priv->row_reference);
1355 select_next_valid_row (header_model, &(priv->next_row_reference), FALSE, priv->is_outbox);
1356 already_changed = TRUE;
1358 gtk_tree_path_free (cur);
1359 gtk_tree_path_free (next);
1361 if (priv->next_row_reference)
1362 gtk_tree_row_reference_free (priv->next_row_reference);
1363 /* Update next row reference */
1364 priv->next_row_reference = gtk_tree_row_reference_copy (priv->row_reference);
1365 select_next_valid_row (header_model, &(priv->next_row_reference), FALSE, priv->is_outbox);
1366 already_changed = TRUE;
1369 check_dimming_rules_after_change (window);
1372 /* The modest_msg_view_window_update_model_replaced implements update
1373 * function for ModestHeaderViewObserver. Checks whether the TnyFolder
1374 * actually belongs to the header-view is the same as the TnyFolder of
1375 * the message of msg-view or not. If they are different, there is
1376 * nothing to do. If they are the same, then the model has replaced and
1377 * the reference in msg-view shall be replaced from the old model to
1378 * the new model. In this case the view will be detached from it's
1379 * header folder. From this point the next/prev buttons are dimmed.
1382 modest_msg_view_window_update_model_replaced (ModestHeaderViewObserver *observer,
1383 GtkTreeModel *model,
1384 const gchar *tny_folder_id)
1386 ModestMsgViewWindowPrivate *priv = NULL;
1387 ModestMsgViewWindow *window = NULL;
1389 g_assert(MODEST_IS_HEADER_VIEW_OBSERVER(observer));
1390 g_assert(MODEST_IS_MSG_VIEW_WINDOW(observer));
1392 window = MODEST_MSG_VIEW_WINDOW(observer);
1393 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(window);
1395 /* If there is an other folder in the header-view then we do
1396 * not care about it's model (msg list). Else if the
1397 * header-view shows the folder the msg shown by us is in, we
1398 * shall replace our model reference and make some check. */
1399 if(model == NULL || tny_folder_id == NULL ||
1400 (priv->header_folder_id && !g_str_equal(tny_folder_id, priv->header_folder_id)))
1403 /* Model is changed(replaced), so we should forget the old
1404 * one. Because there might be other references and there
1405 * might be some change on the model even if we unreferenced
1406 * it, we need to disconnect our signals here. */
1407 if (priv->header_model) {
1408 if (g_signal_handler_is_connected(G_OBJECT (priv->header_model),
1409 priv->row_changed_handler))
1410 g_signal_handler_disconnect(G_OBJECT (priv->header_model),
1411 priv->row_changed_handler);
1412 if (g_signal_handler_is_connected(G_OBJECT (priv->header_model),
1413 priv->row_deleted_handler))
1414 g_signal_handler_disconnect(G_OBJECT (priv->header_model),
1415 priv->row_deleted_handler);
1416 if (g_signal_handler_is_connected(G_OBJECT (priv->header_model),
1417 priv->row_inserted_handler))
1418 g_signal_handler_disconnect(G_OBJECT (priv->header_model),
1419 priv->row_inserted_handler);
1420 if (g_signal_handler_is_connected(G_OBJECT (priv->header_model),
1421 priv->rows_reordered_handler))
1422 g_signal_handler_disconnect(G_OBJECT (priv->header_model),
1423 priv->rows_reordered_handler);
1426 if (priv->row_reference)
1427 gtk_tree_row_reference_free (priv->row_reference);
1428 if (priv->next_row_reference)
1429 gtk_tree_row_reference_free (priv->next_row_reference);
1430 g_object_unref(priv->header_model);
1433 priv->row_changed_handler = 0;
1434 priv->row_deleted_handler = 0;
1435 priv->row_inserted_handler = 0;
1436 priv->rows_reordered_handler = 0;
1437 priv->next_row_reference = NULL;
1438 priv->row_reference = NULL;
1439 priv->header_model = NULL;
1442 priv->header_model = g_object_ref (model);
1444 /* Also we must connect to the new model for row insertions.
1445 * Only for insertions now. We will need other ones only after
1446 * the msg is show by msg-view is added to the new model. */
1447 priv->row_inserted_handler =
1448 g_signal_connect (priv->header_model, "row-inserted",
1449 G_CALLBACK(modest_msg_view_window_on_row_inserted),
1452 modest_ui_actions_check_menu_dimming_rules(MODEST_WINDOW(window));
1453 modest_ui_actions_check_toolbar_dimming_rules(MODEST_WINDOW(window));
1457 modest_msg_view_window_toolbar_on_transfer_mode (ModestMsgViewWindow *self)
1459 ModestMsgViewWindowPrivate *priv= NULL;
1461 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self), FALSE);
1462 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
1464 return priv->progress_hint;
1468 modest_msg_view_window_get_header (ModestMsgViewWindow *self)
1470 ModestMsgViewWindowPrivate *priv= NULL;
1472 TnyHeader *header = NULL;
1473 GtkTreePath *path = NULL;
1476 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self), NULL);
1477 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
1479 /* If the message was not obtained from a treemodel,
1480 * for instance if it was opened directly by the search UI:
1482 if (priv->header_model == NULL ||
1483 priv->row_reference == NULL ||
1484 !gtk_tree_row_reference_valid (priv->row_reference)) {
1485 msg = modest_msg_view_window_get_message (self);
1487 header = tny_msg_get_header (msg);
1488 g_object_unref (msg);
1493 /* Get iter of the currently selected message in the header view: */
1494 path = gtk_tree_row_reference_get_path (priv->row_reference);
1495 g_return_val_if_fail (path != NULL, NULL);
1496 gtk_tree_model_get_iter (priv->header_model,
1500 /* Get current message header */
1501 gtk_tree_model_get (priv->header_model, &iter,
1502 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
1505 gtk_tree_path_free (path);
1510 modest_msg_view_window_get_message (ModestMsgViewWindow *self)
1512 ModestMsgViewWindowPrivate *priv;
1514 g_return_val_if_fail (self, NULL);
1516 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(self);
1518 return tny_msg_view_get_msg (TNY_MSG_VIEW (priv->msg_view));
1522 modest_msg_view_window_get_message_uid (ModestMsgViewWindow *self)
1524 ModestMsgViewWindowPrivate *priv;
1526 g_return_val_if_fail (self, NULL);
1528 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
1530 return (const gchar*) priv->msg_uid;
1533 /* Used for the Ctrl+F accelerator */
1535 modest_msg_view_window_toggle_isearch_toolbar (GtkWidget *obj,
1538 ModestMsgViewWindow *window = MODEST_MSG_VIEW_WINDOW (data);
1539 ModestMsgViewWindowPrivate *priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1541 if (GTK_WIDGET_VISIBLE (priv->isearch_toolbar)) {
1542 modest_msg_view_window_isearch_toolbar_close (obj, data);
1544 modest_msg_view_window_show_isearch_toolbar (obj, data);
1548 /* Handler for menu option */
1550 modest_msg_view_window_show_isearch_toolbar (GtkWidget *obj,
1553 ModestMsgViewWindow *window = MODEST_MSG_VIEW_WINDOW (data);
1554 ModestMsgViewWindowPrivate *priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1556 gtk_widget_show (priv->isearch_toolbar);
1557 modest_isearch_toolbar_highlight_entry (MODEST_ISEARCH_TOOLBAR (priv->isearch_toolbar), TRUE);
1560 /* Handler for click on the "X" close button in isearch toolbar */
1562 modest_msg_view_window_isearch_toolbar_close (GtkWidget *widget,
1563 ModestMsgViewWindow *obj)
1565 ModestMsgViewWindowPrivate *priv;
1567 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (obj);
1570 gtk_widget_hide (priv->isearch_toolbar);
1571 modest_msg_view_grab_focus (MODEST_MSG_VIEW (priv->msg_view));
1575 modest_msg_view_window_isearch_toolbar_search (GtkWidget *widget,
1576 ModestMsgViewWindow *obj)
1578 const gchar *current_search;
1579 ModestMsgViewWindowPrivate *priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (obj);
1581 if (modest_mime_part_view_is_empty (MODEST_MIME_PART_VIEW (priv->msg_view))) {
1582 modest_platform_system_banner (NULL, NULL, _("mail_ib_nothing_to_find"));
1586 current_search = modest_isearch_toolbar_get_search (MODEST_ISEARCH_TOOLBAR (widget));
1588 if ((current_search == NULL) || (strcmp (current_search, "") == 0)) {
1589 modest_platform_system_banner (NULL, NULL, _CS("ecdg_ib_find_rep_enter_text"));
1593 if ((priv->last_search == NULL) || (strcmp (priv->last_search, current_search) != 0)) {
1595 g_free (priv->last_search);
1596 priv->last_search = g_strdup (current_search);
1597 result = modest_isearch_view_search (MODEST_ISEARCH_VIEW (priv->msg_view),
1600 modest_platform_system_banner (NULL, NULL,
1601 _HL("ckct_ib_find_no_matches"));
1602 g_free (priv->last_search);
1603 priv->last_search = NULL;
1605 modest_isearch_toolbar_highlight_entry (MODEST_ISEARCH_TOOLBAR (priv->isearch_toolbar), TRUE);
1608 if (!modest_isearch_view_search_next (MODEST_ISEARCH_VIEW (priv->msg_view))) {
1609 modest_platform_system_banner (NULL, NULL,
1610 _HL("ckct_ib_find_search_complete"));
1611 g_free (priv->last_search);
1612 priv->last_search = NULL;
1614 modest_isearch_toolbar_highlight_entry (MODEST_ISEARCH_TOOLBAR (priv->isearch_toolbar), TRUE);
1621 modest_msg_view_window_set_zoom (ModestWindow *window,
1624 ModestMsgViewWindowPrivate *priv;
1625 ModestWindowPrivate *parent_priv;
1627 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window));
1629 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1630 parent_priv = MODEST_WINDOW_GET_PRIVATE (window);
1631 modest_zoomable_set_zoom (MODEST_ZOOMABLE (priv->msg_view), zoom);
1636 modest_msg_view_window_get_zoom (ModestWindow *window)
1638 ModestMsgViewWindowPrivate *priv;
1640 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), 1.0);
1642 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1643 return modest_zoomable_get_zoom (MODEST_ZOOMABLE (priv->msg_view));
1647 modest_msg_view_window_zoom_plus (ModestWindow *window)
1650 ModestMsgViewWindowPrivate *priv;
1654 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), 1.0);
1655 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1657 zoom_level = modest_zoomable_get_zoom (MODEST_ZOOMABLE (priv->msg_view));
1659 if (zoom_level >= 2.0) {
1660 modest_platform_system_banner (NULL, NULL,
1661 _CS("ckct_ib_max_zoom_level_reached"));
1663 } else if (zoom_level >= 1.5) {
1665 } else if (zoom_level >= 1.2) {
1667 } else if (zoom_level >= 1.0) {
1669 } else if (zoom_level >= 0.8) {
1671 } else if (zoom_level >= 0.5) {
1677 /* set zoom level */
1678 int_zoom = (gint) rint (zoom_level*100.0+0.1);
1679 banner_text = g_strdup_printf (_HL("wdgt_ib_zoom"), int_zoom);
1680 modest_platform_information_banner (GTK_WIDGET (window), NULL, banner_text);
1681 g_free (banner_text);
1682 modest_zoomable_set_zoom (MODEST_ZOOMABLE (priv->msg_view), zoom_level);
1688 modest_msg_view_window_zoom_minus (ModestWindow *window)
1691 ModestMsgViewWindowPrivate *priv;
1695 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), 1.0);
1696 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1698 zoom_level = modest_zoomable_get_zoom (MODEST_ZOOMABLE (priv->msg_view));
1700 if (zoom_level <= 0.5) {
1701 modest_platform_system_banner (NULL, NULL,
1702 _CS("ckct_ib_min_zoom_level_reached"));
1704 } else if (zoom_level <= 0.8) {
1706 } else if (zoom_level <= 1.0) {
1708 } else if (zoom_level <= 1.2) {
1710 } else if (zoom_level <= 1.5) {
1712 } else if (zoom_level <= 2.0) {
1718 /* set zoom level */
1719 int_zoom = (gint) rint (zoom_level*100.0+0.1);
1720 banner_text = g_strdup_printf (_HL("wdgt_ib_zoom"), int_zoom);
1721 modest_platform_information_banner (GTK_WIDGET (window), NULL, banner_text);
1722 g_free (banner_text);
1723 modest_zoomable_set_zoom (MODEST_ZOOMABLE (priv->msg_view), zoom_level);
1729 modest_msg_view_window_key_event (GtkWidget *window,
1735 focus = gtk_window_get_focus (GTK_WINDOW (window));
1737 /* for the isearch toolbar case */
1738 if (focus && GTK_IS_ENTRY (focus)) {
1739 if (event->keyval == GDK_BackSpace) {
1741 copy = gdk_event_copy ((GdkEvent *) event);
1742 gtk_widget_event (focus, copy);
1743 gdk_event_free (copy);
1753 modest_msg_view_window_last_message_selected (ModestMsgViewWindow *window)
1756 ModestMsgViewWindowPrivate *priv;
1757 GtkTreeIter tmp_iter;
1758 gboolean is_last_selected;
1760 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), TRUE);
1761 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1763 /*if no model (so no rows at all), then virtually we are the last*/
1764 if (!priv->header_model || !priv->row_reference)
1767 if (!gtk_tree_row_reference_valid (priv->row_reference))
1770 path = gtk_tree_row_reference_get_path (priv->row_reference);
1774 is_last_selected = TRUE;
1775 while (is_last_selected) {
1777 gtk_tree_path_next (path);
1778 if (!gtk_tree_model_get_iter (priv->header_model, &tmp_iter, path))
1780 gtk_tree_model_get (priv->header_model, &tmp_iter,
1781 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
1784 if (msg_is_visible (header, priv->is_outbox))
1785 is_last_selected = FALSE;
1786 g_object_unref(G_OBJECT(header));
1789 gtk_tree_path_free (path);
1790 return is_last_selected;
1794 modest_msg_view_window_has_headers_model (ModestMsgViewWindow *window)
1796 ModestMsgViewWindowPrivate *priv;
1798 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), TRUE);
1799 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1801 return priv->header_model != NULL;
1805 modest_msg_view_window_is_search_result (ModestMsgViewWindow *window)
1807 ModestMsgViewWindowPrivate *priv;
1809 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), TRUE);
1810 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1812 return priv->is_search_result;
1816 msg_is_visible (TnyHeader *header, gboolean check_outbox)
1818 if ((tny_header_get_flags(header) & TNY_HEADER_FLAG_DELETED))
1820 if (!check_outbox) {
1823 ModestTnySendQueueStatus status;
1824 status = modest_tny_all_send_queues_get_msg_status (header);
1825 return ((status != MODEST_TNY_SEND_QUEUE_FAILED) &&
1826 (status != MODEST_TNY_SEND_QUEUE_SENDING));
1831 modest_msg_view_window_first_message_selected (ModestMsgViewWindow *window)
1834 ModestMsgViewWindowPrivate *priv;
1835 gboolean is_first_selected;
1836 GtkTreeIter tmp_iter;
1838 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), TRUE);
1839 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1841 /*if no model (so no rows at all), then virtually we are the first*/
1842 if (!priv->header_model || !priv->row_reference)
1845 if (!gtk_tree_row_reference_valid (priv->row_reference))
1848 path = gtk_tree_row_reference_get_path (priv->row_reference);
1852 is_first_selected = TRUE;
1853 while (is_first_selected) {
1855 if(!gtk_tree_path_prev (path))
1857 /* Here the 'if' is needless for logic, but let make sure
1858 * iter is valid for gtk_tree_model_get. */
1859 if (!gtk_tree_model_get_iter (priv->header_model, &tmp_iter, path))
1861 gtk_tree_model_get (priv->header_model, &tmp_iter,
1862 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
1865 if (msg_is_visible (header, priv->is_outbox))
1866 is_first_selected = FALSE;
1867 g_object_unref(G_OBJECT(header));
1870 gtk_tree_path_free (path);
1871 return is_first_selected;
1878 GtkTreeRowReference *row_reference;
1882 message_reader_performer (gboolean canceled,
1884 GtkWindow *parent_window,
1885 TnyAccount *account,
1888 ModestMailOperation *mail_op = NULL;
1889 MsgReaderInfo *info;
1891 info = (MsgReaderInfo *) user_data;
1892 if (canceled || err) {
1893 update_window_title (MODEST_MSG_VIEW_WINDOW (parent_window));
1897 /* Register the header - it'll be unregistered in the callback */
1899 modest_window_mgr_register_header (modest_runtime_get_window_mgr (), info->header, NULL);
1901 /* New mail operation */
1902 mail_op = modest_mail_operation_new_with_error_handling (G_OBJECT(parent_window),
1903 modest_ui_actions_disk_operations_error_handler,
1906 modest_mail_operation_queue_add (modest_runtime_get_mail_operation_queue (), mail_op);
1908 modest_mail_operation_get_msg (mail_op, info->header, TRUE, view_msg_cb, info->row_reference);
1910 modest_mail_operation_find_msg (mail_op, info->folder, info->msg_uid, TRUE, view_msg_cb, NULL);
1911 g_object_unref (mail_op);
1913 /* Update dimming rules */
1914 modest_ui_actions_check_toolbar_dimming_rules (MODEST_WINDOW (parent_window));
1915 modest_ui_actions_check_menu_dimming_rules (MODEST_WINDOW (parent_window));
1918 /* Frees. The row_reference will be freed by the view_msg_cb callback */
1919 g_free (info->msg_uid);
1921 g_object_unref (info->folder);
1923 g_object_unref (info->header);
1924 g_slice_free (MsgReaderInfo, info);
1929 * Reads the message whose summary item is @header. It takes care of
1930 * several things, among others:
1932 * If the message was not previously downloaded then ask the user
1933 * before downloading. If there is no connection launch the connection
1934 * dialog. Update toolbar dimming rules.
1936 * Returns: TRUE if the mail operation was started, otherwise if the
1937 * user do not want to download the message, or if the user do not
1938 * want to connect, then the operation is not issued
1941 message_reader (ModestMsgViewWindow *window,
1942 ModestMsgViewWindowPrivate *priv,
1944 const gchar *msg_uid,
1946 GtkTreeRowReference *row_reference)
1948 ModestWindowMgr *mgr;
1949 TnyAccount *account = NULL;
1950 MsgReaderInfo *info;
1952 /* We set the header from model while we're loading */
1953 tny_header_view_set_header (TNY_HEADER_VIEW (priv->msg_view), header);
1954 modest_window_set_title (MODEST_WINDOW (window), _CS("ckdg_pb_updating"));
1960 g_object_ref (folder);
1962 mgr = modest_runtime_get_window_mgr ();
1963 /* Msg download completed */
1964 if (!header || !(tny_header_get_flags (header) & TNY_HEADER_FLAG_CACHED)) {
1966 /* Ask the user if he wants to download the message if
1968 if (!tny_device_is_online (modest_runtime_get_device())) {
1969 GtkResponseType response;
1971 response = modest_platform_run_confirmation_dialog (GTK_WINDOW (window),
1972 _("mcen_nc_get_msg"));
1973 if (response == GTK_RESPONSE_CANCEL) {
1974 update_window_title (window);
1979 folder = tny_header_get_folder (header);
1981 info = g_slice_new (MsgReaderInfo);
1982 info->msg_uid = g_strdup (msg_uid);
1984 info->header = g_object_ref (header);
1986 info->header = NULL;
1988 info->folder = g_object_ref (folder);
1990 info->folder = NULL;
1991 if (row_reference) {
1992 info->row_reference = gtk_tree_row_reference_copy (row_reference);
1994 info->row_reference = NULL;
1997 /* Offer the connection dialog if necessary */
1998 modest_platform_connect_if_remote_and_perform ((GtkWindow *) window,
2000 TNY_FOLDER_STORE (folder),
2001 message_reader_performer,
2004 g_object_unref (folder);
2010 folder = tny_header_get_folder (header);
2013 account = tny_folder_get_account (folder);
2015 info = g_slice_new (MsgReaderInfo);
2016 info->msg_uid = g_strdup (msg_uid);
2018 info->folder = g_object_ref (folder);
2020 info->folder = NULL;
2022 info->header = g_object_ref (header);
2024 info->header = NULL;
2026 info->row_reference = gtk_tree_row_reference_copy (row_reference);
2028 info->row_reference = NULL;
2030 message_reader_performer (FALSE, NULL, (GtkWindow *) window, account, info);
2032 g_object_unref (account);
2034 g_object_unref (folder);
2040 modest_msg_view_window_select_next_message (ModestMsgViewWindow *window)
2042 ModestMsgViewWindowPrivate *priv;
2043 GtkTreePath *path= NULL;
2044 GtkTreeIter tmp_iter;
2046 gboolean retval = TRUE;
2047 GtkTreeRowReference *row_reference = NULL;
2049 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), FALSE);
2050 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
2052 if (!priv->row_reference)
2055 /* Update the next row reference if it's not valid. This could
2056 happen if for example the header which it was pointing to,
2057 was deleted. The best place to do it is in the row-deleted
2058 handler but the tinymail model do not work like the glib
2059 tree models and reports the deletion when the row is still
2061 if (!gtk_tree_row_reference_valid (priv->next_row_reference)) {
2062 if (priv->next_row_reference) {
2063 gtk_tree_row_reference_free (priv->next_row_reference);
2065 if (gtk_tree_row_reference_valid (priv->row_reference)) {
2066 priv->next_row_reference = gtk_tree_row_reference_copy (priv->row_reference);
2067 select_next_valid_row (priv->header_model, &(priv->next_row_reference), FALSE, priv->is_outbox);
2069 priv->next_row_reference = NULL;
2072 if (priv->next_row_reference)
2073 path = gtk_tree_row_reference_get_path (priv->next_row_reference);
2077 row_reference = gtk_tree_row_reference_copy (priv->next_row_reference);
2079 gtk_tree_model_get_iter (priv->header_model,
2082 gtk_tree_path_free (path);
2084 gtk_tree_model_get (priv->header_model, &tmp_iter,
2085 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
2088 /* Read the message & show it */
2089 if (!message_reader (window, priv, header, NULL, NULL, row_reference)) {
2092 gtk_tree_row_reference_free (row_reference);
2095 g_object_unref (header);
2101 modest_msg_view_window_select_previous_message (ModestMsgViewWindow *window)
2103 ModestMsgViewWindowPrivate *priv = NULL;
2105 gboolean finished = FALSE;
2106 gboolean retval = FALSE;
2108 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), FALSE);
2109 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
2111 if (priv->row_reference && !gtk_tree_row_reference_valid (priv->row_reference)) {
2112 gtk_tree_row_reference_free (priv->row_reference);
2113 priv->row_reference = NULL;
2116 /* Return inmediatly if there is no header model */
2117 if (!priv->header_model || !priv->row_reference)
2120 path = gtk_tree_row_reference_get_path (priv->row_reference);
2121 while (!finished && gtk_tree_path_prev (path)) {
2125 gtk_tree_model_get_iter (priv->header_model, &iter, path);
2126 gtk_tree_model_get (priv->header_model, &iter,
2127 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
2131 if (msg_is_visible (header, priv->is_outbox)) {
2132 GtkTreeRowReference *row_reference;
2133 row_reference = gtk_tree_row_reference_new (priv->header_model, path);
2134 /* Read the message & show it */
2135 retval = message_reader (window, priv, header, NULL, NULL, row_reference);
2136 gtk_tree_row_reference_free (row_reference);
2140 g_object_unref (header);
2144 gtk_tree_path_free (path);
2149 view_msg_cb (ModestMailOperation *mail_op,
2156 ModestMsgViewWindow *self = NULL;
2157 ModestMsgViewWindowPrivate *priv = NULL;
2158 GtkTreeRowReference *row_reference = NULL;
2160 /* Unregister the header (it was registered before creating the mail operation) */
2161 modest_window_mgr_unregister_header (modest_runtime_get_window_mgr (), header);
2163 row_reference = (GtkTreeRowReference *) user_data;
2166 gtk_tree_row_reference_free (row_reference);
2167 self = (ModestMsgViewWindow *) modest_mail_operation_get_source (mail_op);
2169 /* Restore window title */
2170 update_window_title (self);
2171 modest_ui_actions_on_close_window (NULL, MODEST_WINDOW (self));
2172 g_object_unref (self);
2177 /* If there was any error */
2178 if (!modest_ui_actions_msg_retrieval_check (mail_op, header, msg)) {
2180 gtk_tree_row_reference_free (row_reference);
2181 self = (ModestMsgViewWindow *) modest_mail_operation_get_source (mail_op);
2183 /* Restore window title */
2184 update_window_title (self);
2185 modest_ui_actions_on_close_window (NULL, MODEST_WINDOW (self));
2186 g_object_unref (self);
2191 /* Get the window */
2192 self = (ModestMsgViewWindow *) modest_mail_operation_get_source (mail_op);
2193 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self));
2194 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
2196 /* Update the row reference */
2197 if (priv->row_reference != NULL) {
2198 gtk_tree_row_reference_free (priv->row_reference);
2199 priv->row_reference = (row_reference && gtk_tree_row_reference_valid (row_reference))?gtk_tree_row_reference_copy (row_reference):NULL;
2200 if (priv->next_row_reference != NULL) {
2201 gtk_tree_row_reference_free (priv->next_row_reference);
2203 if (priv->row_reference) {
2204 priv->next_row_reference = gtk_tree_row_reference_copy (priv->row_reference);
2205 select_next_valid_row (priv->header_model, &(priv->next_row_reference), TRUE, priv->is_outbox);
2207 priv->next_row_reference = NULL;
2211 /* Mark header as read */
2212 if (!(tny_header_get_flags (header) & TNY_HEADER_FLAG_SEEN))
2213 tny_header_set_flag (header, TNY_HEADER_FLAG_SEEN);
2215 /* Set new message */
2216 if (priv->msg_view != NULL && TNY_IS_MSG_VIEW (priv->msg_view)) {
2217 tny_msg_view_set_msg (TNY_MSG_VIEW (priv->msg_view), msg);
2218 modest_msg_view_window_update_priority (self);
2219 update_window_title (MODEST_MSG_VIEW_WINDOW (self));
2220 update_branding (MODEST_MSG_VIEW_WINDOW (self));
2221 modest_msg_view_grab_focus (MODEST_MSG_VIEW (priv->msg_view));
2224 /* Set the new message uid of the window */
2225 if (priv->msg_uid) {
2226 g_free (priv->msg_uid);
2227 priv->msg_uid = modest_tny_folder_get_header_unique_id (header);
2230 /* Notify the observers */
2231 g_signal_emit (G_OBJECT (self), signals[MSG_CHANGED_SIGNAL],
2232 0, priv->header_model, priv->row_reference);
2234 /* Sync the flags if the message is not opened from a header
2235 model, i.e, if it's opened from a notification */
2236 if (!priv->header_model)
2240 g_object_unref (self);
2242 gtk_tree_row_reference_free (row_reference);
2246 modest_msg_view_window_get_folder_type (ModestMsgViewWindow *window)
2248 ModestMsgViewWindowPrivate *priv;
2250 TnyFolderType folder_type;
2252 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
2254 folder_type = TNY_FOLDER_TYPE_UNKNOWN;
2256 msg = tny_msg_view_get_msg (TNY_MSG_VIEW (priv->msg_view));
2260 folder = tny_msg_get_folder (msg);
2262 folder_type = modest_tny_folder_guess_folder_type (folder);
2263 g_object_unref (folder);
2265 g_object_unref (msg);
2273 modest_msg_view_window_update_priority (ModestMsgViewWindow *window)
2275 ModestMsgViewWindowPrivate *priv;
2276 TnyHeader *header = NULL;
2277 TnyHeaderFlags flags = 0;
2279 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
2281 if (priv->header_model && priv->row_reference && gtk_tree_row_reference_valid (priv->row_reference)) {
2283 GtkTreePath *path = NULL;
2285 path = gtk_tree_row_reference_get_path (priv->row_reference);
2286 g_return_if_fail (path != NULL);
2287 gtk_tree_model_get_iter (priv->header_model,
2289 gtk_tree_row_reference_get_path (priv->row_reference));
2291 gtk_tree_model_get (priv->header_model, &iter, TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
2293 gtk_tree_path_free (path);
2296 msg = tny_msg_view_get_msg (TNY_MSG_VIEW (priv->msg_view));
2298 header = tny_msg_get_header (msg);
2299 g_object_unref (msg);
2304 flags = tny_header_get_flags (header);
2305 g_object_unref(G_OBJECT(header));
2308 modest_msg_view_set_priority (MODEST_MSG_VIEW(priv->msg_view), flags);
2313 toolbar_resize (ModestMsgViewWindow *self)
2315 ModestMsgViewWindowPrivate *priv = NULL;
2316 ModestWindowPrivate *parent_priv = NULL;
2318 gint static_button_size;
2319 ModestWindowMgr *mgr;
2321 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self));
2322 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
2323 parent_priv = MODEST_WINDOW_GET_PRIVATE(self);
2325 mgr = modest_runtime_get_window_mgr ();
2326 static_button_size = modest_window_mgr_get_fullscreen_mode (mgr)?120:120;
2328 if (parent_priv->toolbar) {
2329 /* Set expandable and homogeneous tool buttons */
2330 widget = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/ToolbarMessageReply");
2331 gtk_tool_item_set_expand (GTK_TOOL_ITEM (widget), TRUE);
2332 gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (widget), TRUE);
2333 widget = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/ToolbarMessageReplyAll");
2334 gtk_tool_item_set_expand (GTK_TOOL_ITEM (widget), TRUE);
2335 gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (widget), TRUE);
2336 widget = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/ToolbarMessageForward");
2337 gtk_tool_item_set_expand (GTK_TOOL_ITEM (widget), TRUE);
2338 gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (widget), TRUE);
2339 widget = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/ToolbarDeleteMessage");
2340 gtk_tool_item_set_expand (GTK_TOOL_ITEM (widget), TRUE);
2341 gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (widget), TRUE);
2342 widget = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/ToolbarDownloadExternalImages");
2343 gtk_tool_item_set_expand (GTK_TOOL_ITEM (widget), TRUE);
2344 gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (widget), TRUE);
2345 gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (priv->next_toolitem), TRUE);
2346 gtk_tool_item_set_expand (GTK_TOOL_ITEM (priv->next_toolitem), TRUE);
2347 gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (priv->prev_toolitem), TRUE);
2348 gtk_tool_item_set_expand (GTK_TOOL_ITEM (priv->prev_toolitem), TRUE);
2353 modest_msg_view_window_show_toolbar (ModestWindow *self,
2354 gboolean show_toolbar)
2356 ModestMsgViewWindowPrivate *priv = NULL;
2357 ModestWindowPrivate *parent_priv;
2359 parent_priv = MODEST_WINDOW_GET_PRIVATE(self);
2360 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(self);
2362 /* Set optimized view status */
2363 priv->optimized_view = !show_toolbar;
2365 if (!parent_priv->toolbar) {
2366 parent_priv->toolbar = gtk_ui_manager_get_widget (parent_priv->ui_manager,
2369 /* We don't use HILDON_ICON_SIZE_FINGER in order to avoid the ifdef here */
2370 gtk_toolbar_set_icon_size (GTK_TOOLBAR (parent_priv->toolbar), gtk_icon_size_from_name ("hildon-finger"));
2371 gtk_widget_set_no_show_all (parent_priv->toolbar, TRUE);
2373 priv->next_toolitem = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/ToolbarMessageNext");
2374 priv->prev_toolitem = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/ToolbarMessageBack");
2375 toolbar_resize (MODEST_MSG_VIEW_WINDOW (self));
2377 modest_window_add_toolbar (MODEST_WINDOW (self),
2378 GTK_TOOLBAR (parent_priv->toolbar));
2383 /* Quick hack: this prevents toolbar icons "dance" when progress bar show status is changed */
2384 /* TODO: resize mode migth be GTK_RESIZE_QUEUE, in order to avoid unneccesary shows */
2385 gtk_container_set_resize_mode (GTK_CONTAINER(parent_priv->toolbar), GTK_RESIZE_IMMEDIATE);
2387 gtk_widget_show (GTK_WIDGET (parent_priv->toolbar));
2388 if (modest_msg_view_window_transfer_mode_enabled (MODEST_MSG_VIEW_WINDOW (self)))
2389 set_progress_hint (MODEST_MSG_VIEW_WINDOW (self), TRUE);
2391 set_progress_hint (MODEST_MSG_VIEW_WINDOW (self), FALSE);
2394 gtk_widget_set_no_show_all (parent_priv->toolbar, TRUE);
2395 gtk_widget_hide (GTK_WIDGET (parent_priv->toolbar));
2400 modest_msg_view_window_clipboard_owner_change (GtkClipboard *clipboard,
2402 ModestMsgViewWindow *window)
2404 if (!GTK_WIDGET_VISIBLE (window))
2407 modest_window_check_dimming_rules_group (MODEST_WINDOW (window), MODEST_DIMMING_RULES_CLIPBOARD);
2411 modest_msg_view_window_transfer_mode_enabled (ModestMsgViewWindow *self)
2413 ModestMsgViewWindowPrivate *priv;
2415 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self), FALSE);
2416 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(self);
2418 return priv->progress_hint;
2422 observers_empty (ModestMsgViewWindow *self)
2425 ModestMsgViewWindowPrivate *priv;
2426 gboolean is_empty = TRUE;
2427 guint pending_ops = 0;
2429 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(self);
2430 tmp = priv->progress_widgets;
2432 /* Check all observers */
2433 while (tmp && is_empty) {
2434 pending_ops = modest_progress_object_num_pending_operations (MODEST_PROGRESS_OBJECT(tmp->data));
2435 is_empty = pending_ops == 0;
2437 tmp = g_slist_next(tmp);
2444 on_account_removed (TnyAccountStore *account_store,
2445 TnyAccount *account,
2448 /* Do nothing if it's a transport account, because we only
2449 show the messages of a store account */
2450 if (tny_account_get_account_type(account) == TNY_ACCOUNT_TYPE_STORE) {
2451 const gchar *parent_acc = NULL;
2452 const gchar *our_acc = NULL;
2454 our_acc = modest_window_get_active_account (MODEST_WINDOW (user_data));
2455 parent_acc = modest_tny_account_get_parent_modest_account_name_for_server_account (account);
2457 /* Close this window if I'm showing a message of the removed account */
2458 if (our_acc && parent_acc && strcmp (parent_acc, our_acc) == 0)
2459 modest_ui_actions_on_close_window (NULL, MODEST_WINDOW (user_data));
2464 on_mail_operation_started (ModestMailOperation *mail_op,
2467 ModestMsgViewWindow *self;
2468 ModestMailOperationTypeOperation op_type;
2470 ModestMsgViewWindowPrivate *priv;
2471 GObject *source = NULL;
2473 self = MODEST_MSG_VIEW_WINDOW (user_data);
2474 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
2475 op_type = modest_mail_operation_get_type_operation (mail_op);
2476 tmp = priv->progress_widgets;
2477 source = modest_mail_operation_get_source(mail_op);
2478 if (G_OBJECT (self) == source) {
2479 if (op_type == MODEST_MAIL_OPERATION_TYPE_RECEIVE ||
2480 op_type == MODEST_MAIL_OPERATION_TYPE_OPEN ||
2481 op_type == MODEST_MAIL_OPERATION_TYPE_DELETE) {
2482 set_progress_hint (self, TRUE);
2484 modest_progress_object_add_operation (
2485 MODEST_PROGRESS_OBJECT (tmp->data),
2487 tmp = g_slist_next (tmp);
2491 g_object_unref (source);
2493 /* Update dimming rules */
2494 check_dimming_rules_after_change (self);
2498 on_mail_operation_finished (ModestMailOperation *mail_op,
2501 ModestMsgViewWindow *self;
2502 ModestMailOperationTypeOperation op_type;
2504 ModestMsgViewWindowPrivate *priv;
2506 self = MODEST_MSG_VIEW_WINDOW (user_data);
2507 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
2508 op_type = modest_mail_operation_get_type_operation (mail_op);
2509 tmp = priv->progress_widgets;
2511 if (op_type == MODEST_MAIL_OPERATION_TYPE_RECEIVE ||
2512 op_type == MODEST_MAIL_OPERATION_TYPE_OPEN ||
2513 op_type == MODEST_MAIL_OPERATION_TYPE_DELETE) {
2515 modest_progress_object_remove_operation (MODEST_PROGRESS_OBJECT (tmp->data),
2517 tmp = g_slist_next (tmp);
2520 /* If no more operations are being observed, NORMAL mode is enabled again */
2521 if (observers_empty (self)) {
2522 set_progress_hint (self, FALSE);
2526 /* Update dimming rules. We have to do this right here
2527 and not in view_msg_cb because at that point the
2528 transfer mode is still enabled so the dimming rule
2529 won't let the user delete the message that has been
2530 readed for example */
2531 check_dimming_rules_after_change (self);
2535 on_queue_changed (ModestMailOperationQueue *queue,
2536 ModestMailOperation *mail_op,
2537 ModestMailOperationQueueNotification type,
2538 ModestMsgViewWindow *self)
2540 ModestMsgViewWindowPrivate *priv;
2542 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
2544 /* If this operations was created by another window, do nothing */
2545 if (!modest_mail_operation_is_mine (mail_op, G_OBJECT(self)))
2548 if (type == MODEST_MAIL_OPERATION_QUEUE_OPERATION_ADDED) {
2549 priv->sighandlers = modest_signal_mgr_connect (priv->sighandlers,
2551 "operation-started",
2552 G_CALLBACK (on_mail_operation_started),
2554 priv->sighandlers = modest_signal_mgr_connect (priv->sighandlers,
2556 "operation-finished",
2557 G_CALLBACK (on_mail_operation_finished),
2559 } else if (type == MODEST_MAIL_OPERATION_QUEUE_OPERATION_REMOVED) {
2560 priv->sighandlers = modest_signal_mgr_disconnect (priv->sighandlers,
2562 "operation-started");
2563 priv->sighandlers = modest_signal_mgr_disconnect (priv->sighandlers,
2565 "operation-finished");
2570 modest_msg_view_window_get_attachments (ModestMsgViewWindow *win)
2572 ModestMsgViewWindowPrivate *priv;
2573 TnyList *selected_attachments = NULL;
2575 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (win), NULL);
2576 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (win);
2578 /* In Hildon 2.2 as there's no selection we assume we have all attachments selected */
2579 selected_attachments = modest_msg_view_get_attachments (MODEST_MSG_VIEW (priv->msg_view));
2581 return selected_attachments;
2585 ModestMsgViewWindow *self;
2587 gchar *attachment_uid;
2588 } DecodeAsyncHelper;
2591 on_decode_to_stream_async_handler (TnyMimePart *mime_part,
2597 DecodeAsyncHelper *helper = (DecodeAsyncHelper *) user_data;
2598 const gchar *content_type;
2600 if (cancelled || err) {
2603 if ((err->domain == TNY_ERROR_DOMAIN) &&
2604 (err->code == TNY_IO_ERROR_WRITE) &&
2605 (errno == ENOSPC)) {
2606 msg = g_strdup_printf (_KR("cerm_device_memory_full"), "");
2608 msg = g_strdup (_("mail_ib_file_operation_failed"));
2610 modest_platform_information_banner (NULL, NULL, msg);
2616 /* It could happen that the window was closed. So we
2617 assume it is a cancelation */
2618 if (!GTK_WIDGET_VISIBLE (helper->self))
2621 /* Remove the progress hint */
2622 set_progress_hint (helper->self, FALSE);
2624 content_type = tny_mime_part_get_content_type (mime_part);
2625 if (g_str_has_prefix (content_type, "message/rfc822")) {
2626 ModestWindowMgr *mgr;
2627 ModestWindow *msg_win = NULL;
2630 const gchar *mailbox;
2631 TnyStream *file_stream;
2634 fd = g_open (helper->file_path, O_RDONLY, 0644);
2636 file_stream = tny_fs_stream_new (fd);
2638 mgr = modest_runtime_get_window_mgr ();
2640 account = g_strdup (modest_window_get_active_account (MODEST_WINDOW (helper->self)));
2641 mailbox = modest_window_get_active_mailbox (MODEST_WINDOW (helper->self));
2644 account = modest_account_mgr_get_default_account (modest_runtime_get_account_mgr ());
2646 msg = tny_camel_msg_new ();
2647 tny_camel_msg_parse (msg, file_stream);
2648 msg_win = modest_msg_view_window_new_for_attachment (TNY_MSG (msg), account, mailbox, helper->attachment_uid);
2649 modest_window_set_zoom (MODEST_WINDOW (msg_win),
2650 modest_window_get_zoom (MODEST_WINDOW (helper->self)));
2651 if (modest_window_mgr_register_window (mgr, msg_win, MODEST_WINDOW (helper->self)))
2652 gtk_widget_show_all (GTK_WIDGET (msg_win));
2654 gtk_widget_destroy (GTK_WIDGET (msg_win));
2655 g_object_unref (msg);
2656 g_object_unref (file_stream);
2658 modest_platform_information_banner (NULL, NULL, _("mail_ib_file_operation_failed"));
2663 /* make the file read-only */
2664 g_chmod(helper->file_path, 0444);
2666 /* Activate the file */
2667 modest_platform_activate_file (helper->file_path, tny_mime_part_get_content_type (mime_part));
2672 g_object_unref (helper->self);
2673 g_free (helper->file_path);
2674 g_free (helper->attachment_uid);
2675 g_slice_free (DecodeAsyncHelper, helper);
2679 modest_msg_view_window_view_attachment (ModestMsgViewWindow *window,
2680 TnyMimePart *mime_part)
2682 ModestMsgViewWindowPrivate *priv;
2683 const gchar *msg_uid;
2684 gchar *attachment_uid = NULL;
2685 gint attachment_index = 0;
2686 TnyList *attachments;
2688 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window));
2689 g_return_if_fail (TNY_IS_MIME_PART (mime_part) || (mime_part == NULL));
2690 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
2692 msg_uid = modest_msg_view_window_get_message_uid (MODEST_MSG_VIEW_WINDOW (window));
2693 attachments = modest_msg_view_get_attachments (MODEST_MSG_VIEW (priv->msg_view));
2694 attachment_index = modest_list_index (attachments, (GObject *) mime_part);
2695 g_object_unref (attachments);
2697 if (msg_uid && attachment_index >= 0) {
2698 attachment_uid = g_strdup_printf ("%s/%d", msg_uid, attachment_index);
2701 if (mime_part == NULL) {
2702 gboolean error = FALSE;
2703 TnyList *selected_attachments = modest_msg_view_get_selected_attachments (MODEST_MSG_VIEW (priv->msg_view));
2704 if (selected_attachments == NULL || tny_list_get_length (selected_attachments) == 0) {
2706 } else if (tny_list_get_length (selected_attachments) > 1) {
2707 modest_platform_system_banner (NULL, NULL, _("mcen_ib_unable_to_display_more"));
2711 iter = tny_list_create_iterator (selected_attachments);
2712 mime_part = (TnyMimePart *) tny_iterator_get_current (iter);
2713 g_object_unref (iter);
2715 if (selected_attachments)
2716 g_object_unref (selected_attachments);
2721 g_object_ref (mime_part);
2724 if (tny_mime_part_is_purged (mime_part))
2727 if (!modest_tny_mime_part_is_msg (mime_part) && tny_mime_part_get_filename (mime_part)) {
2728 gchar *filepath = NULL;
2729 const gchar *att_filename = tny_mime_part_get_filename (mime_part);
2730 gboolean show_error_banner = FALSE;
2731 TnyFsStream *temp_stream = NULL;
2732 temp_stream = modest_utils_create_temp_stream (att_filename, attachment_uid,
2735 if (temp_stream != NULL) {
2736 ModestAccountMgr *mgr;
2737 DecodeAsyncHelper *helper;
2738 gboolean decode_in_provider;
2739 ModestProtocol *protocol;
2740 const gchar *account;
2742 /* Activate progress hint */
2743 set_progress_hint (window, TRUE);
2745 helper = g_slice_new0 (DecodeAsyncHelper);
2746 helper->self = g_object_ref (window);
2747 helper->file_path = g_strdup (filepath);
2748 helper->attachment_uid = g_strdup (attachment_uid);
2750 decode_in_provider = FALSE;
2751 mgr = modest_runtime_get_account_mgr ();
2752 account = modest_window_get_active_account (MODEST_WINDOW (window));
2753 if (modest_account_mgr_account_is_multimailbox (mgr, account, &protocol)) {
2754 if (MODEST_IS_ACCOUNT_PROTOCOL (protocol)) {
2756 uri = g_strconcat ("file://", filepath, NULL);
2757 decode_in_provider =
2758 modest_account_protocol_decode_part_to_stream_async (
2759 MODEST_ACCOUNT_PROTOCOL (protocol),
2762 TNY_STREAM (temp_stream),
2763 on_decode_to_stream_async_handler,
2770 if (!decode_in_provider)
2771 tny_mime_part_decode_to_stream_async (mime_part, TNY_STREAM (temp_stream),
2772 on_decode_to_stream_async_handler,
2775 g_object_unref (temp_stream);
2776 /* NOTE: files in the temporary area will be automatically
2777 * cleaned after some time if they are no longer in use */
2780 const gchar *content_type;
2781 /* the file may already exist but it isn't writable,
2782 * let's try to open it anyway */
2783 content_type = tny_mime_part_get_content_type (mime_part);
2784 modest_platform_activate_file (filepath, content_type);
2786 g_warning ("%s: modest_utils_create_temp_stream failed", __FUNCTION__);
2787 show_error_banner = TRUE;
2792 if (show_error_banner)
2793 modest_platform_information_banner (NULL, NULL, _("mail_ib_file_operation_failed"));
2794 } else if (!modest_tny_mime_part_is_msg (mime_part)) {
2795 ModestWindowMgr *mgr;
2796 ModestWindow *msg_win = NULL;
2797 TnyMsg *current_msg;
2801 current_msg = modest_msg_view_window_get_message (MODEST_MSG_VIEW_WINDOW (window));
2802 mgr = modest_runtime_get_window_mgr ();
2803 header = tny_msg_get_header (TNY_MSG (current_msg));
2804 found = modest_window_mgr_find_registered_message_uid (mgr,
2809 g_debug ("window for this body is already being created");
2812 /* it's not found, so create a new window for it */
2813 modest_window_mgr_register_header (mgr, header, attachment_uid); /* register the uid before building the window */
2814 gchar *account = g_strdup (modest_window_get_active_account (MODEST_WINDOW (window)));
2815 const gchar *mailbox = modest_window_get_active_mailbox (MODEST_WINDOW (window));
2817 account = modest_account_mgr_get_default_account (modest_runtime_get_account_mgr ());
2819 msg_win = modest_msg_view_window_new_with_other_body (TNY_MSG (current_msg), TNY_MIME_PART (mime_part),
2820 account, mailbox, attachment_uid);
2822 modest_window_set_zoom (MODEST_WINDOW (msg_win),
2823 modest_window_get_zoom (MODEST_WINDOW (window)));
2824 if (modest_window_mgr_register_window (mgr, msg_win, MODEST_WINDOW (window)))
2825 gtk_widget_show_all (GTK_WIDGET (msg_win));
2827 gtk_widget_destroy (GTK_WIDGET (msg_win));
2829 g_object_unref (current_msg);
2831 /* message attachment */
2832 TnyHeader *header = NULL;
2833 ModestWindowMgr *mgr;
2834 ModestWindow *msg_win = NULL;
2837 header = tny_msg_get_header (TNY_MSG (mime_part));
2838 mgr = modest_runtime_get_window_mgr ();
2839 found = modest_window_mgr_find_registered_header (mgr, header, &msg_win);
2842 /* if it's found, but there is no msg_win, it's probably in the process of being created;
2843 * thus, we don't do anything */
2844 g_debug ("window for is already being created");
2846 /* it's not found, so create a new window for it */
2847 modest_window_mgr_register_header (mgr, header, attachment_uid); /* register the uid before building the window */
2848 gchar *account = g_strdup (modest_window_get_active_account (MODEST_WINDOW (window)));
2849 const gchar *mailbox = modest_window_get_active_mailbox (MODEST_WINDOW (window));
2851 account = modest_account_mgr_get_default_account (modest_runtime_get_account_mgr ());
2852 msg_win = modest_msg_view_window_new_for_attachment (TNY_MSG (mime_part), account,
2853 mailbox, attachment_uid);
2854 modest_window_set_zoom (MODEST_WINDOW (msg_win),
2855 modest_window_get_zoom (MODEST_WINDOW (window)));
2856 if (modest_window_mgr_register_window (mgr, msg_win, MODEST_WINDOW (window)))
2857 gtk_widget_show_all (GTK_WIDGET (msg_win));
2859 gtk_widget_destroy (GTK_WIDGET (msg_win));
2865 g_free (attachment_uid);
2867 g_object_unref (mime_part);
2879 GnomeVFSResult result;
2881 ModestMsgViewWindow *window;
2884 static void save_mime_part_info_free (SaveMimePartInfo *info, gboolean with_struct);
2885 static gboolean idle_save_mime_part_show_result (SaveMimePartInfo *info);
2886 static gpointer save_mime_part_to_file (SaveMimePartInfo *info);
2887 static void save_mime_parts_to_file_with_checks (GtkWindow *parent, SaveMimePartInfo *info);
2890 save_mime_part_info_free (SaveMimePartInfo *info, gboolean with_struct)
2894 for (node = info->pairs; node != NULL; node = g_list_next (node)) {
2895 SaveMimePartPair *pair = (SaveMimePartPair *) node->data;
2896 g_free (pair->filename);
2897 g_object_unref (pair->part);
2898 g_slice_free (SaveMimePartPair, pair);
2900 g_list_free (info->pairs);
2903 g_object_unref (info->window);
2904 info->window = NULL;
2906 g_slice_free (SaveMimePartInfo, info);
2911 idle_save_mime_part_show_result (SaveMimePartInfo *info)
2913 /* This is a GDK lock because we are an idle callback and
2914 * modest_platform_system_banner is or does Gtk+ code */
2916 gdk_threads_enter (); /* CHECKED */
2917 if (info->result == GNOME_VFS_OK) {
2918 modest_platform_system_banner (NULL, NULL, _CS("sfil_ib_saved"));
2919 } else if (info->result == GNOME_VFS_ERROR_NO_SPACE) {
2922 /* Check if the uri belongs to the external mmc */
2923 if (g_str_has_prefix (info->uri, g_getenv (MODEST_MMC1_VOLUMEPATH_ENV)))
2924 msg = g_strdup_printf (_KR("cerm_device_memory_full"), "");
2926 msg = g_strdup (_KR("cerm_memory_card_full"));
2927 modest_platform_information_banner (NULL, NULL, msg);
2930 modest_platform_system_banner (NULL, NULL, _("mail_ib_file_operation_failed"));
2932 save_mime_part_info_free (info, FALSE);
2933 gdk_threads_leave (); /* CHECKED */
2939 save_mime_part_to_file (SaveMimePartInfo *info)
2941 GnomeVFSHandle *handle;
2943 SaveMimePartPair *pair = (SaveMimePartPair *) info->pairs->data;
2945 info->result = gnome_vfs_create (&handle, pair->filename, GNOME_VFS_OPEN_WRITE, FALSE, 0644);
2946 if (info->result == GNOME_VFS_OK) {
2947 GError *error = NULL;
2948 gboolean decode_in_provider;
2950 ModestAccountMgr *mgr;
2951 const gchar *account;
2952 ModestProtocol *protocol = NULL;
2954 stream = tny_vfs_stream_new (handle);
2956 decode_in_provider = FALSE;
2957 mgr = modest_runtime_get_account_mgr ();
2958 account = modest_window_get_active_account (MODEST_WINDOW (info->window));
2959 if (modest_account_mgr_account_is_multimailbox (mgr, account, &protocol)) {
2960 if (MODEST_IS_ACCOUNT_PROTOCOL (protocol)) {
2961 decode_in_provider =
2962 modest_account_protocol_decode_part_to_stream (
2963 MODEST_ACCOUNT_PROTOCOL (protocol),
2971 if (!decode_in_provider)
2972 written = tny_mime_part_decode_to_stream (pair->part, stream, &error);
2975 g_warning ("modest: could not save attachment %s: %d (%s)\n", pair->filename, error?error->code:-1, error?error->message:"Unknown error");
2977 if ((error->domain == TNY_ERROR_DOMAIN) &&
2978 (error->code == TNY_IO_ERROR_WRITE) &&
2979 (errno == ENOSPC)) {
2980 info->result = GNOME_VFS_ERROR_NO_SPACE;
2982 info->result = GNOME_VFS_ERROR_IO;
2985 g_object_unref (G_OBJECT (stream));
2987 g_warning ("Could not create save attachment %s: %s\n",
2988 pair->filename, gnome_vfs_result_to_string (info->result));
2991 /* Go on saving remaining files */
2992 info->pairs = g_list_remove_link (info->pairs, info->pairs);
2993 if (info->pairs != NULL) {
2994 save_mime_part_to_file (info);
2996 g_idle_add ((GSourceFunc) idle_save_mime_part_show_result, info);
3003 save_mime_parts_to_file_with_checks (GtkWindow *parent,
3004 SaveMimePartInfo *info)
3006 gboolean is_ok = TRUE;
3007 gint replaced_files = 0;
3008 const GList *files = info->pairs;
3009 const GList *iter, *to_replace = NULL;
3011 for (iter = files; (iter != NULL) && (replaced_files < 2); iter = g_list_next(iter)) {
3012 SaveMimePartPair *pair = iter->data;
3013 gchar *unescaped = g_uri_unescape_string (pair->filename, NULL);
3015 if (modest_utils_file_exists (unescaped)) {
3017 if (replaced_files == 1)
3022 if (replaced_files) {
3025 if (replaced_files == 1) {
3026 SaveMimePartPair *pair = to_replace->data;
3027 const gchar *basename = strrchr (pair->filename, G_DIR_SEPARATOR) + 1;
3028 gchar *escaped_basename, *message;
3030 escaped_basename = g_uri_unescape_string (basename, NULL);
3031 message = g_strdup_printf ("%s\n%s",
3032 _FM("docm_nc_replace_file"),
3033 (escaped_basename) ? escaped_basename : "");
3034 response = modest_platform_run_confirmation_dialog (parent, message);
3036 g_free (escaped_basename);
3038 response = modest_platform_run_confirmation_dialog (parent,
3039 _FM("docm_nc_replace_multiple"));
3041 if (response != GTK_RESPONSE_OK)
3046 save_mime_part_info_free (info, TRUE);
3048 g_thread_create ((GThreadFunc)save_mime_part_to_file, info, FALSE, NULL);
3053 typedef struct _SaveAttachmentsInfo {
3054 TnyList *attachments_list;
3055 ModestMsgViewWindow *window;
3056 } SaveAttachmentsInfo;
3059 save_attachments_response (GtkDialog *dialog,
3063 TnyList *mime_parts;
3065 GList *files_to_save = NULL;
3066 gchar *current_folder;
3067 SaveAttachmentsInfo *sa_info = (SaveAttachmentsInfo *) user_data;
3069 mime_parts = TNY_LIST (sa_info->attachments_list);
3071 if (arg1 != GTK_RESPONSE_OK)
3074 chooser_uri = gtk_file_chooser_get_uri (GTK_FILE_CHOOSER (dialog));
3075 current_folder = gtk_file_chooser_get_current_folder_uri (GTK_FILE_CHOOSER (dialog));
3076 if (current_folder && *current_folder != '\0') {
3078 modest_conf_set_string (modest_runtime_get_conf (), MODEST_CONF_LATEST_SAVE_ATTACHMENT_PATH,
3079 current_folder,&err);
3081 g_debug ("Error storing latest used folder: %s", err->message);
3085 g_free (current_folder);
3087 if (!modest_utils_folder_writable (chooser_uri)) {
3088 const gchar *err_msg;
3090 #ifdef MODEST_PLATFORM_MAEMO
3091 if (modest_maemo_utils_in_usb_mode ()) {
3092 err_msg = dgettext ("hildon-status-bar-usb", "usbh_ib_mmc_usb_connected");
3094 err_msg = _FM("sfil_ib_readonly_location");
3097 err_msg = _FM("sfil_ib_readonly_location");
3099 modest_platform_system_banner (NULL, NULL, err_msg);
3103 iter = tny_list_create_iterator (mime_parts);
3104 while (!tny_iterator_is_done (iter)) {
3105 TnyMimePart *mime_part = (TnyMimePart *) tny_iterator_get_current (iter);
3107 if ((modest_tny_mime_part_is_attachment_for_modest (mime_part)) &&
3108 !tny_mime_part_is_purged (mime_part) &&
3109 (tny_mime_part_get_filename (mime_part) != NULL)) {
3110 SaveMimePartPair *pair;
3112 pair = g_slice_new0 (SaveMimePartPair);
3114 if (tny_list_get_length (mime_parts) > 1) {
3116 gnome_vfs_escape_slashes (tny_mime_part_get_filename (mime_part));
3117 pair->filename = g_build_filename (chooser_uri, escaped, NULL);
3120 pair->filename = g_strdup (chooser_uri);
3122 pair->part = mime_part;
3123 files_to_save = g_list_prepend (files_to_save, pair);
3125 tny_iterator_next (iter);
3127 g_object_unref (iter);
3130 if (files_to_save != NULL) {
3131 SaveMimePartInfo *info = g_slice_new0 (SaveMimePartInfo);
3132 info->pairs = files_to_save;
3133 info->result = TRUE;
3134 info->uri = g_strdup (chooser_uri);
3135 info->window = g_object_ref (sa_info->window);
3136 save_mime_parts_to_file_with_checks ((GtkWindow *) dialog, info);
3138 g_free (chooser_uri);
3141 /* Free and close the dialog */
3142 g_object_unref (mime_parts);
3143 g_object_unref (sa_info->window);
3144 g_slice_free (SaveAttachmentsInfo, sa_info);
3145 gtk_widget_destroy (GTK_WIDGET (dialog));
3149 modest_msg_view_window_save_attachments (ModestMsgViewWindow *window,
3150 TnyList *mime_parts)
3152 ModestMsgViewWindowPrivate *priv;
3153 GtkWidget *save_dialog = NULL;
3154 gchar *conf_folder = NULL;
3155 gchar *filename = NULL;
3156 gchar *save_multiple_str = NULL;
3157 const gchar *root_folder = "file:///";
3159 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window));
3160 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
3162 if (mime_parts == NULL) {
3163 /* In Hildon 2.2 save and delete operate over all the attachments as there's no
3164 * selection available */
3165 mime_parts = modest_msg_view_get_attachments (MODEST_MSG_VIEW (priv->msg_view));
3166 if (mime_parts && !modest_maemo_utils_select_attachments (GTK_WINDOW (window), mime_parts, FALSE)) {
3167 g_object_unref (mime_parts);
3170 if (mime_parts == NULL || tny_list_get_length (mime_parts) == 0) {
3172 g_object_unref (mime_parts);
3178 g_object_ref (mime_parts);
3181 /* prepare dialog */
3182 if (tny_list_get_length (mime_parts) == 1) {
3184 /* only one attachment selected */
3185 iter = tny_list_create_iterator (mime_parts);
3186 TnyMimePart *mime_part = (TnyMimePart *) tny_iterator_get_current (iter);
3187 g_object_unref (iter);
3188 if (!modest_tny_mime_part_is_msg (mime_part) &&
3189 modest_tny_mime_part_is_attachment_for_modest (mime_part) &&
3190 !tny_mime_part_is_purged (mime_part)) {
3191 filename = g_strdup (tny_mime_part_get_filename (mime_part));
3193 /* TODO: show any error? */
3194 g_warning ("%s: Tried to save a non-file attachment", __FUNCTION__);
3195 g_object_unref (mime_parts);
3198 g_object_unref (mime_part);
3200 gint num = tny_list_get_length (mime_parts);
3201 save_multiple_str = g_strdup_printf (dngettext("hildon-fm",
3202 "sfil_va_number_of_objects_attachment",
3203 "sfil_va_number_of_objects_attachments",
3207 /* Creation of hildon file chooser dialog for saving */
3208 save_dialog = modest_toolkit_factory_create_file_chooser_dialog (modest_runtime_get_toolkit_factory (),
3210 (GtkWindow *) window,
3211 GTK_FILE_CHOOSER_ACTION_SAVE);
3213 /* Get last used folder */
3214 conf_folder = modest_conf_get_string (modest_runtime_get_conf (),
3215 MODEST_CONF_LATEST_SAVE_ATTACHMENT_PATH, NULL);
3217 /* File chooser stops working if we select "file:///" as current folder */
3218 if (conf_folder && g_ascii_strcasecmp (root_folder, conf_folder) != 0) {
3219 g_free (conf_folder);
3223 if (conf_folder && conf_folder[0] != '\0') {
3224 gtk_file_chooser_set_current_folder_uri (GTK_FILE_CHOOSER (save_dialog), conf_folder);
3227 /* Set the default folder to documents folder */
3228 docs_folder = (gchar *) g_strdup(g_get_user_special_dir (G_USER_DIRECTORY_DOCUMENTS));
3231 docs_folder = g_build_filename (g_getenv (MYDOCS_ENV), DOCS_FOLDER, NULL);
3233 gtk_file_chooser_set_current_folder (GTK_FILE_CHOOSER (save_dialog), docs_folder);
3234 g_free (docs_folder);
3236 g_free (conf_folder);
3240 gtk_file_chooser_set_current_name (GTK_FILE_CHOOSER (save_dialog),
3245 /* if multiple, set multiple string */
3246 if (save_multiple_str) {
3247 g_object_set (G_OBJECT (save_dialog), "save-multiple", save_multiple_str, NULL);
3248 gtk_window_set_title (GTK_WINDOW (save_dialog), _FM("sfil_ti_save_objects_files"));
3249 g_free (save_multiple_str);
3252 /* We must run this asynchronously, because the hildon dialog
3253 performs a gtk_dialog_run by itself which leads to gdk
3255 SaveAttachmentsInfo *sa_info;
3256 sa_info = g_slice_new (SaveAttachmentsInfo);
3257 sa_info->attachments_list = mime_parts;
3258 sa_info->window = g_object_ref (window);
3259 g_signal_connect (save_dialog, "response",
3260 G_CALLBACK (save_attachments_response), sa_info);
3262 gtk_widget_show_all (save_dialog);
3266 show_remove_attachment_information (gpointer userdata)
3268 ModestMsgViewWindow *window = (ModestMsgViewWindow *) userdata;
3269 ModestMsgViewWindowPrivate *priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
3271 /* We're outside the main lock */
3272 gdk_threads_enter ();
3274 if (priv->remove_attachment_banner != NULL) {
3275 gtk_widget_destroy (priv->remove_attachment_banner);
3276 g_object_unref (priv->remove_attachment_banner);
3279 priv->remove_attachment_banner = g_object_ref (
3280 modest_platform_animation_banner (NULL, NULL, _("mcen_me_inbox_remove_attachments")));
3282 gdk_threads_leave ();
3288 modest_msg_view_window_remove_attachments (ModestMsgViewWindow *window, gboolean get_all)
3290 ModestMsgViewWindowPrivate *priv;
3291 TnyList *mime_parts = NULL, *tmp;
3292 gchar *confirmation_message;
3298 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window));
3299 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
3301 /* In hildon 2.2 we ignore the get_all flag as we always get all attachments. This is
3302 * because we don't have selection
3304 mime_parts = modest_msg_view_get_attachments (MODEST_MSG_VIEW (priv->msg_view));
3306 /* Remove already purged messages from mime parts list. We use
3307 a copy of the list to remove items in the original one */
3308 tmp = tny_list_copy (mime_parts);
3309 iter = tny_list_create_iterator (tmp);
3310 while (!tny_iterator_is_done (iter)) {
3311 TnyMimePart *part = TNY_MIME_PART (tny_iterator_get_current (iter));
3312 if (tny_mime_part_is_purged (part))
3313 tny_list_remove (mime_parts, (GObject *) part);
3315 g_object_unref (part);
3316 tny_iterator_next (iter);
3318 g_object_unref (tmp);
3319 g_object_unref (iter);
3321 if (!modest_maemo_utils_select_attachments (GTK_WINDOW (window), mime_parts, TRUE) ||
3322 tny_list_get_length (mime_parts) == 0) {
3323 g_object_unref (mime_parts);
3327 n_attachments = tny_list_get_length (mime_parts);
3328 if (n_attachments == 1) {
3332 iter = tny_list_create_iterator (mime_parts);
3333 part = (TnyMimePart *) tny_iterator_get_current (iter);
3334 g_object_unref (iter);
3335 if (modest_tny_mime_part_is_msg (part)) {
3337 header = tny_msg_get_header (TNY_MSG (part));
3338 filename = tny_header_dup_subject (header);
3339 g_object_unref (header);
3340 if (filename == NULL)
3341 filename = g_strdup (_("mail_va_no_subject"));
3343 filename = g_strdup (tny_mime_part_get_filename (TNY_MIME_PART (part)));
3345 confirmation_message = g_strdup_printf (_("mcen_nc_purge_file_text"), filename);
3347 g_object_unref (part);
3349 confirmation_message = g_strdup_printf (ngettext("mcen_nc_purge_file_text",
3350 "mcen_nc_purge_files_text",
3351 n_attachments), n_attachments);
3353 response = modest_platform_run_confirmation_dialog (GTK_WINDOW (window),
3354 confirmation_message);
3355 g_free (confirmation_message);
3357 if (response != GTK_RESPONSE_OK) {
3358 g_object_unref (mime_parts);
3362 priv->purge_timeout = g_timeout_add (2000, show_remove_attachment_information, window);
3364 iter = tny_list_create_iterator (mime_parts);
3365 while (!tny_iterator_is_done (iter)) {
3368 part = (TnyMimePart *) tny_iterator_get_current (iter);
3369 tny_mime_part_set_purged (TNY_MIME_PART (part));
3370 g_object_unref (part);
3371 tny_iterator_next (iter);
3373 g_object_unref (iter);
3375 msg = tny_msg_view_get_msg (TNY_MSG_VIEW (priv->msg_view));
3376 tny_msg_view_clear (TNY_MSG_VIEW (priv->msg_view));
3377 tny_msg_rewrite_cache (msg);
3378 tny_msg_view_set_msg (TNY_MSG_VIEW (priv->msg_view), msg);
3379 g_object_unref (msg);
3380 update_branding (MODEST_MSG_VIEW_WINDOW (window));
3382 g_object_unref (mime_parts);
3384 if (priv->purge_timeout > 0) {
3385 g_source_remove (priv->purge_timeout);
3386 priv->purge_timeout = 0;
3389 if (priv->remove_attachment_banner) {
3390 gtk_widget_destroy (priv->remove_attachment_banner);
3391 g_object_unref (priv->remove_attachment_banner);
3392 priv->remove_attachment_banner = NULL;
3398 update_window_title (ModestMsgViewWindow *window)
3400 ModestMsgViewWindowPrivate *priv;
3402 TnyHeader *header = NULL;
3403 gchar *subject = NULL;
3405 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
3407 /* Note that if the window is closed while we're retrieving
3408 the message, this widget could de deleted */
3409 if (!priv->msg_view)
3412 msg = tny_msg_view_get_msg (TNY_MSG_VIEW (priv->msg_view));
3414 if (priv->other_body) {
3417 description = modest_tny_mime_part_get_header_value (priv->other_body, "Content-Description");
3419 g_strstrip (description);
3420 subject = description;
3422 } else if (msg != NULL) {
3423 header = tny_msg_get_header (msg);
3424 subject = tny_header_dup_subject (header);
3425 g_object_unref (header);
3426 g_object_unref (msg);
3429 if ((subject == NULL)||(subject[0] == '\0')) {
3431 subject = g_strdup (_("mail_va_no_subject"));
3434 modest_window_set_title (MODEST_WINDOW (window), subject);
3439 on_move_focus (GtkWidget *widget,
3440 GtkDirectionType direction,
3443 g_signal_stop_emission_by_name (G_OBJECT (widget), "move-focus");
3447 fetch_image_open_stream (TnyStreamCache *self, gint64 *expected_size, gchar *uri)
3449 GnomeVFSResult result;
3450 GnomeVFSHandle *handle = NULL;
3451 GnomeVFSFileInfo *info = NULL;
3454 result = gnome_vfs_open (&handle, uri, GNOME_VFS_OPEN_READ);
3455 if (result != GNOME_VFS_OK) {
3460 info = gnome_vfs_file_info_new ();
3461 result = gnome_vfs_get_file_info_from_handle (handle, info, GNOME_VFS_FILE_INFO_DEFAULT);
3462 if (result != GNOME_VFS_OK || ! (info->valid_fields & GNOME_VFS_FILE_INFO_FIELDS_SIZE)) {
3463 /* We put a "safe" default size for going to cache */
3464 *expected_size = (300*1024);
3466 *expected_size = info->size;
3468 gnome_vfs_file_info_unref (info);
3470 stream = tny_vfs_stream_new (handle);
3479 TnyStream *output_stream;
3480 GtkWidget *msg_view;
3485 on_fetch_image_idle_refresh_view (gpointer userdata)
3488 FetchImageData *fidata = (FetchImageData *) userdata;
3490 gdk_threads_enter ();
3491 if (GTK_WIDGET_DRAWABLE (fidata->msg_view)) {
3492 ModestMsgViewWindowPrivate *priv;
3494 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (fidata->window);
3495 priv->fetching_images--;
3496 gtk_widget_queue_draw (fidata->msg_view);
3497 update_progress_hint (MODEST_MSG_VIEW_WINDOW (fidata->window));
3499 gdk_threads_leave ();
3501 g_object_unref (fidata->msg_view);
3502 g_object_unref (fidata->window);
3503 g_slice_free (FetchImageData, fidata);
3508 on_fetch_image_thread (gpointer userdata)
3510 FetchImageData *fidata = (FetchImageData *) userdata;
3511 TnyStreamCache *cache;
3512 TnyStream *cache_stream;
3514 cache = modest_runtime_get_images_cache ();
3516 tny_stream_cache_get_stream (cache,
3518 (TnyStreamCacheOpenStreamFetcher) fetch_image_open_stream,
3519 (gpointer) fidata->uri);
3520 g_free (fidata->cache_id);
3521 g_free (fidata->uri);
3523 if (cache_stream != NULL) {
3526 while (G_LIKELY (!tny_stream_is_eos (cache_stream))) {
3529 nb_read = tny_stream_read (cache_stream, buffer, sizeof (buffer));
3530 if (G_UNLIKELY (nb_read < 0)) {
3532 } else if (G_LIKELY (nb_read > 0)) {
3533 gssize nb_written = 0;
3535 while (G_UNLIKELY (nb_written < nb_read)) {
3538 len = tny_stream_write (fidata->output_stream, buffer + nb_written,
3539 nb_read - nb_written);
3540 if (G_UNLIKELY (len < 0))
3546 tny_stream_close (cache_stream);
3547 g_object_unref (cache_stream);
3550 tny_stream_close (fidata->output_stream);
3551 g_object_unref (fidata->output_stream);
3553 g_idle_add (on_fetch_image_idle_refresh_view, fidata);
3559 on_fetch_image (ModestMsgView *msgview,
3562 ModestMsgViewWindow *window)
3564 const gchar *current_account;
3565 ModestMsgViewWindowPrivate *priv;
3566 FetchImageData *fidata;
3568 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
3570 current_account = modest_window_get_active_account (MODEST_WINDOW (window));
3572 fidata = g_slice_new0 (FetchImageData);
3573 fidata->msg_view = g_object_ref (msgview);
3574 fidata->window = g_object_ref (window);
3575 fidata->uri = g_strdup (uri);
3576 fidata->cache_id = modest_images_cache_get_id (current_account, uri);
3577 fidata->output_stream = g_object_ref (stream);
3579 priv->fetching_images++;
3580 if (g_thread_create (on_fetch_image_thread, fidata, FALSE, NULL) == NULL) {
3581 g_object_unref (fidata->output_stream);
3582 g_free (fidata->cache_id);
3583 g_free (fidata->uri);
3584 g_object_unref (fidata->msg_view);
3585 g_slice_free (FetchImageData, fidata);
3586 tny_stream_close (stream);
3587 priv->fetching_images--;
3588 update_progress_hint (window);
3591 update_progress_hint (window);
3597 setup_menu (ModestMsgViewWindow *self)
3599 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW(self));
3601 /* Settings menu buttons */
3602 modest_window_add_to_menu (MODEST_WINDOW (self), _("mcen_me_viewer_find"), NULL,
3603 MODEST_WINDOW_MENU_CALLBACK (modest_msg_view_window_show_isearch_toolbar),
3604 MODEST_DIMMING_CALLBACK (modest_ui_dimming_rules_on_find_in_msg));
3606 modest_window_add_to_menu (MODEST_WINDOW (self),
3607 dngettext(GETTEXT_PACKAGE,
3608 "mcen_me_move_message",
3609 "mcen_me_move_messages",
3612 MODEST_WINDOW_MENU_CALLBACK (modest_ui_actions_on_move_to),
3613 MODEST_DIMMING_CALLBACK (modest_ui_dimming_rules_on_move_to));
3615 modest_window_add_to_menu (MODEST_WINDOW (self), _("mcen_me_inbox_mark_as_read"), NULL,
3616 MODEST_WINDOW_MENU_CALLBACK (modest_ui_actions_on_mark_as_read),
3617 MODEST_DIMMING_CALLBACK (modest_ui_dimming_rules_on_mark_as_read_msg_in_view));
3619 modest_window_add_to_menu (MODEST_WINDOW (self), _("mcen_me_inbox_mark_as_unread"), NULL,
3620 MODEST_WINDOW_MENU_CALLBACK (modest_ui_actions_on_mark_as_unread),
3621 MODEST_DIMMING_CALLBACK (modest_ui_dimming_rules_on_mark_as_unread_msg_in_view));
3623 modest_window_add_to_menu (MODEST_WINDOW (self), _("mcen_me_viewer_save_attachments"), NULL,
3624 MODEST_WINDOW_MENU_CALLBACK (modest_ui_actions_save_attachments),
3625 MODEST_DIMMING_CALLBACK (modest_ui_dimming_rules_on_save_attachments));
3626 modest_window_add_to_menu (MODEST_WINDOW (self), _("mcen_me_inbox_remove_attachments"), NULL,
3627 MODEST_WINDOW_MENU_CALLBACK (modest_ui_actions_remove_attachments),
3628 MODEST_DIMMING_CALLBACK (modest_ui_dimming_rules_on_remove_attachments));
3630 modest_window_add_to_menu (MODEST_WINDOW (self), _("mcen_me_new_message"), "<Control>n",
3631 MODEST_WINDOW_MENU_CALLBACK (modest_ui_actions_on_new_msg),
3632 MODEST_DIMMING_CALLBACK (modest_ui_dimming_rules_on_new_msg));
3633 modest_window_add_to_menu (MODEST_WINDOW (self), _("mcen_me_viewer_addtocontacts"), NULL,
3634 MODEST_WINDOW_MENU_CALLBACK (modest_ui_actions_add_to_contacts),
3635 MODEST_DIMMING_CALLBACK (modest_ui_dimming_rules_on_add_to_contacts));
3637 modest_window_add_to_menu (MODEST_WINDOW (self), _("mcen_ti_message_properties"), NULL,
3638 MODEST_WINDOW_MENU_CALLBACK (modest_ui_actions_on_details),
3639 MODEST_DIMMING_CALLBACK (modest_ui_dimming_rules_on_details));
3643 modest_msg_view_window_add_to_contacts (ModestMsgViewWindow *self)
3645 ModestMsgViewWindowPrivate *priv;
3646 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
3647 GSList *recipients = NULL;
3650 msg = tny_msg_view_get_msg (TNY_MSG_VIEW (priv->msg_view));
3654 header = modest_msg_view_window_get_header (self);
3657 recipients = modest_tny_msg_header_get_all_recipients_list (header);
3658 g_object_unref (header);
3660 recipients = modest_tny_msg_get_all_recipients_list (msg);
3661 g_object_unref (msg);
3665 /* Offer the user to add recipients to the address book */
3666 modest_address_book_add_address_list_with_selector (recipients, (GtkWindow *) self);
3667 g_slist_foreach (recipients, (GFunc) g_free, NULL); g_slist_free (recipients);
3672 _modest_msg_view_window_map_event (GtkWidget *widget,
3676 ModestMsgViewWindow *self = (ModestMsgViewWindow *) userdata;
3678 update_progress_hint (self);
3684 modest_msg_view_window_fetch_images (ModestMsgViewWindow *self)
3686 ModestMsgViewWindowPrivate *priv;
3687 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
3689 modest_msg_view_request_fetch_images (MODEST_MSG_VIEW (priv->msg_view));
3693 modest_msg_view_window_has_blocked_external_images (ModestMsgViewWindow *self)
3695 ModestMsgViewWindowPrivate *priv;
3696 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
3698 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self), FALSE);
3700 return modest_msg_view_has_blocked_external_images (MODEST_MSG_VIEW (priv->msg_view));
3704 modest_msg_view_window_reload (ModestMsgViewWindow *self)
3706 ModestMsgViewWindowPrivate *priv;
3707 const gchar *msg_uid;
3708 TnyHeader *header = NULL;
3709 TnyFolder *folder = NULL;
3711 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self));
3713 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
3715 header = modest_msg_view_window_get_header (MODEST_MSG_VIEW_WINDOW (self));
3719 folder = tny_header_get_folder (header);
3720 g_object_unref (header);
3725 msg_uid = modest_msg_view_window_get_message_uid (self);
3727 GtkTreeRowReference *row_reference;
3729 if (priv->row_reference && gtk_tree_row_reference_valid (priv->row_reference)) {
3730 row_reference = priv->row_reference;
3732 row_reference = NULL;
3734 if (!message_reader (self, priv, NULL, msg_uid, folder, row_reference))
3735 g_warning ("Shouldn't happen, trying to reload a message failed");
3738 g_object_unref (folder);
3742 update_branding (ModestMsgViewWindow *self)
3744 const gchar *account;
3745 const gchar *mailbox;
3746 ModestAccountMgr *mgr;
3747 ModestProtocol *protocol = NULL;
3748 gchar *service_name = NULL;
3749 const GdkPixbuf *service_icon = NULL;
3750 ModestMsgViewWindowPrivate *priv;
3752 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
3754 account = modest_window_get_active_account (MODEST_WINDOW (self));
3755 mailbox = modest_window_get_active_mailbox (MODEST_WINDOW (self));
3757 mgr = modest_runtime_get_account_mgr ();
3759 if (modest_account_mgr_account_is_multimailbox (mgr, account, &protocol)) {
3760 if (MODEST_IS_ACCOUNT_PROTOCOL (protocol)) {
3761 service_name = modest_account_protocol_get_service_name (MODEST_ACCOUNT_PROTOCOL (protocol),
3763 service_icon = modest_account_protocol_get_service_icon (MODEST_ACCOUNT_PROTOCOL (protocol),
3764 account, mailbox, MODEST_ICON_SIZE_SMALL);
3768 modest_msg_view_set_branding (MODEST_MSG_VIEW (priv->msg_view), service_name, service_icon);
3769 g_free (service_name);
3773 sync_flags (ModestMsgViewWindow *self)
3775 TnyHeader *header = NULL;
3777 header = modest_msg_view_window_get_header (self);
3779 TnyMsg *msg = modest_msg_view_window_get_message (self);
3781 header = tny_msg_get_header (msg);
3782 g_object_unref (msg);
3787 TnyFolder *folder = tny_header_get_folder (header);
3790 ModestMailOperation *mail_op;
3792 /* Sync folder, we need this to save the seen flag */
3793 mail_op = modest_mail_operation_new (NULL);
3794 modest_mail_operation_queue_add (modest_runtime_get_mail_operation_queue (),
3796 modest_mail_operation_sync_folder (mail_op, folder, FALSE, NULL, NULL);
3797 g_object_unref (mail_op);
3798 g_object_unref (folder);
3800 g_object_unref (header);