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 <hildon/hildon-picker-dialog.h>
54 #include <hildon/hildon-app-menu.h>
55 #include "modest-defs.h"
56 #include "modest-hildon-includes.h"
57 #include "modest-ui-dimming-manager.h"
58 #include <gdk/gdkkeysyms.h>
59 #include <modest-tny-account.h>
60 #include <modest-mime-part-view.h>
61 #include <modest-isearch-view.h>
62 #include <modest-tny-mime-part.h>
63 #include <modest-address-book.h>
66 #include <glib/gstdio.h>
67 #include <modest-debug.h>
68 #include <modest-header-window.h>
69 #include <modest-account-protocol.h>
70 #include <modest-icon-names.h>
71 #include <modest-ui-actions.h>
72 #include <tny-camel-msg.h>
74 #define MYDOCS_ENV "MYDOCSDIR"
75 #define DOCS_FOLDER ".documents"
77 typedef struct _ModestMsgViewWindowPrivate ModestMsgViewWindowPrivate;
78 struct _ModestMsgViewWindowPrivate {
81 GtkWidget *main_scroll;
82 GtkWidget *find_toolbar;
85 /* Progress observers */
86 GSList *progress_widgets;
89 GtkWidget *prev_toolitem;
90 GtkWidget *next_toolitem;
91 gboolean progress_hint;
94 /* Optimized view enabled */
95 gboolean optimized_view;
97 /* Whether this was created via the *_new_for_search_result() function. */
98 gboolean is_search_result;
100 /* Whether the message is in outbox */
103 /* A reference to the @model of the header view
104 * to allow selecting previous/next messages,
105 * if the message is currently selected in the header view.
107 const gchar *header_folder_id;
108 GtkTreeModel *header_model;
109 GtkTreeRowReference *row_reference;
110 GtkTreeRowReference *next_row_reference;
112 gulong clipboard_change_handler;
113 gulong queue_change_handler;
114 gulong account_removed_handler;
115 gulong row_changed_handler;
116 gulong row_deleted_handler;
117 gulong row_inserted_handler;
118 gulong rows_reordered_handler;
121 GtkWidget *remove_attachment_banner;
124 TnyMimePart *other_body;
129 static void modest_msg_view_window_class_init (ModestMsgViewWindowClass *klass);
130 static void modest_msg_view_window_init (ModestMsgViewWindow *obj);
131 static void modest_header_view_observer_init (ModestHeaderViewObserverIface *iface_class);
132 static void modest_msg_view_window_finalize (GObject *obj);
133 static void modest_msg_view_window_show_find_toolbar (GtkWidget *obj, gpointer data);
134 static void modest_msg_view_window_find_toolbar_close (GtkWidget *widget,
135 ModestMsgViewWindow *obj);
136 static void modest_msg_view_window_find_toolbar_search (GtkWidget *widget,
137 ModestMsgViewWindow *obj);
138 static void modest_msg_view_window_toggle_find_toolbar (GtkWidget *obj,
140 static void modest_msg_view_window_disconnect_signals (ModestWindow *self);
142 static gdouble modest_msg_view_window_get_zoom (ModestWindow *window);
143 static void modest_msg_view_window_set_zoom (ModestWindow *window,
145 static gboolean modest_msg_view_window_zoom_minus (ModestWindow *window);
146 static gboolean modest_msg_view_window_zoom_plus (ModestWindow *window);
147 static gboolean modest_msg_view_window_key_event (GtkWidget *window,
150 static void modest_msg_view_window_update_priority (ModestMsgViewWindow *window);
152 static void modest_msg_view_window_show_toolbar (ModestWindow *window,
153 gboolean show_toolbar);
155 static void modest_msg_view_window_clipboard_owner_change (GtkClipboard *clipboard,
157 ModestMsgViewWindow *window);
159 static void modest_msg_view_window_on_row_changed (GtkTreeModel *header_model,
162 ModestMsgViewWindow *window);
164 static void modest_msg_view_window_on_row_deleted (GtkTreeModel *header_model,
166 ModestMsgViewWindow *window);
168 static void modest_msg_view_window_on_row_inserted (GtkTreeModel *header_model,
169 GtkTreePath *tree_path,
170 GtkTreeIter *tree_iter,
171 ModestMsgViewWindow *window);
173 static void modest_msg_view_window_on_row_reordered (GtkTreeModel *header_model,
177 ModestMsgViewWindow *window);
179 static void modest_msg_view_window_update_model_replaced (ModestHeaderViewObserver *window,
181 const gchar *tny_folder_id);
183 static void on_queue_changed (ModestMailOperationQueue *queue,
184 ModestMailOperation *mail_op,
185 ModestMailOperationQueueNotification type,
186 ModestMsgViewWindow *self);
188 static void on_account_removed (TnyAccountStore *account_store,
192 static void on_move_focus (GtkWidget *widget,
193 GtkDirectionType direction,
196 static void view_msg_cb (ModestMailOperation *mail_op,
203 static void set_progress_hint (ModestMsgViewWindow *self,
206 static void update_window_title (ModestMsgViewWindow *window);
208 static void init_window (ModestMsgViewWindow *obj);
210 static gboolean msg_is_visible (TnyHeader *header, gboolean check_outbox);
212 static void check_dimming_rules_after_change (ModestMsgViewWindow *window);
214 static gboolean on_fetch_image (ModestMsgView *msgview,
217 ModestMsgViewWindow *window);
219 static gboolean modest_msg_view_window_scroll_child (ModestMsgViewWindow *self,
220 GtkScrollType scroll_type,
223 static gboolean message_reader (ModestMsgViewWindow *window,
224 ModestMsgViewWindowPrivate *priv,
226 const gchar *msg_uid,
228 GtkTreeRowReference *row_reference);
230 static void setup_menu (ModestMsgViewWindow *self);
231 static gboolean _modest_msg_view_window_map_event (GtkWidget *widget,
234 static void update_branding (ModestMsgViewWindow *self);
235 static void sync_flags (ModestMsgViewWindow *self);
237 /* list my signals */
244 static const GtkActionEntry msg_view_toolbar_action_entries [] = {
247 { "ToolbarMessageReply", MODEST_STOCK_REPLY, N_("mcen_me_inbox_reply"), "<CTRL>R", NULL, G_CALLBACK (modest_ui_actions_on_reply) },
248 { "ToolbarMessageReplyAll", MODEST_STOCK_REPLY_ALL, N_("mcen_me_inbox_replytoall"), NULL, NULL, G_CALLBACK (modest_ui_actions_on_reply_all) },
249 { "ToolbarMessageForward", MODEST_STOCK_FORWARD, N_("mcen_me_inbox_forward"), NULL, NULL, G_CALLBACK (modest_ui_actions_on_forward) },
250 { "ToolbarDeleteMessage", MODEST_STOCK_DELETE, N_("qgn_toolb_gene_deletebutton"), NULL, NULL, G_CALLBACK (modest_ui_actions_on_delete_message_or_folder) },
251 { "ToolbarMessageBack", MODEST_TOOLBAR_ICON_PREV, N_("qgn_toolb_gene_back"), NULL, NULL, G_CALLBACK (modest_ui_actions_on_prev) },
252 { "ToolbarMessageNext", MODEST_TOOLBAR_ICON_NEXT, N_("qgn_toolb_gene_forward"), NULL, NULL, G_CALLBACK (modest_ui_actions_on_next) },
253 { "ToolbarDownloadExternalImages", MODEST_TOOLBAR_ICON_DOWNLOAD_IMAGES, N_("mail_bd_external_images"), NULL, NULL, G_CALLBACK (modest_ui_actions_on_fetch_images) },
256 static const GtkToggleActionEntry msg_view_toggle_action_entries [] = {
257 { "FindInMessage", MODEST_TOOLBAR_ICON_FIND, N_("qgn_toolb_gene_find"), "<CTRL>F", NULL, G_CALLBACK (modest_msg_view_window_toggle_find_toolbar), FALSE },
260 #define MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE((o), \
261 MODEST_TYPE_MSG_VIEW_WINDOW, \
262 ModestMsgViewWindowPrivate))
264 static GtkWindowClass *parent_class = NULL;
266 /* uncomment the following if you have defined any signals */
267 static guint signals[LAST_SIGNAL] = {0};
270 modest_msg_view_window_get_type (void)
272 static GType my_type = 0;
274 static const GTypeInfo my_info = {
275 sizeof(ModestMsgViewWindowClass),
276 NULL, /* base init */
277 NULL, /* base finalize */
278 (GClassInitFunc) modest_msg_view_window_class_init,
279 NULL, /* class finalize */
280 NULL, /* class data */
281 sizeof(ModestMsgViewWindow),
283 (GInstanceInitFunc) modest_msg_view_window_init,
286 #ifdef MODEST_TOOLKIT_GTK
287 my_type = g_type_register_static (MODEST_TYPE_WINDOW,
288 "ModestMsgViewWindow",
291 my_type = g_type_register_static (MODEST_TYPE_HILDON2_WINDOW,
292 "ModestMsgViewWindow",
296 static const GInterfaceInfo modest_header_view_observer_info =
298 (GInterfaceInitFunc) modest_header_view_observer_init,
299 NULL, /* interface_finalize */
300 NULL /* interface_data */
303 g_type_add_interface_static (my_type,
304 MODEST_TYPE_HEADER_VIEW_OBSERVER,
305 &modest_header_view_observer_info);
311 save_state (ModestWindow *self)
313 modest_widget_memory_save (modest_runtime_get_conf (),
315 MODEST_CONF_MSG_VIEW_WINDOW_KEY);
319 modest_msg_view_window_scroll_child (ModestMsgViewWindow *self,
320 GtkScrollType scroll_type,
324 ModestMsgViewWindowPrivate *priv;
327 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(self);
329 switch (scroll_type) {
330 case GTK_SCROLL_STEP_UP:
333 case GTK_SCROLL_STEP_DOWN:
336 case GTK_SCROLL_PAGE_UP:
339 case GTK_SCROLL_PAGE_DOWN:
342 case GTK_SCROLL_START:
353 modest_scrollable_scroll ((ModestScrollable *) priv->main_scroll, 0, step);
355 return (gboolean) step;
359 add_scroll_binding (GtkBindingSet *binding_set,
361 GtkScrollType scroll)
363 guint keypad_keyval = keyval - GDK_Left + GDK_KP_Left;
365 gtk_binding_entry_add_signal (binding_set, keyval, 0,
367 GTK_TYPE_SCROLL_TYPE, scroll,
368 G_TYPE_BOOLEAN, FALSE);
369 gtk_binding_entry_add_signal (binding_set, keypad_keyval, 0,
371 GTK_TYPE_SCROLL_TYPE, scroll,
372 G_TYPE_BOOLEAN, FALSE);
376 modest_msg_view_window_class_init (ModestMsgViewWindowClass *klass)
378 GObjectClass *gobject_class;
379 HildonWindowClass *hildon_window_class;
380 ModestWindowClass *modest_window_class;
381 GtkBindingSet *binding_set;
383 gobject_class = (GObjectClass*) klass;
384 hildon_window_class = (HildonWindowClass *) klass;
385 modest_window_class = (ModestWindowClass *) klass;
387 parent_class = g_type_class_peek_parent (klass);
388 gobject_class->finalize = modest_msg_view_window_finalize;
390 modest_window_class->set_zoom_func = modest_msg_view_window_set_zoom;
391 modest_window_class->get_zoom_func = modest_msg_view_window_get_zoom;
392 modest_window_class->zoom_plus_func = modest_msg_view_window_zoom_plus;
393 modest_window_class->zoom_minus_func = modest_msg_view_window_zoom_minus;
394 modest_window_class->show_toolbar_func = modest_msg_view_window_show_toolbar;
395 modest_window_class->disconnect_signals_func = modest_msg_view_window_disconnect_signals;
397 modest_window_class->save_state_func = save_state;
399 klass->scroll_child = modest_msg_view_window_scroll_child;
401 signals[MSG_CHANGED_SIGNAL] =
402 g_signal_new ("msg-changed",
403 G_TYPE_FROM_CLASS (gobject_class),
405 G_STRUCT_OFFSET (ModestMsgViewWindowClass, msg_changed),
407 modest_marshal_VOID__POINTER_POINTER,
408 G_TYPE_NONE, 2, G_TYPE_POINTER, G_TYPE_POINTER);
410 signals[SCROLL_CHILD_SIGNAL] =
411 g_signal_new ("scroll-child",
412 G_TYPE_FROM_CLASS (gobject_class),
413 G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
414 G_STRUCT_OFFSET (ModestMsgViewWindowClass, scroll_child),
416 modest_marshal_BOOLEAN__ENUM_BOOLEAN,
417 G_TYPE_BOOLEAN, 2, GTK_TYPE_SCROLL_TYPE, G_TYPE_BOOLEAN);
419 binding_set = gtk_binding_set_by_class (klass);
420 add_scroll_binding (binding_set, GDK_Up, GTK_SCROLL_STEP_UP);
421 add_scroll_binding (binding_set, GDK_Down, GTK_SCROLL_STEP_DOWN);
422 add_scroll_binding (binding_set, GDK_Page_Up, GTK_SCROLL_PAGE_UP);
423 add_scroll_binding (binding_set, GDK_Page_Down, GTK_SCROLL_PAGE_DOWN);
424 add_scroll_binding (binding_set, GDK_Home, GTK_SCROLL_START);
425 add_scroll_binding (binding_set, GDK_End, GTK_SCROLL_END);
427 g_type_class_add_private (gobject_class, sizeof(ModestMsgViewWindowPrivate));
431 static void modest_header_view_observer_init(
432 ModestHeaderViewObserverIface *iface_class)
434 iface_class->update_func = modest_msg_view_window_update_model_replaced;
438 modest_msg_view_window_init (ModestMsgViewWindow *obj)
440 ModestMsgViewWindowPrivate *priv;
441 ModestWindowPrivate *parent_priv = NULL;
442 GtkActionGroup *action_group = NULL;
443 GError *error = NULL;
445 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(obj);
446 parent_priv = MODEST_WINDOW_GET_PRIVATE(obj);
447 parent_priv->ui_manager = gtk_ui_manager_new();
449 action_group = gtk_action_group_new ("ModestMsgViewWindowActions");
450 gtk_action_group_set_translation_domain (action_group, GETTEXT_PACKAGE);
452 /* Add common actions */
453 gtk_action_group_add_actions (action_group,
454 msg_view_toolbar_action_entries,
455 G_N_ELEMENTS (msg_view_toolbar_action_entries),
457 gtk_action_group_add_toggle_actions (action_group,
458 msg_view_toggle_action_entries,
459 G_N_ELEMENTS (msg_view_toggle_action_entries),
462 gtk_ui_manager_insert_action_group (parent_priv->ui_manager, action_group, 0);
463 g_object_unref (action_group);
465 /* Load the UI definition */
466 gtk_ui_manager_add_ui_from_file (parent_priv->ui_manager, MODEST_UIDIR "modest-msg-view-window-ui.xml",
469 g_printerr ("modest: could not merge modest-msg-view-window-ui.xml: %s\n", error->message);
470 g_error_free (error);
475 /* Add accelerators */
476 gtk_window_add_accel_group (GTK_WINDOW (obj),
477 gtk_ui_manager_get_accel_group (parent_priv->ui_manager));
479 priv->is_search_result = FALSE;
480 priv->is_outbox = FALSE;
482 priv->msg_view = NULL;
483 priv->header_model = NULL;
484 priv->header_folder_id = NULL;
485 priv->clipboard_change_handler = 0;
486 priv->queue_change_handler = 0;
487 priv->account_removed_handler = 0;
488 priv->row_changed_handler = 0;
489 priv->row_deleted_handler = 0;
490 priv->row_inserted_handler = 0;
491 priv->rows_reordered_handler = 0;
492 priv->progress_hint = FALSE;
493 priv->fetching_images = 0;
495 priv->optimized_view = FALSE;
496 priv->purge_timeout = 0;
497 priv->remove_attachment_banner = NULL;
498 priv->msg_uid = NULL;
499 priv->other_body = NULL;
501 priv->sighandlers = NULL;
504 init_window (MODEST_MSG_VIEW_WINDOW(obj));
509 update_progress_hint (ModestMsgViewWindow *self)
511 ModestMsgViewWindowPrivate *priv;
512 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(self);
514 if (GTK_WIDGET_VISIBLE (self)) {
515 modest_window_show_progress (MODEST_WINDOW (self),
516 (priv->progress_hint || (priv->fetching_images > 0))?1:0);
521 set_progress_hint (ModestMsgViewWindow *self,
524 ModestWindowPrivate *parent_priv;
525 ModestMsgViewWindowPrivate *priv;
527 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self));
529 parent_priv = MODEST_WINDOW_GET_PRIVATE(self);
530 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(self);
532 /* Sets current progress hint */
533 priv->progress_hint = enabled;
535 update_progress_hint (self);
541 init_window (ModestMsgViewWindow *obj)
543 GtkWidget *main_vbox;
544 ModestMsgViewWindowPrivate *priv;
546 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(obj);
548 priv->msg_view = GTK_WIDGET (tny_platform_factory_new_msg_view (modest_tny_platform_factory_get_instance ()));
549 modest_msg_view_set_shadow_type (MODEST_MSG_VIEW (priv->msg_view), GTK_SHADOW_NONE);
550 main_vbox = gtk_vbox_new (FALSE, 6);
552 priv->main_scroll = modest_toolkit_factory_create_scrollable (modest_runtime_get_toolkit_factory ());
553 modest_scrollable_set_horizontal_policy (MODEST_SCROLLABLE (priv->main_scroll), GTK_POLICY_AUTOMATIC);
554 g_object_set (G_OBJECT (priv->main_scroll),
555 "movement-mode", MODEST_MOVEMENT_MODE_BOTH,
556 "horizontal-max-overshoot", 0,
558 gtk_container_add (GTK_CONTAINER (priv->main_scroll), priv->msg_view);
559 gtk_box_pack_start (GTK_BOX(main_vbox), priv->main_scroll, TRUE, TRUE, 0);
560 gtk_container_add (GTK_CONTAINER(obj), main_vbox);
562 /* NULL-ize fields if the window is destroyed */
563 g_signal_connect (priv->msg_view, "destroy", G_CALLBACK (gtk_widget_destroyed), &(priv->msg_view));
565 gtk_widget_show_all (GTK_WIDGET(main_vbox));
569 modest_msg_view_window_disconnect_signals (ModestWindow *self)
571 ModestMsgViewWindowPrivate *priv;
572 GtkWidget *header_view = NULL;
573 GtkWindow *parent_window = NULL;
575 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
577 if (gtk_clipboard_get (GDK_SELECTION_PRIMARY) &&
578 g_signal_handler_is_connected (gtk_clipboard_get (GDK_SELECTION_PRIMARY),
579 priv->clipboard_change_handler))
580 g_signal_handler_disconnect (gtk_clipboard_get (GDK_SELECTION_PRIMARY),
581 priv->clipboard_change_handler);
583 if (g_signal_handler_is_connected (G_OBJECT (modest_runtime_get_mail_operation_queue ()),
584 priv->queue_change_handler))
585 g_signal_handler_disconnect (G_OBJECT (modest_runtime_get_mail_operation_queue ()),
586 priv->queue_change_handler);
588 if (g_signal_handler_is_connected (G_OBJECT (modest_runtime_get_account_store ()),
589 priv->account_removed_handler))
590 g_signal_handler_disconnect (G_OBJECT (modest_runtime_get_account_store ()),
591 priv->account_removed_handler);
593 if (priv->header_model) {
594 if (g_signal_handler_is_connected(G_OBJECT (priv->header_model),
595 priv->row_changed_handler))
596 g_signal_handler_disconnect(G_OBJECT (priv->header_model),
597 priv->row_changed_handler);
599 if (g_signal_handler_is_connected(G_OBJECT (priv->header_model),
600 priv->row_deleted_handler))
601 g_signal_handler_disconnect(G_OBJECT (priv->header_model),
602 priv->row_deleted_handler);
604 if (g_signal_handler_is_connected(G_OBJECT (priv->header_model),
605 priv->row_inserted_handler))
606 g_signal_handler_disconnect(G_OBJECT (priv->header_model),
607 priv->row_inserted_handler);
609 if (g_signal_handler_is_connected(G_OBJECT (priv->header_model),
610 priv->rows_reordered_handler))
611 g_signal_handler_disconnect(G_OBJECT (priv->header_model),
612 priv->rows_reordered_handler);
615 modest_signal_mgr_disconnect_all_and_destroy (priv->sighandlers);
616 priv->sighandlers = NULL;
618 parent_window = gtk_window_get_transient_for (GTK_WINDOW (self));
619 if (parent_window && MODEST_IS_HEADER_WINDOW (parent_window)) {
620 header_view = GTK_WIDGET (modest_header_window_get_header_view (MODEST_HEADER_WINDOW (parent_window)));
622 modest_header_view_remove_observer(MODEST_HEADER_VIEW (header_view),
623 MODEST_HEADER_VIEW_OBSERVER(self));
629 modest_msg_view_window_finalize (GObject *obj)
631 ModestMsgViewWindowPrivate *priv;
633 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (obj);
635 /* Sanity check: shouldn't be needed, the window mgr should
636 call this function before */
637 modest_msg_view_window_disconnect_signals (MODEST_WINDOW (obj));
639 if (priv->other_body != NULL) {
640 g_object_unref (priv->other_body);
641 priv->other_body = NULL;
644 if (priv->header_model != NULL) {
645 g_object_unref (priv->header_model);
646 priv->header_model = NULL;
649 if (priv->remove_attachment_banner) {
650 gtk_widget_destroy (priv->remove_attachment_banner);
651 g_object_unref (priv->remove_attachment_banner);
652 priv->remove_attachment_banner = NULL;
655 if (priv->purge_timeout > 0) {
656 g_source_remove (priv->purge_timeout);
657 priv->purge_timeout = 0;
660 if (priv->row_reference) {
661 gtk_tree_row_reference_free (priv->row_reference);
662 priv->row_reference = NULL;
665 if (priv->next_row_reference) {
666 gtk_tree_row_reference_free (priv->next_row_reference);
667 priv->next_row_reference = NULL;
671 g_free (priv->msg_uid);
672 priv->msg_uid = NULL;
675 G_OBJECT_CLASS(parent_class)->finalize (obj);
679 select_next_valid_row (GtkTreeModel *model,
680 GtkTreeRowReference **row_reference,
684 GtkTreeIter tmp_iter;
686 GtkTreePath *next = NULL;
687 gboolean retval = FALSE, finished;
689 g_return_val_if_fail (gtk_tree_row_reference_valid (*row_reference), FALSE);
691 path = gtk_tree_row_reference_get_path (*row_reference);
692 gtk_tree_model_get_iter (model, &tmp_iter, path);
693 gtk_tree_row_reference_free (*row_reference);
694 *row_reference = NULL;
698 TnyHeader *header = NULL;
700 if (gtk_tree_model_iter_next (model, &tmp_iter)) {
701 gtk_tree_model_get (model, &tmp_iter,
702 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
706 if (msg_is_visible (header, is_outbox)) {
707 next = gtk_tree_model_get_path (model, &tmp_iter);
708 *row_reference = gtk_tree_row_reference_new (model, next);
709 gtk_tree_path_free (next);
713 g_object_unref (header);
716 } else if (cycle && gtk_tree_model_get_iter_first (model, &tmp_iter)) {
717 next = gtk_tree_model_get_path (model, &tmp_iter);
719 /* Ensure that we are not selecting the same */
720 if (gtk_tree_path_compare (path, next) != 0) {
721 gtk_tree_model_get (model, &tmp_iter,
722 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
725 if (msg_is_visible (header, is_outbox)) {
726 *row_reference = gtk_tree_row_reference_new (model, next);
730 g_object_unref (header);
734 /* If we ended up in the same message
735 then there is no valid next
739 gtk_tree_path_free (next);
741 /* If there are no more messages and we don't
742 want to start again in the first one then
743 there is no valid next message */
749 gtk_tree_path_free (path);
754 /* TODO: This should be in _init(), with the parameters as properties. */
756 modest_msg_view_window_construct (ModestMsgViewWindow *self,
757 const gchar *modest_account_name,
758 const gchar *mailbox,
759 const gchar *msg_uid)
762 ModestMsgViewWindowPrivate *priv = NULL;
763 ModestWindowPrivate *parent_priv = NULL;
764 ModestDimmingRulesGroup *toolbar_rules_group = NULL;
765 ModestDimmingRulesGroup *clipboard_rules_group = NULL;
767 obj = G_OBJECT (self);
768 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(obj);
769 parent_priv = MODEST_WINDOW_GET_PRIVATE(obj);
771 priv->msg_uid = g_strdup (msg_uid);
774 parent_priv->menubar = NULL;
776 toolbar_rules_group = modest_dimming_rules_group_new (MODEST_DIMMING_RULES_TOOLBAR, TRUE);
777 clipboard_rules_group = modest_dimming_rules_group_new (MODEST_DIMMING_RULES_CLIPBOARD, FALSE);
780 /* Add common dimming rules */
781 modest_dimming_rules_group_add_rules (toolbar_rules_group,
782 modest_msg_view_toolbar_dimming_entries,
783 G_N_ELEMENTS (modest_msg_view_toolbar_dimming_entries),
784 MODEST_WINDOW (self));
785 modest_dimming_rules_group_add_rules (clipboard_rules_group,
786 modest_msg_view_clipboard_dimming_entries,
787 G_N_ELEMENTS (modest_msg_view_clipboard_dimming_entries),
788 MODEST_WINDOW (self));
790 /* Insert dimming rules group for this window */
791 modest_ui_dimming_manager_insert_rules_group (parent_priv->ui_dimming_manager, toolbar_rules_group);
792 modest_ui_dimming_manager_insert_rules_group (parent_priv->ui_dimming_manager, clipboard_rules_group);
793 g_object_unref (toolbar_rules_group);
794 g_object_unref (clipboard_rules_group);
796 /* g_signal_connect (G_OBJECT(obj), "delete-event", G_CALLBACK(on_delete_event), obj); */
798 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);
799 g_signal_connect (G_OBJECT(priv->msg_view), "activate_link",
800 G_CALLBACK (modest_ui_actions_on_msg_link_clicked), obj);
801 g_signal_connect (G_OBJECT(priv->msg_view), "link_hover",
802 G_CALLBACK (modest_ui_actions_on_msg_link_hover), obj);
803 g_signal_connect (G_OBJECT(priv->msg_view), "attachment_clicked",
804 G_CALLBACK (modest_ui_actions_on_msg_attachment_clicked), obj);
805 g_signal_connect (G_OBJECT(priv->msg_view), "recpt_activated",
806 G_CALLBACK (modest_ui_actions_on_msg_recpt_activated), obj);
807 g_signal_connect (G_OBJECT(priv->msg_view), "show_details",
808 G_CALLBACK (modest_ui_actions_on_details), obj);
809 g_signal_connect (G_OBJECT(priv->msg_view), "link_contextual",
810 G_CALLBACK (modest_ui_actions_on_msg_link_contextual), obj);
811 g_signal_connect (G_OBJECT(priv->msg_view), "limit_error",
812 G_CALLBACK (modest_ui_actions_on_limit_error), obj);
813 g_signal_connect (G_OBJECT (priv->msg_view), "fetch_image",
814 G_CALLBACK (on_fetch_image), obj);
816 g_signal_connect (G_OBJECT (obj), "key-release-event",
817 G_CALLBACK (modest_msg_view_window_key_event),
820 g_signal_connect (G_OBJECT (obj), "key-press-event",
821 G_CALLBACK (modest_msg_view_window_key_event),
824 g_signal_connect (G_OBJECT (obj), "move-focus",
825 G_CALLBACK (on_move_focus), obj);
827 g_signal_connect (G_OBJECT (obj), "map-event",
828 G_CALLBACK (_modest_msg_view_window_map_event),
831 /* Mail Operation Queue */
832 priv->queue_change_handler = g_signal_connect (G_OBJECT (modest_runtime_get_mail_operation_queue ()),
834 G_CALLBACK (on_queue_changed),
837 /* Account manager */
838 priv->account_removed_handler = g_signal_connect (G_OBJECT (modest_runtime_get_account_store ()),
840 G_CALLBACK(on_account_removed),
843 modest_window_set_active_account (MODEST_WINDOW(obj), modest_account_name);
844 modest_window_set_active_mailbox (MODEST_WINDOW(obj), mailbox);
846 /* First add out toolbar ... */
847 modest_msg_view_window_show_toolbar (MODEST_WINDOW (obj), TRUE);
849 /****** HILDON2:START
852 /* ... and later the find toolbar. This way find toolbar will
853 be shown over the other */
854 priv->find_toolbar = hildon_find_toolbar_new (NULL);
855 hildon_window_add_toolbar (HILDON_WINDOW (obj), GTK_TOOLBAR (priv->find_toolbar));
856 gtk_widget_set_no_show_all (priv->find_toolbar, TRUE);
857 g_signal_connect (G_OBJECT (priv->find_toolbar), "close",
858 G_CALLBACK (modest_msg_view_window_find_toolbar_close), obj);
859 g_signal_connect (G_OBJECT (priv->find_toolbar), "search",
860 G_CALLBACK (modest_msg_view_window_find_toolbar_search), obj);
861 priv->last_search = NULL;
862 /****** HILDON2:END */
864 /* Init the clipboard actions dim status */
865 modest_msg_view_grab_focus(MODEST_MSG_VIEW (priv->msg_view));
867 update_window_title (MODEST_MSG_VIEW_WINDOW (obj));
872 /* FIXME: parameter checks */
874 modest_msg_view_window_new_with_header_model (TnyMsg *msg,
875 const gchar *modest_account_name,
876 const gchar *mailbox,
877 const gchar *msg_uid,
879 GtkTreeRowReference *row_reference)
881 ModestMsgViewWindow *window = NULL;
882 ModestMsgViewWindowPrivate *priv = NULL;
883 TnyFolder *header_folder = NULL;
884 ModestHeaderView *header_view = NULL;
885 ModestWindowMgr *mgr = NULL;
888 modest_tny_mime_part_to_string (TNY_MIME_PART (msg), 0);
891 mgr = modest_runtime_get_window_mgr ();
892 window = MODEST_MSG_VIEW_WINDOW (modest_window_mgr_get_msg_view_window (mgr));
893 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), NULL);
895 modest_msg_view_window_construct (window, modest_account_name, mailbox, msg_uid);
897 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
899 /* Remember the message list's TreeModel so we can detect changes
900 * and change the list selection when necessary: */
901 header_folder = modest_header_view_get_folder (header_view);
903 priv->is_outbox = (modest_tny_folder_guess_folder_type (header_folder) ==
904 TNY_FOLDER_TYPE_OUTBOX);
905 priv->header_folder_id = tny_folder_get_id (header_folder);
906 g_object_unref(header_folder);
909 /* Setup row references and connect signals */
910 priv->header_model = g_object_ref (model);
912 if (row_reference && gtk_tree_row_reference_valid (row_reference)) {
913 priv->row_reference = gtk_tree_row_reference_copy (row_reference);
914 priv->next_row_reference = gtk_tree_row_reference_copy (row_reference);
915 select_next_valid_row (model, &(priv->next_row_reference), TRUE, priv->is_outbox);
917 priv->row_reference = NULL;
918 priv->next_row_reference = NULL;
921 /* Connect signals */
922 priv->row_changed_handler =
923 g_signal_connect (GTK_TREE_MODEL(model), "row-changed",
924 G_CALLBACK(modest_msg_view_window_on_row_changed),
926 priv->row_deleted_handler =
927 g_signal_connect (GTK_TREE_MODEL(model), "row-deleted",
928 G_CALLBACK(modest_msg_view_window_on_row_deleted),
930 priv->row_inserted_handler =
931 g_signal_connect (GTK_TREE_MODEL(model), "row-inserted",
932 G_CALLBACK(modest_msg_view_window_on_row_inserted),
934 priv->rows_reordered_handler =
935 g_signal_connect(GTK_TREE_MODEL(model), "rows-reordered",
936 G_CALLBACK(modest_msg_view_window_on_row_reordered),
939 if (header_view != NULL){
940 modest_header_view_add_observer(header_view,
941 MODEST_HEADER_VIEW_OBSERVER(window));
944 tny_msg_view_set_msg (TNY_MSG_VIEW (priv->msg_view), msg);
945 update_window_title (MODEST_MSG_VIEW_WINDOW (window));
946 update_branding (MODEST_MSG_VIEW_WINDOW (window));
948 /* gtk_widget_show_all (GTK_WIDGET (window)); */
949 modest_msg_view_window_update_priority (window);
950 /* Check dimming rules */
951 modest_ui_actions_check_toolbar_dimming_rules (MODEST_WINDOW (window));
952 modest_ui_actions_check_menu_dimming_rules (MODEST_WINDOW (window));
953 modest_window_check_dimming_rules_group (MODEST_WINDOW (window), MODEST_DIMMING_RULES_CLIPBOARD);
955 return MODEST_WINDOW(window);
959 modest_msg_view_window_new_from_uid (const gchar *modest_account_name,
960 const gchar *mailbox,
961 const gchar *msg_uid)
963 ModestMsgViewWindow *window = NULL;
964 ModestMsgViewWindowPrivate *priv = NULL;
965 ModestWindowMgr *mgr = NULL;
967 TnyAccount *account = NULL;
969 mgr = modest_runtime_get_window_mgr ();
970 window = MODEST_MSG_VIEW_WINDOW (modest_window_mgr_get_msg_view_window (mgr));
971 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), NULL);
973 modest_msg_view_window_construct (window, modest_account_name, mailbox, msg_uid);
975 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
977 is_merge = g_str_has_prefix (msg_uid, "merge:");
979 /* Get the account */
981 account = tny_account_store_find_account (TNY_ACCOUNT_STORE (modest_runtime_get_account_store ()),
984 if (is_merge || account) {
985 TnyFolder *folder = NULL;
987 /* Try to get the message, if it's already downloaded
988 we don't need to connect */
990 folder = modest_tny_folder_store_find_folder_from_uri (TNY_FOLDER_STORE (account), msg_uid);
992 ModestTnyAccountStore *account_store;
993 ModestTnyLocalFoldersAccount *local_folders_account;
995 account_store = modest_runtime_get_account_store ();
996 local_folders_account = MODEST_TNY_LOCAL_FOLDERS_ACCOUNT (
997 modest_tny_account_store_get_local_folders_account (account_store));
998 folder = modest_tny_local_folders_account_get_merged_outbox (local_folders_account);
999 g_object_unref (local_folders_account);
1003 gboolean device_online;
1005 device = modest_runtime_get_device();
1006 device_online = tny_device_is_online (device);
1007 if (device_online) {
1008 message_reader (window, priv, NULL, msg_uid, folder, NULL);
1010 TnyMsg *msg = tny_folder_find_msg (folder, msg_uid, NULL);
1012 tny_msg_view_set_msg (TNY_MSG_VIEW (priv->msg_view), msg);
1013 update_window_title (MODEST_MSG_VIEW_WINDOW (window));
1014 update_branding (MODEST_MSG_VIEW_WINDOW (window));
1015 g_object_unref (msg);
1016 /* Sync flags to server */
1017 sync_flags (MODEST_MSG_VIEW_WINDOW (window));
1019 message_reader (window, priv, NULL, msg_uid, folder, NULL);
1022 g_object_unref (folder);
1027 /* Check dimming rules */
1028 modest_ui_actions_check_toolbar_dimming_rules (MODEST_WINDOW (window));
1029 modest_ui_actions_check_menu_dimming_rules (MODEST_WINDOW (window));
1030 modest_window_check_dimming_rules_group (MODEST_WINDOW (window), MODEST_DIMMING_RULES_CLIPBOARD);
1032 return MODEST_WINDOW(window);
1036 modest_msg_view_window_new_from_header_view (ModestHeaderView *header_view,
1037 const gchar *modest_account_name,
1038 const gchar *mailbox,
1039 const gchar *msg_uid,
1040 GtkTreeRowReference *row_reference)
1042 ModestMsgViewWindow *window = NULL;
1043 ModestMsgViewWindowPrivate *priv = NULL;
1044 TnyFolder *header_folder = NULL;
1045 ModestWindowMgr *mgr = NULL;
1049 mgr = modest_runtime_get_window_mgr ();
1050 window = MODEST_MSG_VIEW_WINDOW (modest_window_mgr_get_msg_view_window (mgr));
1051 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), NULL);
1053 modest_msg_view_window_construct (window, modest_account_name, mailbox, msg_uid);
1055 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1057 /* Remember the message list's TreeModel so we can detect changes
1058 * and change the list selection when necessary: */
1060 if (header_view != NULL){
1061 header_folder = modest_header_view_get_folder(header_view);
1062 /* This could happen if the header folder was
1063 unseleted before opening this msg window (for
1064 example if the user selects an account in the
1065 folder view of the main window */
1066 if (header_folder) {
1067 priv->is_outbox = (modest_tny_folder_guess_folder_type (header_folder) ==
1068 TNY_FOLDER_TYPE_OUTBOX);
1069 priv->header_folder_id = tny_folder_get_id(header_folder);
1070 g_object_unref(header_folder);
1074 /* Setup row references and connect signals */
1075 priv->header_model = gtk_tree_view_get_model (GTK_TREE_VIEW (header_view));
1076 g_object_ref (priv->header_model);
1078 if (row_reference && gtk_tree_row_reference_valid (row_reference)) {
1079 priv->row_reference = gtk_tree_row_reference_copy (row_reference);
1080 priv->next_row_reference = gtk_tree_row_reference_copy (row_reference);
1081 select_next_valid_row (priv->header_model, &(priv->next_row_reference), TRUE, priv->is_outbox);
1083 priv->row_reference = NULL;
1084 priv->next_row_reference = NULL;
1087 /* Connect signals */
1088 priv->row_changed_handler =
1089 g_signal_connect (GTK_TREE_MODEL(priv->header_model), "row-changed",
1090 G_CALLBACK(modest_msg_view_window_on_row_changed),
1092 priv->row_deleted_handler =
1093 g_signal_connect (GTK_TREE_MODEL(priv->header_model), "row-deleted",
1094 G_CALLBACK(modest_msg_view_window_on_row_deleted),
1096 priv->row_inserted_handler =
1097 g_signal_connect (GTK_TREE_MODEL(priv->header_model), "row-inserted",
1098 G_CALLBACK(modest_msg_view_window_on_row_inserted),
1100 priv->rows_reordered_handler =
1101 g_signal_connect(GTK_TREE_MODEL(priv->header_model), "rows-reordered",
1102 G_CALLBACK(modest_msg_view_window_on_row_reordered),
1105 if (header_view != NULL){
1106 modest_header_view_add_observer(header_view,
1107 MODEST_HEADER_VIEW_OBSERVER(window));
1110 tny_msg_view_set_msg (TNY_MSG_VIEW (priv->msg_view), NULL);
1111 update_branding (MODEST_MSG_VIEW_WINDOW (window));
1113 if (priv->row_reference) {
1114 path = gtk_tree_row_reference_get_path (priv->row_reference);
1115 if (gtk_tree_model_get_iter (priv->header_model, &iter, path)) {
1117 gtk_tree_model_get (priv->header_model, &iter,
1118 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
1120 message_reader (window, priv, header, NULL, NULL, priv->row_reference);
1121 g_object_unref (header);
1123 gtk_tree_path_free (path);
1125 /* Check dimming rules */
1126 modest_ui_actions_check_toolbar_dimming_rules (MODEST_WINDOW (window));
1127 modest_ui_actions_check_menu_dimming_rules (MODEST_WINDOW (window));
1128 modest_window_check_dimming_rules_group (MODEST_WINDOW (window), MODEST_DIMMING_RULES_CLIPBOARD);
1130 return MODEST_WINDOW(window);
1134 modest_msg_view_window_new_for_search_result (TnyMsg *msg,
1135 const gchar *modest_account_name,
1136 const gchar *mailbox,
1137 const gchar *msg_uid)
1139 ModestMsgViewWindow *window = NULL;
1140 ModestMsgViewWindowPrivate *priv = NULL;
1141 ModestWindowMgr *mgr = NULL;
1143 mgr = modest_runtime_get_window_mgr ();
1144 window = MODEST_MSG_VIEW_WINDOW (modest_window_mgr_get_msg_view_window (mgr));
1145 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), NULL);
1146 modest_msg_view_window_construct (window, modest_account_name, mailbox, msg_uid);
1148 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1150 /* Remember that this is a search result,
1151 * so we can disable some UI appropriately: */
1152 priv->is_search_result = TRUE;
1154 tny_msg_view_set_msg (TNY_MSG_VIEW (priv->msg_view), msg);
1155 update_branding (MODEST_MSG_VIEW_WINDOW (window));
1157 update_window_title (window);
1158 /* gtk_widget_show_all (GTK_WIDGET (window));*/
1159 modest_msg_view_window_update_priority (window);
1161 /* Check dimming rules */
1162 modest_ui_actions_check_toolbar_dimming_rules (MODEST_WINDOW (window));
1163 modest_ui_actions_check_menu_dimming_rules (MODEST_WINDOW (window));
1164 modest_window_check_dimming_rules_group (MODEST_WINDOW (window), MODEST_DIMMING_RULES_CLIPBOARD);
1166 return MODEST_WINDOW(window);
1170 modest_msg_view_window_is_other_body (ModestMsgViewWindow *self)
1172 ModestMsgViewWindowPrivate *priv = NULL;
1174 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self), FALSE);
1175 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
1177 return (priv->other_body != NULL);
1181 modest_msg_view_window_new_with_other_body (TnyMsg *msg,
1182 TnyMimePart *other_body,
1183 const gchar *modest_account_name,
1184 const gchar *mailbox,
1185 const gchar *msg_uid)
1187 GObject *obj = NULL;
1188 ModestMsgViewWindowPrivate *priv;
1189 ModestWindowMgr *mgr = NULL;
1191 g_return_val_if_fail (msg, NULL);
1192 mgr = modest_runtime_get_window_mgr ();
1193 obj = G_OBJECT (modest_window_mgr_get_msg_view_window (mgr));
1194 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (obj);
1195 modest_msg_view_window_construct (MODEST_MSG_VIEW_WINDOW (obj),
1196 modest_account_name, mailbox, msg_uid);
1199 priv->other_body = g_object_ref (other_body);
1200 modest_msg_view_set_msg_with_other_body (MODEST_MSG_VIEW (priv->msg_view), msg, other_body);
1202 tny_msg_view_set_msg (TNY_MSG_VIEW (priv->msg_view), msg);
1204 update_window_title (MODEST_MSG_VIEW_WINDOW (obj));
1205 update_branding (MODEST_MSG_VIEW_WINDOW (obj));
1207 /* gtk_widget_show_all (GTK_WIDGET (obj)); */
1209 /* Check dimming rules */
1210 modest_ui_actions_check_toolbar_dimming_rules (MODEST_WINDOW (obj));
1211 modest_ui_actions_check_menu_dimming_rules (MODEST_WINDOW (obj));
1212 modest_window_check_dimming_rules_group (MODEST_WINDOW (obj), MODEST_DIMMING_RULES_CLIPBOARD);
1214 return MODEST_WINDOW(obj);
1218 modest_msg_view_window_new_for_attachment (TnyMsg *msg,
1219 const gchar *modest_account_name,
1220 const gchar *mailbox,
1221 const gchar *msg_uid)
1223 return modest_msg_view_window_new_with_other_body (msg, NULL, modest_account_name, mailbox, msg_uid);
1227 modest_msg_view_window_on_row_changed (GtkTreeModel *header_model,
1230 ModestMsgViewWindow *window)
1232 check_dimming_rules_after_change (window);
1236 modest_msg_view_window_on_row_deleted(GtkTreeModel *header_model,
1238 ModestMsgViewWindow *window)
1240 check_dimming_rules_after_change (window);
1242 /* The window could have dissapeared */
1245 check_dimming_rules_after_change (ModestMsgViewWindow *window)
1247 modest_ui_actions_check_menu_dimming_rules (MODEST_WINDOW (window));
1248 modest_ui_actions_check_toolbar_dimming_rules (MODEST_WINDOW (window));
1252 /* On insertions we check if the folder still has the message we are
1253 * showing or do not. If do not, we do nothing. Which means we are still
1254 * not attached to any header folder and thus next/prev buttons are
1255 * still dimmed. Once the message that is shown by msg-view is found, the
1256 * new model of header-view will be attached and the references will be set.
1257 * On each further insertions dimming rules will be checked. However
1258 * this requires extra CPU time at least works.
1259 * (An message might be deleted from TnyFolder and thus will not be
1260 * inserted into the model again for example if it is removed by the
1261 * imap server and the header view is refreshed.)
1264 modest_msg_view_window_on_row_inserted (GtkTreeModel *model,
1265 GtkTreePath *tree_path,
1266 GtkTreeIter *tree_iter,
1267 ModestMsgViewWindow *window)
1269 ModestMsgViewWindowPrivate *priv = NULL;
1270 TnyHeader *header = NULL;
1272 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window));
1273 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1275 g_assert (model == priv->header_model);
1277 /* Check if the newly inserted message is the same we are actually
1278 * showing. IF not, we should remain detached from the header model
1279 * and thus prev and next toolbar buttons should remain dimmed. */
1280 gtk_tree_model_get (model, tree_iter,
1281 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
1284 if (TNY_IS_HEADER (header)) {
1287 uid = modest_tny_folder_get_header_unique_id (header);
1288 if (!g_str_equal(priv->msg_uid, uid)) {
1289 check_dimming_rules_after_change (window);
1291 g_object_unref (G_OBJECT(header));
1295 g_object_unref(G_OBJECT(header));
1298 if (priv->row_reference) {
1299 gtk_tree_row_reference_free (priv->row_reference);
1302 /* Setup row_reference for the actual msg. */
1303 priv->row_reference = gtk_tree_row_reference_new (priv->header_model, tree_path);
1304 if (priv->row_reference == NULL) {
1305 g_warning("%s: No reference for msg header item.", __FUNCTION__);
1309 /* Now set up next_row_reference. */
1310 if (priv->next_row_reference) {
1311 gtk_tree_row_reference_free (priv->next_row_reference);
1314 priv->next_row_reference = gtk_tree_row_reference_copy (priv->row_reference);
1315 select_next_valid_row (priv->header_model,
1316 &(priv->next_row_reference), FALSE, priv->is_outbox);
1318 /* Connect the remaining callbacks to become able to detect
1319 * changes in header-view. */
1320 priv->row_changed_handler =
1321 g_signal_connect (priv->header_model, "row-changed",
1322 G_CALLBACK (modest_msg_view_window_on_row_changed),
1324 priv->row_deleted_handler =
1325 g_signal_connect (priv->header_model, "row-deleted",
1326 G_CALLBACK (modest_msg_view_window_on_row_deleted),
1328 priv->rows_reordered_handler =
1329 g_signal_connect (priv->header_model, "rows-reordered",
1330 G_CALLBACK (modest_msg_view_window_on_row_reordered),
1333 check_dimming_rules_after_change (window);
1337 modest_msg_view_window_on_row_reordered (GtkTreeModel *header_model,
1341 ModestMsgViewWindow *window)
1343 ModestMsgViewWindowPrivate *priv = NULL;
1344 gboolean already_changed = FALSE;
1346 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(window);
1348 /* If the current row was reordered select the proper next
1349 valid row. The same if the next row reference changes */
1350 if (!priv->row_reference ||
1351 !gtk_tree_row_reference_valid (priv->row_reference))
1354 if (priv->next_row_reference &&
1355 gtk_tree_row_reference_valid (priv->next_row_reference)) {
1356 GtkTreePath *cur, *next;
1357 /* Check that the order is still the correct one */
1358 cur = gtk_tree_row_reference_get_path (priv->row_reference);
1359 next = gtk_tree_row_reference_get_path (priv->next_row_reference);
1360 gtk_tree_path_next (cur);
1361 if (gtk_tree_path_compare (cur, next) != 0) {
1362 gtk_tree_row_reference_free (priv->next_row_reference);
1363 priv->next_row_reference = gtk_tree_row_reference_copy (priv->row_reference);
1364 select_next_valid_row (header_model, &(priv->next_row_reference), FALSE, priv->is_outbox);
1365 already_changed = TRUE;
1367 gtk_tree_path_free (cur);
1368 gtk_tree_path_free (next);
1370 if (priv->next_row_reference)
1371 gtk_tree_row_reference_free (priv->next_row_reference);
1372 /* Update next row reference */
1373 priv->next_row_reference = gtk_tree_row_reference_copy (priv->row_reference);
1374 select_next_valid_row (header_model, &(priv->next_row_reference), FALSE, priv->is_outbox);
1375 already_changed = TRUE;
1378 check_dimming_rules_after_change (window);
1381 /* The modest_msg_view_window_update_model_replaced implements update
1382 * function for ModestHeaderViewObserver. Checks whether the TnyFolder
1383 * actually belongs to the header-view is the same as the TnyFolder of
1384 * the message of msg-view or not. If they are different, there is
1385 * nothing to do. If they are the same, then the model has replaced and
1386 * the reference in msg-view shall be replaced from the old model to
1387 * the new model. In this case the view will be detached from it's
1388 * header folder. From this point the next/prev buttons are dimmed.
1391 modest_msg_view_window_update_model_replaced (ModestHeaderViewObserver *observer,
1392 GtkTreeModel *model,
1393 const gchar *tny_folder_id)
1395 ModestMsgViewWindowPrivate *priv = NULL;
1396 ModestMsgViewWindow *window = NULL;
1398 g_assert(MODEST_IS_HEADER_VIEW_OBSERVER(observer));
1399 g_assert(MODEST_IS_MSG_VIEW_WINDOW(observer));
1401 window = MODEST_MSG_VIEW_WINDOW(observer);
1402 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(window);
1404 /* If there is an other folder in the header-view then we do
1405 * not care about it's model (msg list). Else if the
1406 * header-view shows the folder the msg shown by us is in, we
1407 * shall replace our model reference and make some check. */
1408 if(model == NULL || tny_folder_id == NULL ||
1409 (priv->header_folder_id && !g_str_equal(tny_folder_id, priv->header_folder_id)))
1412 /* Model is changed(replaced), so we should forget the old
1413 * one. Because there might be other references and there
1414 * might be some change on the model even if we unreferenced
1415 * it, we need to disconnect our signals here. */
1416 if (priv->header_model) {
1417 if (g_signal_handler_is_connected(G_OBJECT (priv->header_model),
1418 priv->row_changed_handler))
1419 g_signal_handler_disconnect(G_OBJECT (priv->header_model),
1420 priv->row_changed_handler);
1421 if (g_signal_handler_is_connected(G_OBJECT (priv->header_model),
1422 priv->row_deleted_handler))
1423 g_signal_handler_disconnect(G_OBJECT (priv->header_model),
1424 priv->row_deleted_handler);
1425 if (g_signal_handler_is_connected(G_OBJECT (priv->header_model),
1426 priv->row_inserted_handler))
1427 g_signal_handler_disconnect(G_OBJECT (priv->header_model),
1428 priv->row_inserted_handler);
1429 if (g_signal_handler_is_connected(G_OBJECT (priv->header_model),
1430 priv->rows_reordered_handler))
1431 g_signal_handler_disconnect(G_OBJECT (priv->header_model),
1432 priv->rows_reordered_handler);
1435 if (priv->row_reference)
1436 gtk_tree_row_reference_free (priv->row_reference);
1437 if (priv->next_row_reference)
1438 gtk_tree_row_reference_free (priv->next_row_reference);
1439 g_object_unref(priv->header_model);
1442 priv->row_changed_handler = 0;
1443 priv->row_deleted_handler = 0;
1444 priv->row_inserted_handler = 0;
1445 priv->rows_reordered_handler = 0;
1446 priv->next_row_reference = NULL;
1447 priv->row_reference = NULL;
1448 priv->header_model = NULL;
1451 priv->header_model = g_object_ref (model);
1453 /* Also we must connect to the new model for row insertions.
1454 * Only for insertions now. We will need other ones only after
1455 * the msg is show by msg-view is added to the new model. */
1456 priv->row_inserted_handler =
1457 g_signal_connect (priv->header_model, "row-inserted",
1458 G_CALLBACK(modest_msg_view_window_on_row_inserted),
1461 modest_ui_actions_check_menu_dimming_rules(MODEST_WINDOW(window));
1462 modest_ui_actions_check_toolbar_dimming_rules(MODEST_WINDOW(window));
1466 modest_msg_view_window_toolbar_on_transfer_mode (ModestMsgViewWindow *self)
1468 ModestMsgViewWindowPrivate *priv= NULL;
1470 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self), FALSE);
1471 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
1473 return priv->progress_hint;
1477 modest_msg_view_window_get_header (ModestMsgViewWindow *self)
1479 ModestMsgViewWindowPrivate *priv= NULL;
1481 TnyHeader *header = NULL;
1482 GtkTreePath *path = NULL;
1485 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self), NULL);
1486 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
1488 /* If the message was not obtained from a treemodel,
1489 * for instance if it was opened directly by the search UI:
1491 if (priv->header_model == NULL ||
1492 priv->row_reference == NULL ||
1493 !gtk_tree_row_reference_valid (priv->row_reference)) {
1494 msg = modest_msg_view_window_get_message (self);
1496 header = tny_msg_get_header (msg);
1497 g_object_unref (msg);
1502 /* Get iter of the currently selected message in the header view: */
1503 path = gtk_tree_row_reference_get_path (priv->row_reference);
1504 g_return_val_if_fail (path != NULL, NULL);
1505 gtk_tree_model_get_iter (priv->header_model,
1509 /* Get current message header */
1510 gtk_tree_model_get (priv->header_model, &iter,
1511 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
1514 gtk_tree_path_free (path);
1519 modest_msg_view_window_get_message (ModestMsgViewWindow *self)
1521 ModestMsgViewWindowPrivate *priv;
1523 g_return_val_if_fail (self, NULL);
1525 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(self);
1527 return tny_msg_view_get_msg (TNY_MSG_VIEW (priv->msg_view));
1531 modest_msg_view_window_get_message_uid (ModestMsgViewWindow *self)
1533 ModestMsgViewWindowPrivate *priv;
1535 g_return_val_if_fail (self, NULL);
1537 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
1539 return (const gchar*) priv->msg_uid;
1542 /* Used for the Ctrl+F accelerator */
1544 modest_msg_view_window_toggle_find_toolbar (GtkWidget *obj,
1547 ModestMsgViewWindow *window = MODEST_MSG_VIEW_WINDOW (data);
1548 ModestMsgViewWindowPrivate *priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1550 if (GTK_WIDGET_VISIBLE (priv->find_toolbar)) {
1551 modest_msg_view_window_find_toolbar_close (obj, data);
1553 modest_msg_view_window_show_find_toolbar (obj, data);
1557 /* Handler for menu option */
1559 modest_msg_view_window_show_find_toolbar (GtkWidget *obj,
1562 ModestMsgViewWindow *window = MODEST_MSG_VIEW_WINDOW (data);
1563 ModestMsgViewWindowPrivate *priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1565 gtk_widget_show (priv->find_toolbar);
1566 /****** HILDON2:START */
1567 hildon_find_toolbar_highlight_entry (HILDON_FIND_TOOLBAR (priv->find_toolbar), TRUE);
1568 /****** HILDON2:END */
1571 /* Handler for click on the "X" close button in find toolbar */
1573 modest_msg_view_window_find_toolbar_close (GtkWidget *widget,
1574 ModestMsgViewWindow *obj)
1576 ModestMsgViewWindowPrivate *priv;
1578 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (obj);
1581 gtk_widget_hide (priv->find_toolbar);
1582 modest_msg_view_grab_focus (MODEST_MSG_VIEW (priv->msg_view));
1586 modest_msg_view_window_find_toolbar_search (GtkWidget *widget,
1587 ModestMsgViewWindow *obj)
1589 gchar *current_search;
1590 ModestMsgViewWindowPrivate *priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (obj);
1592 if (modest_mime_part_view_is_empty (MODEST_MIME_PART_VIEW (priv->msg_view))) {
1593 modest_platform_system_banner (NULL, NULL, _("mail_ib_nothing_to_find"));
1597 g_object_get (G_OBJECT (widget), "prefix", ¤t_search, NULL);
1599 if ((current_search == NULL) || (strcmp (current_search, "") == 0)) {
1600 g_free (current_search);
1601 modest_platform_system_banner (NULL, NULL, _CS("ecdg_ib_find_rep_enter_text"));
1605 if ((priv->last_search == NULL) || (strcmp (priv->last_search, current_search) != 0)) {
1607 g_free (priv->last_search);
1608 priv->last_search = g_strdup (current_search);
1609 result = modest_isearch_view_search (MODEST_ISEARCH_VIEW (priv->msg_view),
1612 modest_platform_system_banner (NULL, NULL,
1613 _HL("ckct_ib_find_no_matches"));
1614 g_free (priv->last_search);
1615 priv->last_search = NULL;
1617 /****** HILDON2:START */
1618 hildon_find_toolbar_highlight_entry (HILDON_FIND_TOOLBAR (priv->find_toolbar), TRUE);
1619 /****** HILDON2:END */
1622 if (!modest_isearch_view_search_next (MODEST_ISEARCH_VIEW (priv->msg_view))) {
1623 modest_platform_system_banner (NULL, NULL,
1624 _HL("ckct_ib_find_search_complete"));
1625 g_free (priv->last_search);
1626 priv->last_search = NULL;
1628 /****** HILDON2:START */
1629 hildon_find_toolbar_highlight_entry (HILDON_FIND_TOOLBAR (priv->find_toolbar), TRUE);
1630 /****** HILDON2:END */
1634 g_free (current_search);
1639 modest_msg_view_window_set_zoom (ModestWindow *window,
1642 ModestMsgViewWindowPrivate *priv;
1643 ModestWindowPrivate *parent_priv;
1645 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window));
1647 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1648 parent_priv = MODEST_WINDOW_GET_PRIVATE (window);
1649 modest_zoomable_set_zoom (MODEST_ZOOMABLE (priv->msg_view), zoom);
1654 modest_msg_view_window_get_zoom (ModestWindow *window)
1656 ModestMsgViewWindowPrivate *priv;
1658 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), 1.0);
1660 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1661 return modest_zoomable_get_zoom (MODEST_ZOOMABLE (priv->msg_view));
1665 modest_msg_view_window_zoom_plus (ModestWindow *window)
1668 ModestMsgViewWindowPrivate *priv;
1672 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), 1.0);
1673 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1675 zoom_level = modest_zoomable_get_zoom (MODEST_ZOOMABLE (priv->msg_view));
1677 if (zoom_level >= 2.0) {
1678 modest_platform_system_banner (NULL, NULL,
1679 _CS("ckct_ib_max_zoom_level_reached"));
1681 } else if (zoom_level >= 1.5) {
1683 } else if (zoom_level >= 1.2) {
1685 } else if (zoom_level >= 1.0) {
1687 } else if (zoom_level >= 0.8) {
1689 } else if (zoom_level >= 0.5) {
1695 /* set zoom level */
1696 int_zoom = (gint) rint (zoom_level*100.0+0.1);
1697 banner_text = g_strdup_printf (_HL("wdgt_ib_zoom"), int_zoom);
1698 modest_platform_information_banner (GTK_WIDGET (window), NULL, banner_text);
1699 g_free (banner_text);
1700 modest_zoomable_set_zoom (MODEST_ZOOMABLE (priv->msg_view), zoom_level);
1706 modest_msg_view_window_zoom_minus (ModestWindow *window)
1709 ModestMsgViewWindowPrivate *priv;
1713 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), 1.0);
1714 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1716 zoom_level = modest_zoomable_get_zoom (MODEST_ZOOMABLE (priv->msg_view));
1718 if (zoom_level <= 0.5) {
1719 modest_platform_system_banner (NULL, NULL,
1720 _CS("ckct_ib_min_zoom_level_reached"));
1722 } else if (zoom_level <= 0.8) {
1724 } else if (zoom_level <= 1.0) {
1726 } else if (zoom_level <= 1.2) {
1728 } else if (zoom_level <= 1.5) {
1730 } else if (zoom_level <= 2.0) {
1736 /* set zoom level */
1737 int_zoom = (gint) rint (zoom_level*100.0+0.1);
1738 banner_text = g_strdup_printf (_HL("wdgt_ib_zoom"), int_zoom);
1739 modest_platform_information_banner (GTK_WIDGET (window), NULL, banner_text);
1740 g_free (banner_text);
1741 modest_zoomable_set_zoom (MODEST_ZOOMABLE (priv->msg_view), zoom_level);
1747 modest_msg_view_window_key_event (GtkWidget *window,
1753 focus = gtk_window_get_focus (GTK_WINDOW (window));
1755 /* for the find toolbar case */
1756 if (focus && GTK_IS_ENTRY (focus)) {
1757 if (event->keyval == GDK_BackSpace) {
1759 copy = gdk_event_copy ((GdkEvent *) event);
1760 gtk_widget_event (focus, copy);
1761 gdk_event_free (copy);
1771 modest_msg_view_window_last_message_selected (ModestMsgViewWindow *window)
1774 ModestMsgViewWindowPrivate *priv;
1775 GtkTreeIter tmp_iter;
1776 gboolean is_last_selected;
1778 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), TRUE);
1779 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1781 /*if no model (so no rows at all), then virtually we are the last*/
1782 if (!priv->header_model || !priv->row_reference)
1785 if (!gtk_tree_row_reference_valid (priv->row_reference))
1788 path = gtk_tree_row_reference_get_path (priv->row_reference);
1792 is_last_selected = TRUE;
1793 while (is_last_selected) {
1795 gtk_tree_path_next (path);
1796 if (!gtk_tree_model_get_iter (priv->header_model, &tmp_iter, path))
1798 gtk_tree_model_get (priv->header_model, &tmp_iter,
1799 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
1802 if (msg_is_visible (header, priv->is_outbox))
1803 is_last_selected = FALSE;
1804 g_object_unref(G_OBJECT(header));
1807 gtk_tree_path_free (path);
1808 return is_last_selected;
1812 modest_msg_view_window_has_headers_model (ModestMsgViewWindow *window)
1814 ModestMsgViewWindowPrivate *priv;
1816 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), TRUE);
1817 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1819 return priv->header_model != NULL;
1823 modest_msg_view_window_is_search_result (ModestMsgViewWindow *window)
1825 ModestMsgViewWindowPrivate *priv;
1827 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), TRUE);
1828 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1830 return priv->is_search_result;
1834 msg_is_visible (TnyHeader *header, gboolean check_outbox)
1836 if ((tny_header_get_flags(header) & TNY_HEADER_FLAG_DELETED))
1838 if (!check_outbox) {
1841 ModestTnySendQueueStatus status;
1842 status = modest_tny_all_send_queues_get_msg_status (header);
1843 return ((status != MODEST_TNY_SEND_QUEUE_FAILED) &&
1844 (status != MODEST_TNY_SEND_QUEUE_SENDING));
1849 modest_msg_view_window_first_message_selected (ModestMsgViewWindow *window)
1852 ModestMsgViewWindowPrivate *priv;
1853 gboolean is_first_selected;
1854 GtkTreeIter tmp_iter;
1856 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), TRUE);
1857 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1859 /*if no model (so no rows at all), then virtually we are the first*/
1860 if (!priv->header_model || !priv->row_reference)
1863 if (!gtk_tree_row_reference_valid (priv->row_reference))
1866 path = gtk_tree_row_reference_get_path (priv->row_reference);
1870 is_first_selected = TRUE;
1871 while (is_first_selected) {
1873 if(!gtk_tree_path_prev (path))
1875 /* Here the 'if' is needless for logic, but let make sure
1876 * iter is valid for gtk_tree_model_get. */
1877 if (!gtk_tree_model_get_iter (priv->header_model, &tmp_iter, path))
1879 gtk_tree_model_get (priv->header_model, &tmp_iter,
1880 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
1883 if (msg_is_visible (header, priv->is_outbox))
1884 is_first_selected = FALSE;
1885 g_object_unref(G_OBJECT(header));
1888 gtk_tree_path_free (path);
1889 return is_first_selected;
1896 GtkTreeRowReference *row_reference;
1900 message_reader_performer (gboolean canceled,
1902 GtkWindow *parent_window,
1903 TnyAccount *account,
1906 ModestMailOperation *mail_op = NULL;
1907 MsgReaderInfo *info;
1909 info = (MsgReaderInfo *) user_data;
1910 if (canceled || err) {
1911 update_window_title (MODEST_MSG_VIEW_WINDOW (parent_window));
1915 /* Register the header - it'll be unregistered in the callback */
1917 modest_window_mgr_register_header (modest_runtime_get_window_mgr (), info->header, NULL);
1919 /* New mail operation */
1920 mail_op = modest_mail_operation_new_with_error_handling (G_OBJECT(parent_window),
1921 modest_ui_actions_disk_operations_error_handler,
1924 modest_mail_operation_queue_add (modest_runtime_get_mail_operation_queue (), mail_op);
1926 modest_mail_operation_get_msg (mail_op, info->header, TRUE, view_msg_cb, info->row_reference);
1928 modest_mail_operation_find_msg (mail_op, info->folder, info->msg_uid, TRUE, view_msg_cb, NULL);
1929 g_object_unref (mail_op);
1931 /* Update dimming rules */
1932 modest_ui_actions_check_toolbar_dimming_rules (MODEST_WINDOW (parent_window));
1933 modest_ui_actions_check_menu_dimming_rules (MODEST_WINDOW (parent_window));
1936 /* Frees. The row_reference will be freed by the view_msg_cb callback */
1937 g_free (info->msg_uid);
1939 g_object_unref (info->folder);
1941 g_object_unref (info->header);
1942 g_slice_free (MsgReaderInfo, info);
1947 * Reads the message whose summary item is @header. It takes care of
1948 * several things, among others:
1950 * If the message was not previously downloaded then ask the user
1951 * before downloading. If there is no connection launch the connection
1952 * dialog. Update toolbar dimming rules.
1954 * Returns: TRUE if the mail operation was started, otherwise if the
1955 * user do not want to download the message, or if the user do not
1956 * want to connect, then the operation is not issued
1959 message_reader (ModestMsgViewWindow *window,
1960 ModestMsgViewWindowPrivate *priv,
1962 const gchar *msg_uid,
1964 GtkTreeRowReference *row_reference)
1966 ModestWindowMgr *mgr;
1967 TnyAccount *account = NULL;
1968 MsgReaderInfo *info;
1970 /* We set the header from model while we're loading */
1971 tny_header_view_set_header (TNY_HEADER_VIEW (priv->msg_view), header);
1972 gtk_window_set_title (GTK_WINDOW (window), _CS("ckdg_pb_updating"));
1978 g_object_ref (folder);
1980 mgr = modest_runtime_get_window_mgr ();
1981 /* Msg download completed */
1982 if (!header || !(tny_header_get_flags (header) & TNY_HEADER_FLAG_CACHED)) {
1984 /* Ask the user if he wants to download the message if
1986 if (!tny_device_is_online (modest_runtime_get_device())) {
1987 GtkResponseType response;
1989 response = modest_platform_run_confirmation_dialog (GTK_WINDOW (window),
1990 _("mcen_nc_get_msg"));
1991 if (response == GTK_RESPONSE_CANCEL) {
1992 update_window_title (window);
1997 folder = tny_header_get_folder (header);
1999 info = g_slice_new (MsgReaderInfo);
2000 info->msg_uid = g_strdup (msg_uid);
2002 info->header = g_object_ref (header);
2004 info->header = NULL;
2006 info->folder = g_object_ref (folder);
2008 info->folder = NULL;
2009 if (row_reference) {
2010 info->row_reference = gtk_tree_row_reference_copy (row_reference);
2012 info->row_reference = NULL;
2015 /* Offer the connection dialog if necessary */
2016 modest_platform_connect_if_remote_and_perform ((GtkWindow *) window,
2018 TNY_FOLDER_STORE (folder),
2019 message_reader_performer,
2022 g_object_unref (folder);
2028 folder = tny_header_get_folder (header);
2031 account = tny_folder_get_account (folder);
2033 info = g_slice_new (MsgReaderInfo);
2034 info->msg_uid = g_strdup (msg_uid);
2036 info->folder = g_object_ref (folder);
2038 info->folder = NULL;
2040 info->header = g_object_ref (header);
2042 info->header = NULL;
2044 info->row_reference = gtk_tree_row_reference_copy (row_reference);
2046 info->row_reference = NULL;
2048 message_reader_performer (FALSE, NULL, (GtkWindow *) window, account, info);
2050 g_object_unref (account);
2052 g_object_unref (folder);
2058 modest_msg_view_window_select_next_message (ModestMsgViewWindow *window)
2060 ModestMsgViewWindowPrivate *priv;
2061 GtkTreePath *path= NULL;
2062 GtkTreeIter tmp_iter;
2064 gboolean retval = TRUE;
2065 GtkTreeRowReference *row_reference = NULL;
2067 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), FALSE);
2068 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
2070 if (!priv->row_reference)
2073 /* Update the next row reference if it's not valid. This could
2074 happen if for example the header which it was pointing to,
2075 was deleted. The best place to do it is in the row-deleted
2076 handler but the tinymail model do not work like the glib
2077 tree models and reports the deletion when the row is still
2079 if (!gtk_tree_row_reference_valid (priv->next_row_reference)) {
2080 if (priv->next_row_reference) {
2081 gtk_tree_row_reference_free (priv->next_row_reference);
2083 if (gtk_tree_row_reference_valid (priv->row_reference)) {
2084 priv->next_row_reference = gtk_tree_row_reference_copy (priv->row_reference);
2085 select_next_valid_row (priv->header_model, &(priv->next_row_reference), FALSE, priv->is_outbox);
2087 priv->next_row_reference = NULL;
2090 if (priv->next_row_reference)
2091 path = gtk_tree_row_reference_get_path (priv->next_row_reference);
2095 row_reference = gtk_tree_row_reference_copy (priv->next_row_reference);
2097 gtk_tree_model_get_iter (priv->header_model,
2100 gtk_tree_path_free (path);
2102 gtk_tree_model_get (priv->header_model, &tmp_iter,
2103 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
2106 /* Read the message & show it */
2107 if (!message_reader (window, priv, header, NULL, NULL, row_reference)) {
2110 gtk_tree_row_reference_free (row_reference);
2113 g_object_unref (header);
2119 modest_msg_view_window_select_previous_message (ModestMsgViewWindow *window)
2121 ModestMsgViewWindowPrivate *priv = NULL;
2123 gboolean finished = FALSE;
2124 gboolean retval = FALSE;
2126 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), FALSE);
2127 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
2129 if (priv->row_reference && !gtk_tree_row_reference_valid (priv->row_reference)) {
2130 gtk_tree_row_reference_free (priv->row_reference);
2131 priv->row_reference = NULL;
2134 /* Return inmediatly if there is no header model */
2135 if (!priv->header_model || !priv->row_reference)
2138 path = gtk_tree_row_reference_get_path (priv->row_reference);
2139 while (!finished && gtk_tree_path_prev (path)) {
2143 gtk_tree_model_get_iter (priv->header_model, &iter, path);
2144 gtk_tree_model_get (priv->header_model, &iter,
2145 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
2149 if (msg_is_visible (header, priv->is_outbox)) {
2150 GtkTreeRowReference *row_reference;
2151 row_reference = gtk_tree_row_reference_new (priv->header_model, path);
2152 /* Read the message & show it */
2153 retval = message_reader (window, priv, header, NULL, NULL, row_reference);
2154 gtk_tree_row_reference_free (row_reference);
2158 g_object_unref (header);
2162 gtk_tree_path_free (path);
2167 view_msg_cb (ModestMailOperation *mail_op,
2174 ModestMsgViewWindow *self = NULL;
2175 ModestMsgViewWindowPrivate *priv = NULL;
2176 GtkTreeRowReference *row_reference = NULL;
2178 /* Unregister the header (it was registered before creating the mail operation) */
2179 modest_window_mgr_unregister_header (modest_runtime_get_window_mgr (), header);
2181 row_reference = (GtkTreeRowReference *) user_data;
2184 gtk_tree_row_reference_free (row_reference);
2185 self = (ModestMsgViewWindow *) modest_mail_operation_get_source (mail_op);
2187 /* Restore window title */
2188 update_window_title (self);
2189 modest_ui_actions_on_close_window (NULL, MODEST_WINDOW (self));
2190 g_object_unref (self);
2195 /* If there was any error */
2196 if (!modest_ui_actions_msg_retrieval_check (mail_op, header, msg)) {
2198 gtk_tree_row_reference_free (row_reference);
2199 self = (ModestMsgViewWindow *) modest_mail_operation_get_source (mail_op);
2201 /* Restore window title */
2202 update_window_title (self);
2203 modest_ui_actions_on_close_window (NULL, MODEST_WINDOW (self));
2204 g_object_unref (self);
2209 /* Get the window */
2210 self = (ModestMsgViewWindow *) modest_mail_operation_get_source (mail_op);
2211 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self));
2212 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
2214 /* Update the row reference */
2215 if (priv->row_reference != NULL) {
2216 gtk_tree_row_reference_free (priv->row_reference);
2217 priv->row_reference = (row_reference && gtk_tree_row_reference_valid (row_reference))?gtk_tree_row_reference_copy (row_reference):NULL;
2218 if (priv->next_row_reference != NULL) {
2219 gtk_tree_row_reference_free (priv->next_row_reference);
2221 if (priv->row_reference) {
2222 priv->next_row_reference = gtk_tree_row_reference_copy (priv->row_reference);
2223 select_next_valid_row (priv->header_model, &(priv->next_row_reference), TRUE, priv->is_outbox);
2225 priv->next_row_reference = NULL;
2229 /* Mark header as read */
2230 if (!(tny_header_get_flags (header) & TNY_HEADER_FLAG_SEEN))
2231 tny_header_set_flag (header, TNY_HEADER_FLAG_SEEN);
2233 /* Set new message */
2234 if (priv->msg_view != NULL && TNY_IS_MSG_VIEW (priv->msg_view)) {
2235 tny_msg_view_set_msg (TNY_MSG_VIEW (priv->msg_view), msg);
2236 modest_msg_view_window_update_priority (self);
2237 update_window_title (MODEST_MSG_VIEW_WINDOW (self));
2238 update_branding (MODEST_MSG_VIEW_WINDOW (self));
2239 modest_msg_view_grab_focus (MODEST_MSG_VIEW (priv->msg_view));
2242 /* Set the new message uid of the window */
2243 if (priv->msg_uid) {
2244 g_free (priv->msg_uid);
2245 priv->msg_uid = modest_tny_folder_get_header_unique_id (header);
2248 /* Notify the observers */
2249 g_signal_emit (G_OBJECT (self), signals[MSG_CHANGED_SIGNAL],
2250 0, priv->header_model, priv->row_reference);
2252 /* Sync the flags if the message is not opened from a header
2253 model, i.e, if it's opened from a notification */
2254 if (!priv->header_model)
2258 g_object_unref (self);
2260 gtk_tree_row_reference_free (row_reference);
2264 modest_msg_view_window_get_folder_type (ModestMsgViewWindow *window)
2266 ModestMsgViewWindowPrivate *priv;
2268 TnyFolderType folder_type;
2270 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
2272 folder_type = TNY_FOLDER_TYPE_UNKNOWN;
2274 msg = tny_msg_view_get_msg (TNY_MSG_VIEW (priv->msg_view));
2278 folder = tny_msg_get_folder (msg);
2280 folder_type = modest_tny_folder_guess_folder_type (folder);
2281 g_object_unref (folder);
2283 g_object_unref (msg);
2291 modest_msg_view_window_update_priority (ModestMsgViewWindow *window)
2293 ModestMsgViewWindowPrivate *priv;
2294 TnyHeader *header = NULL;
2295 TnyHeaderFlags flags = 0;
2297 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
2299 if (priv->header_model && priv->row_reference && gtk_tree_row_reference_valid (priv->row_reference)) {
2301 GtkTreePath *path = NULL;
2303 path = gtk_tree_row_reference_get_path (priv->row_reference);
2304 g_return_if_fail (path != NULL);
2305 gtk_tree_model_get_iter (priv->header_model,
2307 gtk_tree_row_reference_get_path (priv->row_reference));
2309 gtk_tree_model_get (priv->header_model, &iter, TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
2311 gtk_tree_path_free (path);
2314 msg = tny_msg_view_get_msg (TNY_MSG_VIEW (priv->msg_view));
2316 header = tny_msg_get_header (msg);
2317 g_object_unref (msg);
2322 flags = tny_header_get_flags (header);
2323 g_object_unref(G_OBJECT(header));
2326 modest_msg_view_set_priority (MODEST_MSG_VIEW(priv->msg_view), flags);
2331 toolbar_resize (ModestMsgViewWindow *self)
2333 ModestMsgViewWindowPrivate *priv = NULL;
2334 ModestWindowPrivate *parent_priv = NULL;
2336 gint static_button_size;
2337 ModestWindowMgr *mgr;
2339 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self));
2340 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
2341 parent_priv = MODEST_WINDOW_GET_PRIVATE(self);
2343 mgr = modest_runtime_get_window_mgr ();
2344 static_button_size = modest_window_mgr_get_fullscreen_mode (mgr)?120:120;
2346 if (parent_priv->toolbar) {
2347 /* Set expandable and homogeneous tool buttons */
2348 widget = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/ToolbarMessageReply");
2349 gtk_tool_item_set_expand (GTK_TOOL_ITEM (widget), TRUE);
2350 gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (widget), TRUE);
2351 widget = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/ToolbarMessageReplyAll");
2352 gtk_tool_item_set_expand (GTK_TOOL_ITEM (widget), TRUE);
2353 gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (widget), TRUE);
2354 widget = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/ToolbarMessageForward");
2355 gtk_tool_item_set_expand (GTK_TOOL_ITEM (widget), TRUE);
2356 gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (widget), TRUE);
2357 widget = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/ToolbarDeleteMessage");
2358 gtk_tool_item_set_expand (GTK_TOOL_ITEM (widget), TRUE);
2359 gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (widget), TRUE);
2360 widget = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/ToolbarDownloadExternalImages");
2361 gtk_tool_item_set_expand (GTK_TOOL_ITEM (widget), TRUE);
2362 gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (widget), TRUE);
2363 gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (priv->next_toolitem), TRUE);
2364 gtk_tool_item_set_expand (GTK_TOOL_ITEM (priv->next_toolitem), TRUE);
2365 gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (priv->prev_toolitem), TRUE);
2366 gtk_tool_item_set_expand (GTK_TOOL_ITEM (priv->prev_toolitem), TRUE);
2371 modest_msg_view_window_show_toolbar (ModestWindow *self,
2372 gboolean show_toolbar)
2374 ModestMsgViewWindowPrivate *priv = NULL;
2375 ModestWindowPrivate *parent_priv;
2377 parent_priv = MODEST_WINDOW_GET_PRIVATE(self);
2378 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(self);
2380 /* Set optimized view status */
2381 priv->optimized_view = !show_toolbar;
2383 if (!parent_priv->toolbar) {
2384 parent_priv->toolbar = gtk_ui_manager_get_widget (parent_priv->ui_manager,
2386 gtk_toolbar_set_icon_size (GTK_TOOLBAR (parent_priv->toolbar), HILDON_ICON_SIZE_FINGER);
2387 gtk_widget_set_no_show_all (parent_priv->toolbar, TRUE);
2389 priv->next_toolitem = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/ToolbarMessageNext");
2390 priv->prev_toolitem = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/ToolbarMessageBack");
2391 toolbar_resize (MODEST_MSG_VIEW_WINDOW (self));
2393 /****** HILDON2:START
2394 * attach toolbar to window
2397 hildon_window_add_toolbar (HILDON_WINDOW (self),
2398 GTK_TOOLBAR (parent_priv->toolbar));
2399 /****** HILDON2:END */
2404 /* Quick hack: this prevents toolbar icons "dance" when progress bar show status is changed */
2405 /* TODO: resize mode migth be GTK_RESIZE_QUEUE, in order to avoid unneccesary shows */
2406 gtk_container_set_resize_mode (GTK_CONTAINER(parent_priv->toolbar), GTK_RESIZE_IMMEDIATE);
2408 gtk_widget_show (GTK_WIDGET (parent_priv->toolbar));
2409 if (modest_msg_view_window_transfer_mode_enabled (MODEST_MSG_VIEW_WINDOW (self)))
2410 set_progress_hint (MODEST_MSG_VIEW_WINDOW (self), TRUE);
2412 set_progress_hint (MODEST_MSG_VIEW_WINDOW (self), FALSE);
2415 gtk_widget_set_no_show_all (parent_priv->toolbar, TRUE);
2416 gtk_widget_hide (GTK_WIDGET (parent_priv->toolbar));
2421 modest_msg_view_window_clipboard_owner_change (GtkClipboard *clipboard,
2423 ModestMsgViewWindow *window)
2425 if (!GTK_WIDGET_VISIBLE (window))
2428 modest_window_check_dimming_rules_group (MODEST_WINDOW (window), MODEST_DIMMING_RULES_CLIPBOARD);
2432 modest_msg_view_window_transfer_mode_enabled (ModestMsgViewWindow *self)
2434 ModestMsgViewWindowPrivate *priv;
2436 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self), FALSE);
2437 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(self);
2439 return priv->progress_hint;
2443 observers_empty (ModestMsgViewWindow *self)
2446 ModestMsgViewWindowPrivate *priv;
2447 gboolean is_empty = TRUE;
2448 guint pending_ops = 0;
2450 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(self);
2451 tmp = priv->progress_widgets;
2453 /* Check all observers */
2454 while (tmp && is_empty) {
2455 pending_ops = modest_progress_object_num_pending_operations (MODEST_PROGRESS_OBJECT(tmp->data));
2456 is_empty = pending_ops == 0;
2458 tmp = g_slist_next(tmp);
2465 on_account_removed (TnyAccountStore *account_store,
2466 TnyAccount *account,
2469 /* Do nothing if it's a transport account, because we only
2470 show the messages of a store account */
2471 if (tny_account_get_account_type(account) == TNY_ACCOUNT_TYPE_STORE) {
2472 const gchar *parent_acc = NULL;
2473 const gchar *our_acc = NULL;
2475 our_acc = modest_window_get_active_account (MODEST_WINDOW (user_data));
2476 parent_acc = modest_tny_account_get_parent_modest_account_name_for_server_account (account);
2478 /* Close this window if I'm showing a message of the removed account */
2479 if (our_acc && parent_acc && strcmp (parent_acc, our_acc) == 0)
2480 modest_ui_actions_on_close_window (NULL, MODEST_WINDOW (user_data));
2485 on_mail_operation_started (ModestMailOperation *mail_op,
2488 ModestMsgViewWindow *self;
2489 ModestMailOperationTypeOperation op_type;
2491 ModestMsgViewWindowPrivate *priv;
2492 GObject *source = NULL;
2494 self = MODEST_MSG_VIEW_WINDOW (user_data);
2495 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
2496 op_type = modest_mail_operation_get_type_operation (mail_op);
2497 tmp = priv->progress_widgets;
2498 source = modest_mail_operation_get_source(mail_op);
2499 if (G_OBJECT (self) == source) {
2500 if (op_type == MODEST_MAIL_OPERATION_TYPE_RECEIVE ||
2501 op_type == MODEST_MAIL_OPERATION_TYPE_OPEN ||
2502 op_type == MODEST_MAIL_OPERATION_TYPE_DELETE) {
2503 set_progress_hint (self, TRUE);
2505 modest_progress_object_add_operation (
2506 MODEST_PROGRESS_OBJECT (tmp->data),
2508 tmp = g_slist_next (tmp);
2512 g_object_unref (source);
2514 /* Update dimming rules */
2515 check_dimming_rules_after_change (self);
2519 on_mail_operation_finished (ModestMailOperation *mail_op,
2522 ModestMsgViewWindow *self;
2523 ModestMailOperationTypeOperation op_type;
2525 ModestMsgViewWindowPrivate *priv;
2527 self = MODEST_MSG_VIEW_WINDOW (user_data);
2528 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
2529 op_type = modest_mail_operation_get_type_operation (mail_op);
2530 tmp = priv->progress_widgets;
2532 if (op_type == MODEST_MAIL_OPERATION_TYPE_RECEIVE ||
2533 op_type == MODEST_MAIL_OPERATION_TYPE_OPEN ||
2534 op_type == MODEST_MAIL_OPERATION_TYPE_DELETE) {
2536 modest_progress_object_remove_operation (MODEST_PROGRESS_OBJECT (tmp->data),
2538 tmp = g_slist_next (tmp);
2541 /* If no more operations are being observed, NORMAL mode is enabled again */
2542 if (observers_empty (self)) {
2543 set_progress_hint (self, FALSE);
2547 /* Update dimming rules. We have to do this right here
2548 and not in view_msg_cb because at that point the
2549 transfer mode is still enabled so the dimming rule
2550 won't let the user delete the message that has been
2551 readed for example */
2552 check_dimming_rules_after_change (self);
2556 on_queue_changed (ModestMailOperationQueue *queue,
2557 ModestMailOperation *mail_op,
2558 ModestMailOperationQueueNotification type,
2559 ModestMsgViewWindow *self)
2561 ModestMsgViewWindowPrivate *priv;
2563 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
2565 /* If this operations was created by another window, do nothing */
2566 if (!modest_mail_operation_is_mine (mail_op, G_OBJECT(self)))
2569 if (type == MODEST_MAIL_OPERATION_QUEUE_OPERATION_ADDED) {
2570 priv->sighandlers = modest_signal_mgr_connect (priv->sighandlers,
2572 "operation-started",
2573 G_CALLBACK (on_mail_operation_started),
2575 priv->sighandlers = modest_signal_mgr_connect (priv->sighandlers,
2577 "operation-finished",
2578 G_CALLBACK (on_mail_operation_finished),
2580 } else if (type == MODEST_MAIL_OPERATION_QUEUE_OPERATION_REMOVED) {
2581 priv->sighandlers = modest_signal_mgr_disconnect (priv->sighandlers,
2583 "operation-started");
2584 priv->sighandlers = modest_signal_mgr_disconnect (priv->sighandlers,
2586 "operation-finished");
2591 modest_msg_view_window_get_attachments (ModestMsgViewWindow *win)
2593 ModestMsgViewWindowPrivate *priv;
2594 TnyList *selected_attachments = NULL;
2596 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (win), NULL);
2597 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (win);
2599 /* In Hildon 2.2 as there's no selection we assume we have all attachments selected */
2600 selected_attachments = modest_msg_view_get_attachments (MODEST_MSG_VIEW (priv->msg_view));
2602 return selected_attachments;
2606 ModestMsgViewWindow *self;
2608 gchar *attachment_uid;
2609 } DecodeAsyncHelper;
2612 on_decode_to_stream_async_handler (TnyMimePart *mime_part,
2618 DecodeAsyncHelper *helper = (DecodeAsyncHelper *) user_data;
2619 const gchar *content_type;
2621 if (cancelled || err) {
2624 if ((err->domain == TNY_ERROR_DOMAIN) &&
2625 (err->code == TNY_IO_ERROR_WRITE) &&
2626 (errno == ENOSPC)) {
2627 msg = g_strdup_printf (_KR("cerm_device_memory_full"), "");
2629 msg = g_strdup (_("mail_ib_file_operation_failed"));
2631 modest_platform_information_banner (NULL, NULL, msg);
2637 /* It could happen that the window was closed. So we
2638 assume it is a cancelation */
2639 if (!GTK_WIDGET_VISIBLE (helper->self))
2642 /* Remove the progress hint */
2643 set_progress_hint (helper->self, FALSE);
2645 content_type = tny_mime_part_get_content_type (mime_part);
2646 if (g_str_has_prefix (content_type, "message/rfc822")) {
2647 ModestWindowMgr *mgr;
2648 ModestWindow *msg_win = NULL;
2651 const gchar *mailbox;
2652 TnyStream *file_stream;
2655 fd = g_open (helper->file_path, O_RDONLY, 0644);
2657 file_stream = tny_fs_stream_new (fd);
2659 mgr = modest_runtime_get_window_mgr ();
2661 account = g_strdup (modest_window_get_active_account (MODEST_WINDOW (helper->self)));
2662 mailbox = modest_window_get_active_mailbox (MODEST_WINDOW (helper->self));
2665 account = modest_account_mgr_get_default_account (modest_runtime_get_account_mgr ());
2667 msg = tny_camel_msg_new ();
2668 tny_camel_msg_parse (msg, file_stream);
2669 msg_win = modest_msg_view_window_new_for_attachment (TNY_MSG (msg), account, mailbox, helper->attachment_uid);
2670 modest_window_set_zoom (MODEST_WINDOW (msg_win),
2671 modest_window_get_zoom (MODEST_WINDOW (helper->self)));
2672 if (modest_window_mgr_register_window (mgr, msg_win, MODEST_WINDOW (helper->self)))
2673 gtk_widget_show_all (GTK_WIDGET (msg_win));
2675 gtk_widget_destroy (GTK_WIDGET (msg_win));
2676 g_object_unref (msg);
2677 g_object_unref (file_stream);
2679 modest_platform_information_banner (NULL, NULL, _("mail_ib_file_operation_failed"));
2684 /* make the file read-only */
2685 g_chmod(helper->file_path, 0444);
2687 /* Activate the file */
2688 modest_platform_activate_file (helper->file_path, tny_mime_part_get_content_type (mime_part));
2693 g_object_unref (helper->self);
2694 g_free (helper->file_path);
2695 g_free (helper->attachment_uid);
2696 g_slice_free (DecodeAsyncHelper, helper);
2700 modest_msg_view_window_view_attachment (ModestMsgViewWindow *window,
2701 TnyMimePart *mime_part)
2703 ModestMsgViewWindowPrivate *priv;
2704 const gchar *msg_uid;
2705 gchar *attachment_uid = NULL;
2706 gint attachment_index = 0;
2707 TnyList *attachments;
2709 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window));
2710 g_return_if_fail (TNY_IS_MIME_PART (mime_part) || (mime_part == NULL));
2711 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
2713 msg_uid = modest_msg_view_window_get_message_uid (MODEST_MSG_VIEW_WINDOW (window));
2714 attachments = modest_msg_view_get_attachments (MODEST_MSG_VIEW (priv->msg_view));
2715 attachment_index = modest_list_index (attachments, (GObject *) mime_part);
2716 g_object_unref (attachments);
2718 if (msg_uid && attachment_index >= 0) {
2719 attachment_uid = g_strdup_printf ("%s/%d", msg_uid, attachment_index);
2722 if (mime_part == NULL) {
2723 gboolean error = FALSE;
2724 TnyList *selected_attachments = modest_msg_view_get_selected_attachments (MODEST_MSG_VIEW (priv->msg_view));
2725 if (selected_attachments == NULL || tny_list_get_length (selected_attachments) == 0) {
2727 } else if (tny_list_get_length (selected_attachments) > 1) {
2728 modest_platform_system_banner (NULL, NULL, _("mcen_ib_unable_to_display_more"));
2732 iter = tny_list_create_iterator (selected_attachments);
2733 mime_part = (TnyMimePart *) tny_iterator_get_current (iter);
2734 g_object_unref (iter);
2736 if (selected_attachments)
2737 g_object_unref (selected_attachments);
2742 g_object_ref (mime_part);
2745 if (tny_mime_part_is_purged (mime_part))
2748 if (!modest_tny_mime_part_is_msg (mime_part) && tny_mime_part_get_filename (mime_part)) {
2749 gchar *filepath = NULL;
2750 const gchar *att_filename = tny_mime_part_get_filename (mime_part);
2751 gboolean show_error_banner = FALSE;
2752 TnyFsStream *temp_stream = NULL;
2753 temp_stream = modest_utils_create_temp_stream (att_filename, attachment_uid,
2756 if (temp_stream != NULL) {
2757 ModestAccountMgr *mgr;
2758 DecodeAsyncHelper *helper;
2759 gboolean decode_in_provider;
2760 ModestProtocol *protocol;
2761 const gchar *account;
2763 /* Activate progress hint */
2764 set_progress_hint (window, TRUE);
2766 helper = g_slice_new0 (DecodeAsyncHelper);
2767 helper->self = g_object_ref (window);
2768 helper->file_path = g_strdup (filepath);
2769 helper->attachment_uid = g_strdup (attachment_uid);
2771 decode_in_provider = FALSE;
2772 mgr = modest_runtime_get_account_mgr ();
2773 account = modest_window_get_active_account (MODEST_WINDOW (window));
2774 if (modest_account_mgr_account_is_multimailbox (mgr, account, &protocol)) {
2775 if (MODEST_IS_ACCOUNT_PROTOCOL (protocol)) {
2777 uri = g_strconcat ("file://", filepath, NULL);
2778 decode_in_provider =
2779 modest_account_protocol_decode_part_to_stream_async (
2780 MODEST_ACCOUNT_PROTOCOL (protocol),
2783 TNY_STREAM (temp_stream),
2784 on_decode_to_stream_async_handler,
2791 if (!decode_in_provider)
2792 tny_mime_part_decode_to_stream_async (mime_part, TNY_STREAM (temp_stream),
2793 on_decode_to_stream_async_handler,
2796 g_object_unref (temp_stream);
2797 /* NOTE: files in the temporary area will be automatically
2798 * cleaned after some time if they are no longer in use */
2801 const gchar *content_type;
2802 /* the file may already exist but it isn't writable,
2803 * let's try to open it anyway */
2804 content_type = tny_mime_part_get_content_type (mime_part);
2805 modest_platform_activate_file (filepath, content_type);
2807 g_warning ("%s: modest_utils_create_temp_stream failed", __FUNCTION__);
2808 show_error_banner = TRUE;
2813 if (show_error_banner)
2814 modest_platform_information_banner (NULL, NULL, _("mail_ib_file_operation_failed"));
2815 } else if (!modest_tny_mime_part_is_msg (mime_part)) {
2816 ModestWindowMgr *mgr;
2817 ModestWindow *msg_win = NULL;
2818 TnyMsg *current_msg;
2822 current_msg = modest_msg_view_window_get_message (MODEST_MSG_VIEW_WINDOW (window));
2823 mgr = modest_runtime_get_window_mgr ();
2824 header = tny_msg_get_header (TNY_MSG (current_msg));
2825 found = modest_window_mgr_find_registered_message_uid (mgr,
2830 g_debug ("window for this body is already being created");
2833 /* it's not found, so create a new window for it */
2834 modest_window_mgr_register_header (mgr, header, attachment_uid); /* register the uid before building the window */
2835 gchar *account = g_strdup (modest_window_get_active_account (MODEST_WINDOW (window)));
2836 const gchar *mailbox = modest_window_get_active_mailbox (MODEST_WINDOW (window));
2838 account = modest_account_mgr_get_default_account (modest_runtime_get_account_mgr ());
2840 msg_win = modest_msg_view_window_new_with_other_body (TNY_MSG (current_msg), TNY_MIME_PART (mime_part),
2841 account, mailbox, attachment_uid);
2843 modest_window_set_zoom (MODEST_WINDOW (msg_win),
2844 modest_window_get_zoom (MODEST_WINDOW (window)));
2845 if (modest_window_mgr_register_window (mgr, msg_win, MODEST_WINDOW (window)))
2846 gtk_widget_show_all (GTK_WIDGET (msg_win));
2848 gtk_widget_destroy (GTK_WIDGET (msg_win));
2850 g_object_unref (current_msg);
2852 /* message attachment */
2853 TnyHeader *header = NULL;
2854 ModestWindowMgr *mgr;
2855 ModestWindow *msg_win = NULL;
2858 header = tny_msg_get_header (TNY_MSG (mime_part));
2859 mgr = modest_runtime_get_window_mgr ();
2860 found = modest_window_mgr_find_registered_header (mgr, header, &msg_win);
2863 /* if it's found, but there is no msg_win, it's probably in the process of being created;
2864 * thus, we don't do anything */
2865 g_debug ("window for is already being created");
2867 /* it's not found, so create a new window for it */
2868 modest_window_mgr_register_header (mgr, header, attachment_uid); /* register the uid before building the window */
2869 gchar *account = g_strdup (modest_window_get_active_account (MODEST_WINDOW (window)));
2870 const gchar *mailbox = modest_window_get_active_mailbox (MODEST_WINDOW (window));
2872 account = modest_account_mgr_get_default_account (modest_runtime_get_account_mgr ());
2873 msg_win = modest_msg_view_window_new_for_attachment (TNY_MSG (mime_part), account,
2874 mailbox, attachment_uid);
2875 modest_window_set_zoom (MODEST_WINDOW (msg_win),
2876 modest_window_get_zoom (MODEST_WINDOW (window)));
2877 if (modest_window_mgr_register_window (mgr, msg_win, MODEST_WINDOW (window)))
2878 gtk_widget_show_all (GTK_WIDGET (msg_win));
2880 gtk_widget_destroy (GTK_WIDGET (msg_win));
2886 g_free (attachment_uid);
2888 g_object_unref (mime_part);
2900 GnomeVFSResult result;
2902 ModestMsgViewWindow *window;
2905 static void save_mime_part_info_free (SaveMimePartInfo *info, gboolean with_struct);
2906 static gboolean idle_save_mime_part_show_result (SaveMimePartInfo *info);
2907 static gpointer save_mime_part_to_file (SaveMimePartInfo *info);
2908 static void save_mime_parts_to_file_with_checks (GtkWindow *parent, SaveMimePartInfo *info);
2911 save_mime_part_info_free (SaveMimePartInfo *info, gboolean with_struct)
2915 for (node = info->pairs; node != NULL; node = g_list_next (node)) {
2916 SaveMimePartPair *pair = (SaveMimePartPair *) node->data;
2917 g_free (pair->filename);
2918 g_object_unref (pair->part);
2919 g_slice_free (SaveMimePartPair, pair);
2921 g_list_free (info->pairs);
2924 g_object_unref (info->window);
2925 info->window = NULL;
2927 g_slice_free (SaveMimePartInfo, info);
2932 idle_save_mime_part_show_result (SaveMimePartInfo *info)
2934 /* This is a GDK lock because we are an idle callback and
2935 * modest_platform_system_banner is or does Gtk+ code */
2937 gdk_threads_enter (); /* CHECKED */
2938 if (info->result == GNOME_VFS_OK) {
2939 modest_platform_system_banner (NULL, NULL, _CS("sfil_ib_saved"));
2940 } else if (info->result == GNOME_VFS_ERROR_NO_SPACE) {
2943 /* Check if the uri belongs to the external mmc */
2944 if (g_str_has_prefix (info->uri, g_getenv (MODEST_MMC1_VOLUMEPATH_ENV)))
2945 msg = g_strdup_printf (_KR("cerm_device_memory_full"), "");
2947 msg = g_strdup (_KR("cerm_memory_card_full"));
2948 modest_platform_information_banner (NULL, NULL, msg);
2951 modest_platform_system_banner (NULL, NULL, _("mail_ib_file_operation_failed"));
2953 save_mime_part_info_free (info, FALSE);
2954 gdk_threads_leave (); /* CHECKED */
2960 save_mime_part_to_file (SaveMimePartInfo *info)
2962 GnomeVFSHandle *handle;
2964 SaveMimePartPair *pair = (SaveMimePartPair *) info->pairs->data;
2966 info->result = gnome_vfs_create (&handle, pair->filename, GNOME_VFS_OPEN_WRITE, FALSE, 0644);
2967 if (info->result == GNOME_VFS_OK) {
2968 GError *error = NULL;
2969 gboolean decode_in_provider;
2971 ModestAccountMgr *mgr;
2972 const gchar *account;
2973 ModestProtocol *protocol = NULL;
2975 stream = tny_vfs_stream_new (handle);
2977 decode_in_provider = FALSE;
2978 mgr = modest_runtime_get_account_mgr ();
2979 account = modest_window_get_active_account (MODEST_WINDOW (info->window));
2980 if (modest_account_mgr_account_is_multimailbox (mgr, account, &protocol)) {
2981 if (MODEST_IS_ACCOUNT_PROTOCOL (protocol)) {
2982 decode_in_provider =
2983 modest_account_protocol_decode_part_to_stream (
2984 MODEST_ACCOUNT_PROTOCOL (protocol),
2992 if (!decode_in_provider)
2993 written = tny_mime_part_decode_to_stream (pair->part, stream, &error);
2996 g_warning ("modest: could not save attachment %s: %d (%s)\n", pair->filename, error?error->code:-1, error?error->message:"Unknown error");
2998 if ((error->domain == TNY_ERROR_DOMAIN) &&
2999 (error->code == TNY_IO_ERROR_WRITE) &&
3000 (errno == ENOSPC)) {
3001 info->result = GNOME_VFS_ERROR_NO_SPACE;
3003 info->result = GNOME_VFS_ERROR_IO;
3006 g_object_unref (G_OBJECT (stream));
3008 g_warning ("Could not create save attachment %s: %s\n",
3009 pair->filename, gnome_vfs_result_to_string (info->result));
3012 /* Go on saving remaining files */
3013 info->pairs = g_list_remove_link (info->pairs, info->pairs);
3014 if (info->pairs != NULL) {
3015 save_mime_part_to_file (info);
3017 g_idle_add ((GSourceFunc) idle_save_mime_part_show_result, info);
3024 save_mime_parts_to_file_with_checks (GtkWindow *parent,
3025 SaveMimePartInfo *info)
3027 gboolean is_ok = TRUE;
3028 gint replaced_files = 0;
3029 const GList *files = info->pairs;
3030 const GList *iter, *to_replace = NULL;
3032 for (iter = files; (iter != NULL) && (replaced_files < 2); iter = g_list_next(iter)) {
3033 SaveMimePartPair *pair = iter->data;
3034 gchar *unescaped = g_uri_unescape_string (pair->filename, NULL);
3036 if (modest_utils_file_exists (unescaped)) {
3038 if (replaced_files == 1)
3043 if (replaced_files) {
3046 if (replaced_files == 1) {
3047 SaveMimePartPair *pair = to_replace->data;
3048 const gchar *basename = strrchr (pair->filename, G_DIR_SEPARATOR) + 1;
3049 gchar *escaped_basename, *message;
3051 escaped_basename = g_uri_unescape_string (basename, NULL);
3052 message = g_strdup_printf ("%s\n%s",
3053 _FM("docm_nc_replace_file"),
3054 (escaped_basename) ? escaped_basename : "");
3055 response = modest_platform_run_confirmation_dialog (parent, message);
3057 g_free (escaped_basename);
3059 response = modest_platform_run_confirmation_dialog (parent,
3060 _FM("docm_nc_replace_multiple"));
3062 if (response != GTK_RESPONSE_OK)
3067 save_mime_part_info_free (info, TRUE);
3069 g_thread_create ((GThreadFunc)save_mime_part_to_file, info, FALSE, NULL);
3074 typedef struct _SaveAttachmentsInfo {
3075 TnyList *attachments_list;
3076 ModestMsgViewWindow *window;
3077 } SaveAttachmentsInfo;
3080 save_attachments_response (GtkDialog *dialog,
3084 TnyList *mime_parts;
3086 GList *files_to_save = NULL;
3087 gchar *current_folder;
3088 SaveAttachmentsInfo *sa_info = (SaveAttachmentsInfo *) user_data;
3090 mime_parts = TNY_LIST (sa_info->attachments_list);
3092 if (arg1 != GTK_RESPONSE_OK)
3095 chooser_uri = gtk_file_chooser_get_uri (GTK_FILE_CHOOSER (dialog));
3096 current_folder = gtk_file_chooser_get_current_folder_uri (GTK_FILE_CHOOSER (dialog));
3097 if (current_folder && *current_folder != '\0') {
3099 modest_conf_set_string (modest_runtime_get_conf (), MODEST_CONF_LATEST_SAVE_ATTACHMENT_PATH,
3100 current_folder,&err);
3102 g_debug ("Error storing latest used folder: %s", err->message);
3106 g_free (current_folder);
3108 if (!modest_utils_folder_writable (chooser_uri)) {
3109 const gchar *err_msg;
3111 #ifdef MODEST_PLATFORM_MAEMO
3112 if (modest_maemo_utils_in_usb_mode ()) {
3113 err_msg = dgettext ("hildon-status-bar-usb", "usbh_ib_mmc_usb_connected");
3115 err_msg = _FM("sfil_ib_readonly_location");
3118 err_msg = _FM("sfil_ib_readonly_location");
3120 modest_platform_system_banner (NULL, NULL, err_msg);
3124 iter = tny_list_create_iterator (mime_parts);
3125 while (!tny_iterator_is_done (iter)) {
3126 TnyMimePart *mime_part = (TnyMimePart *) tny_iterator_get_current (iter);
3128 if ((modest_tny_mime_part_is_attachment_for_modest (mime_part)) &&
3129 !tny_mime_part_is_purged (mime_part) &&
3130 (tny_mime_part_get_filename (mime_part) != NULL)) {
3131 SaveMimePartPair *pair;
3133 pair = g_slice_new0 (SaveMimePartPair);
3135 if (tny_list_get_length (mime_parts) > 1) {
3137 gnome_vfs_escape_slashes (tny_mime_part_get_filename (mime_part));
3138 pair->filename = g_build_filename (chooser_uri, escaped, NULL);
3141 pair->filename = g_strdup (chooser_uri);
3143 pair->part = mime_part;
3144 files_to_save = g_list_prepend (files_to_save, pair);
3146 tny_iterator_next (iter);
3148 g_object_unref (iter);
3151 if (files_to_save != NULL) {
3152 SaveMimePartInfo *info = g_slice_new0 (SaveMimePartInfo);
3153 info->pairs = files_to_save;
3154 info->result = TRUE;
3155 info->uri = g_strdup (chooser_uri);
3156 info->window = g_object_ref (sa_info->window);
3157 save_mime_parts_to_file_with_checks ((GtkWindow *) dialog, info);
3159 g_free (chooser_uri);
3162 /* Free and close the dialog */
3163 g_object_unref (mime_parts);
3164 g_object_unref (sa_info->window);
3165 g_slice_free (SaveAttachmentsInfo, sa_info);
3166 gtk_widget_destroy (GTK_WIDGET (dialog));
3170 modest_msg_view_window_save_attachments (ModestMsgViewWindow *window,
3171 TnyList *mime_parts)
3173 ModestMsgViewWindowPrivate *priv;
3174 GtkWidget *save_dialog = NULL;
3175 gchar *conf_folder = NULL;
3176 gchar *filename = NULL;
3177 gchar *save_multiple_str = NULL;
3178 const gchar *root_folder = "file:///";
3180 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window));
3181 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
3183 if (mime_parts == NULL) {
3184 /* In Hildon 2.2 save and delete operate over all the attachments as there's no
3185 * selection available */
3186 mime_parts = modest_msg_view_get_attachments (MODEST_MSG_VIEW (priv->msg_view));
3187 if (mime_parts && !modest_maemo_utils_select_attachments (GTK_WINDOW (window), mime_parts, FALSE)) {
3188 g_object_unref (mime_parts);
3191 if (mime_parts == NULL || tny_list_get_length (mime_parts) == 0) {
3193 g_object_unref (mime_parts);
3199 g_object_ref (mime_parts);
3202 /* prepare dialog */
3203 if (tny_list_get_length (mime_parts) == 1) {
3205 /* only one attachment selected */
3206 iter = tny_list_create_iterator (mime_parts);
3207 TnyMimePart *mime_part = (TnyMimePart *) tny_iterator_get_current (iter);
3208 g_object_unref (iter);
3209 if (!modest_tny_mime_part_is_msg (mime_part) &&
3210 modest_tny_mime_part_is_attachment_for_modest (mime_part) &&
3211 !tny_mime_part_is_purged (mime_part)) {
3212 filename = g_strdup (tny_mime_part_get_filename (mime_part));
3214 /* TODO: show any error? */
3215 g_warning ("%s: Tried to save a non-file attachment", __FUNCTION__);
3216 g_object_unref (mime_parts);
3219 g_object_unref (mime_part);
3221 gint num = tny_list_get_length (mime_parts);
3222 save_multiple_str = g_strdup_printf (dngettext("hildon-fm",
3223 "sfil_va_number_of_objects_attachment",
3224 "sfil_va_number_of_objects_attachments",
3228 /****** HILDON2:START
3229 * creation of hildon file chooser dialog for saving
3231 save_dialog = hildon_file_chooser_dialog_new (GTK_WINDOW (window),
3232 GTK_FILE_CHOOSER_ACTION_SAVE);
3233 /****** HILDON2:END */
3235 /* Get last used folder */
3236 conf_folder = modest_conf_get_string (modest_runtime_get_conf (),
3237 MODEST_CONF_LATEST_SAVE_ATTACHMENT_PATH, NULL);
3239 /* File chooser stops working if we select "file:///" as current folder */
3240 if (conf_folder && g_ascii_strcasecmp (root_folder, conf_folder) != 0) {
3241 g_free (conf_folder);
3245 if (conf_folder && conf_folder[0] != '\0') {
3246 gtk_file_chooser_set_current_folder_uri (GTK_FILE_CHOOSER (save_dialog), conf_folder);
3249 /* Set the default folder to documents folder */
3250 docs_folder = (gchar *) g_strdup(g_get_user_special_dir (G_USER_DIRECTORY_DOCUMENTS));
3253 docs_folder = g_build_filename (g_getenv (MYDOCS_ENV), DOCS_FOLDER, NULL);
3255 gtk_file_chooser_set_current_folder (GTK_FILE_CHOOSER (save_dialog), docs_folder);
3256 g_free (docs_folder);
3258 g_free (conf_folder);
3262 gtk_file_chooser_set_current_name (GTK_FILE_CHOOSER (save_dialog),
3267 /* if multiple, set multiple string */
3268 if (save_multiple_str) {
3269 g_object_set (G_OBJECT (save_dialog), "save-multiple", save_multiple_str, NULL);
3270 gtk_window_set_title (GTK_WINDOW (save_dialog), _FM("sfil_ti_save_objects_files"));
3271 g_free (save_multiple_str);
3274 /* We must run this asynchronously, because the hildon dialog
3275 performs a gtk_dialog_run by itself which leads to gdk
3277 SaveAttachmentsInfo *sa_info;
3278 sa_info = g_slice_new (SaveAttachmentsInfo);
3279 sa_info->attachments_list = mime_parts;
3280 sa_info->window = g_object_ref (window);
3281 g_signal_connect (save_dialog, "response",
3282 G_CALLBACK (save_attachments_response), sa_info);
3284 gtk_widget_show_all (save_dialog);
3288 show_remove_attachment_information (gpointer userdata)
3290 ModestMsgViewWindow *window = (ModestMsgViewWindow *) userdata;
3291 ModestMsgViewWindowPrivate *priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
3293 /* We're outside the main lock */
3294 gdk_threads_enter ();
3296 if (priv->remove_attachment_banner != NULL) {
3297 gtk_widget_destroy (priv->remove_attachment_banner);
3298 g_object_unref (priv->remove_attachment_banner);
3301 priv->remove_attachment_banner = g_object_ref (
3302 modest_platform_animation_banner (NULL, NULL, _("mcen_me_inbox_remove_attachments")));
3304 gdk_threads_leave ();
3310 modest_msg_view_window_remove_attachments (ModestMsgViewWindow *window, gboolean get_all)
3312 ModestMsgViewWindowPrivate *priv;
3313 TnyList *mime_parts = NULL, *tmp;
3314 gchar *confirmation_message;
3320 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window));
3321 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
3323 /* In hildon 2.2 we ignore the get_all flag as we always get all attachments. This is
3324 * because we don't have selection
3326 mime_parts = modest_msg_view_get_attachments (MODEST_MSG_VIEW (priv->msg_view));
3328 /* Remove already purged messages from mime parts list. We use
3329 a copy of the list to remove items in the original one */
3330 tmp = tny_list_copy (mime_parts);
3331 iter = tny_list_create_iterator (tmp);
3332 while (!tny_iterator_is_done (iter)) {
3333 TnyMimePart *part = TNY_MIME_PART (tny_iterator_get_current (iter));
3334 if (tny_mime_part_is_purged (part))
3335 tny_list_remove (mime_parts, (GObject *) part);
3337 g_object_unref (part);
3338 tny_iterator_next (iter);
3340 g_object_unref (tmp);
3341 g_object_unref (iter);
3343 if (!modest_maemo_utils_select_attachments (GTK_WINDOW (window), mime_parts, TRUE) ||
3344 tny_list_get_length (mime_parts) == 0) {
3345 g_object_unref (mime_parts);
3349 n_attachments = tny_list_get_length (mime_parts);
3350 if (n_attachments == 1) {
3354 iter = tny_list_create_iterator (mime_parts);
3355 part = (TnyMimePart *) tny_iterator_get_current (iter);
3356 g_object_unref (iter);
3357 if (modest_tny_mime_part_is_msg (part)) {
3359 header = tny_msg_get_header (TNY_MSG (part));
3360 filename = tny_header_dup_subject (header);
3361 g_object_unref (header);
3362 if (filename == NULL)
3363 filename = g_strdup (_("mail_va_no_subject"));
3365 filename = g_strdup (tny_mime_part_get_filename (TNY_MIME_PART (part)));
3367 confirmation_message = g_strdup_printf (_("mcen_nc_purge_file_text"), filename);
3369 g_object_unref (part);
3371 confirmation_message = g_strdup_printf (ngettext("mcen_nc_purge_file_text",
3372 "mcen_nc_purge_files_text",
3373 n_attachments), n_attachments);
3375 response = modest_platform_run_confirmation_dialog (GTK_WINDOW (window),
3376 confirmation_message);
3377 g_free (confirmation_message);
3379 if (response != GTK_RESPONSE_OK) {
3380 g_object_unref (mime_parts);
3384 priv->purge_timeout = g_timeout_add (2000, show_remove_attachment_information, window);
3386 iter = tny_list_create_iterator (mime_parts);
3387 while (!tny_iterator_is_done (iter)) {
3390 part = (TnyMimePart *) tny_iterator_get_current (iter);
3391 tny_mime_part_set_purged (TNY_MIME_PART (part));
3392 g_object_unref (part);
3393 tny_iterator_next (iter);
3395 g_object_unref (iter);
3397 msg = tny_msg_view_get_msg (TNY_MSG_VIEW (priv->msg_view));
3398 tny_msg_view_clear (TNY_MSG_VIEW (priv->msg_view));
3399 tny_msg_rewrite_cache (msg);
3400 tny_msg_view_set_msg (TNY_MSG_VIEW (priv->msg_view), msg);
3401 g_object_unref (msg);
3402 update_branding (MODEST_MSG_VIEW_WINDOW (window));
3404 g_object_unref (mime_parts);
3406 if (priv->purge_timeout > 0) {
3407 g_source_remove (priv->purge_timeout);
3408 priv->purge_timeout = 0;
3411 if (priv->remove_attachment_banner) {
3412 gtk_widget_destroy (priv->remove_attachment_banner);
3413 g_object_unref (priv->remove_attachment_banner);
3414 priv->remove_attachment_banner = NULL;
3420 update_window_title (ModestMsgViewWindow *window)
3422 ModestMsgViewWindowPrivate *priv;
3424 TnyHeader *header = NULL;
3425 gchar *subject = NULL;
3427 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
3429 /* Note that if the window is closed while we're retrieving
3430 the message, this widget could de deleted */
3431 if (!priv->msg_view)
3434 msg = tny_msg_view_get_msg (TNY_MSG_VIEW (priv->msg_view));
3436 if (priv->other_body) {
3439 description = modest_tny_mime_part_get_header_value (priv->other_body, "Content-Description");
3441 g_strstrip (description);
3442 subject = description;
3444 } else if (msg != NULL) {
3445 header = tny_msg_get_header (msg);
3446 subject = tny_header_dup_subject (header);
3447 g_object_unref (header);
3448 g_object_unref (msg);
3451 if ((subject == NULL)||(subject[0] == '\0')) {
3453 subject = g_strdup (_("mail_va_no_subject"));
3456 gtk_window_set_title (GTK_WINDOW (window), subject);
3461 on_move_focus (GtkWidget *widget,
3462 GtkDirectionType direction,
3465 g_signal_stop_emission_by_name (G_OBJECT (widget), "move-focus");
3469 fetch_image_open_stream (TnyStreamCache *self, gint64 *expected_size, gchar *uri)
3471 GnomeVFSResult result;
3472 GnomeVFSHandle *handle = NULL;
3473 GnomeVFSFileInfo *info = NULL;
3476 result = gnome_vfs_open (&handle, uri, GNOME_VFS_OPEN_READ);
3477 if (result != GNOME_VFS_OK) {
3482 info = gnome_vfs_file_info_new ();
3483 result = gnome_vfs_get_file_info_from_handle (handle, info, GNOME_VFS_FILE_INFO_DEFAULT);
3484 if (result != GNOME_VFS_OK || ! (info->valid_fields & GNOME_VFS_FILE_INFO_FIELDS_SIZE)) {
3485 /* We put a "safe" default size for going to cache */
3486 *expected_size = (300*1024);
3488 *expected_size = info->size;
3490 gnome_vfs_file_info_unref (info);
3492 stream = tny_vfs_stream_new (handle);
3501 TnyStream *output_stream;
3502 GtkWidget *msg_view;
3507 on_fetch_image_idle_refresh_view (gpointer userdata)
3510 FetchImageData *fidata = (FetchImageData *) userdata;
3512 gdk_threads_enter ();
3513 if (GTK_WIDGET_DRAWABLE (fidata->msg_view)) {
3514 ModestMsgViewWindowPrivate *priv;
3516 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (fidata->window);
3517 priv->fetching_images--;
3518 gtk_widget_queue_draw (fidata->msg_view);
3519 update_progress_hint (MODEST_MSG_VIEW_WINDOW (fidata->window));
3521 gdk_threads_leave ();
3523 g_object_unref (fidata->msg_view);
3524 g_object_unref (fidata->window);
3525 g_slice_free (FetchImageData, fidata);
3530 on_fetch_image_thread (gpointer userdata)
3532 FetchImageData *fidata = (FetchImageData *) userdata;
3533 TnyStreamCache *cache;
3534 TnyStream *cache_stream;
3536 cache = modest_runtime_get_images_cache ();
3538 tny_stream_cache_get_stream (cache,
3540 (TnyStreamCacheOpenStreamFetcher) fetch_image_open_stream,
3541 (gpointer) fidata->uri);
3542 g_free (fidata->cache_id);
3543 g_free (fidata->uri);
3545 if (cache_stream != NULL) {
3548 while (G_LIKELY (!tny_stream_is_eos (cache_stream))) {
3551 nb_read = tny_stream_read (cache_stream, buffer, sizeof (buffer));
3552 if (G_UNLIKELY (nb_read < 0)) {
3554 } else if (G_LIKELY (nb_read > 0)) {
3555 gssize nb_written = 0;
3557 while (G_UNLIKELY (nb_written < nb_read)) {
3560 len = tny_stream_write (fidata->output_stream, buffer + nb_written,
3561 nb_read - nb_written);
3562 if (G_UNLIKELY (len < 0))
3568 tny_stream_close (cache_stream);
3569 g_object_unref (cache_stream);
3572 tny_stream_close (fidata->output_stream);
3573 g_object_unref (fidata->output_stream);
3575 g_idle_add (on_fetch_image_idle_refresh_view, fidata);
3581 on_fetch_image (ModestMsgView *msgview,
3584 ModestMsgViewWindow *window)
3586 const gchar *current_account;
3587 ModestMsgViewWindowPrivate *priv;
3588 FetchImageData *fidata;
3590 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
3592 current_account = modest_window_get_active_account (MODEST_WINDOW (window));
3594 fidata = g_slice_new0 (FetchImageData);
3595 fidata->msg_view = g_object_ref (msgview);
3596 fidata->window = g_object_ref (window);
3597 fidata->uri = g_strdup (uri);
3598 fidata->cache_id = modest_images_cache_get_id (current_account, uri);
3599 fidata->output_stream = g_object_ref (stream);
3601 priv->fetching_images++;
3602 if (g_thread_create (on_fetch_image_thread, fidata, FALSE, NULL) == NULL) {
3603 g_object_unref (fidata->output_stream);
3604 g_free (fidata->cache_id);
3605 g_free (fidata->uri);
3606 g_object_unref (fidata->msg_view);
3607 g_slice_free (FetchImageData, fidata);
3608 tny_stream_close (stream);
3609 priv->fetching_images--;
3610 update_progress_hint (window);
3613 update_progress_hint (window);
3619 setup_menu (ModestMsgViewWindow *self)
3621 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW(self));
3623 /****** HILDON2:START
3624 * add actions to hildon window menu
3627 /* Settings menu buttons */
3628 modest_hildon2_window_add_to_menu (MODEST_HILDON2_WINDOW (self), _("mcen_me_viewer_find"), NULL,
3629 APP_MENU_CALLBACK (modest_msg_view_window_show_find_toolbar),
3630 MODEST_DIMMING_CALLBACK (modest_ui_dimming_rules_on_find_in_msg));
3632 modest_hildon2_window_add_to_menu (MODEST_HILDON2_WINDOW (self),
3633 dngettext(GETTEXT_PACKAGE,
3634 "mcen_me_move_message",
3635 "mcen_me_move_messages",
3638 APP_MENU_CALLBACK (modest_ui_actions_on_move_to),
3639 MODEST_DIMMING_CALLBACK (modest_ui_dimming_rules_on_move_to));
3641 modest_hildon2_window_add_to_menu (MODEST_HILDON2_WINDOW (self), _("mcen_me_inbox_mark_as_read"), NULL,
3642 APP_MENU_CALLBACK (modest_ui_actions_on_mark_as_read),
3643 MODEST_DIMMING_CALLBACK (modest_ui_dimming_rules_on_mark_as_read_msg_in_view));
3645 modest_hildon2_window_add_to_menu (MODEST_HILDON2_WINDOW (self), _("mcen_me_inbox_mark_as_unread"), NULL,
3646 APP_MENU_CALLBACK (modest_ui_actions_on_mark_as_unread),
3647 MODEST_DIMMING_CALLBACK (modest_ui_dimming_rules_on_mark_as_unread_msg_in_view));
3649 modest_hildon2_window_add_to_menu (MODEST_HILDON2_WINDOW (self), _("mcen_me_viewer_save_attachments"), NULL,
3650 APP_MENU_CALLBACK (modest_ui_actions_save_attachments),
3651 MODEST_DIMMING_CALLBACK (modest_ui_dimming_rules_on_save_attachments));
3652 modest_hildon2_window_add_to_menu (MODEST_HILDON2_WINDOW (self), _("mcen_me_inbox_remove_attachments"), NULL,
3653 APP_MENU_CALLBACK (modest_ui_actions_remove_attachments),
3654 MODEST_DIMMING_CALLBACK (modest_ui_dimming_rules_on_remove_attachments));
3656 modest_hildon2_window_add_to_menu (MODEST_HILDON2_WINDOW (self), _("mcen_me_new_message"), "<Control>n",
3657 APP_MENU_CALLBACK (modest_ui_actions_on_new_msg),
3658 MODEST_DIMMING_CALLBACK (modest_ui_dimming_rules_on_new_msg));
3659 modest_hildon2_window_add_to_menu (MODEST_HILDON2_WINDOW (self), _("mcen_me_viewer_addtocontacts"), NULL,
3660 APP_MENU_CALLBACK (modest_ui_actions_add_to_contacts),
3661 MODEST_DIMMING_CALLBACK (modest_ui_dimming_rules_on_add_to_contacts));
3663 modest_hildon2_window_add_to_menu (MODEST_HILDON2_WINDOW (self), _("mcen_ti_message_properties"), NULL,
3664 APP_MENU_CALLBACK (modest_ui_actions_on_details),
3665 MODEST_DIMMING_CALLBACK (modest_ui_dimming_rules_on_details));
3667 /****** HILDON2:END */
3671 modest_msg_view_window_add_to_contacts (ModestMsgViewWindow *self)
3673 ModestMsgViewWindowPrivate *priv;
3674 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
3675 GSList *recipients = NULL;
3677 gboolean contacts_to_add = FALSE;
3679 msg = tny_msg_view_get_msg (TNY_MSG_VIEW (priv->msg_view));
3683 header = modest_msg_view_window_get_header (self);
3686 recipients = modest_tny_msg_header_get_all_recipients_list (header);
3687 g_object_unref (header);
3689 recipients = modest_tny_msg_get_all_recipients_list (msg);
3690 g_object_unref (msg);
3693 if (recipients != NULL) {
3694 /****** HILDON2:START
3695 * shows dialog with addresses not present in addressbook. User can choose one to
3696 * add it to addressbook.
3698 GtkWidget *picker_dialog;
3699 GtkWidget *selector;
3701 gchar *selected = NULL;
3703 selector = hildon_touch_selector_new_text ();
3704 g_object_ref (selector);
3706 for (node = recipients; node != NULL; node = g_slist_next (node)) {
3707 if (!modest_address_book_has_address ((const gchar *) node->data)) {
3708 hildon_touch_selector_append_text (HILDON_TOUCH_SELECTOR (selector),
3709 (const gchar *) node->data);
3710 contacts_to_add = TRUE;
3714 if (contacts_to_add) {
3717 picker_dialog = hildon_picker_dialog_new (GTK_WINDOW (self));
3718 gtk_window_set_title (GTK_WINDOW (picker_dialog), _("mcen_me_viewer_addtocontacts"));
3720 hildon_picker_dialog_set_selector (HILDON_PICKER_DIALOG (picker_dialog),
3721 HILDON_TOUCH_SELECTOR (selector));
3723 picker_result = gtk_dialog_run (GTK_DIALOG (picker_dialog));
3725 if (picker_result == GTK_RESPONSE_OK) {
3726 selected = hildon_touch_selector_get_current_text (HILDON_TOUCH_SELECTOR (selector));
3728 gtk_widget_destroy (picker_dialog);
3731 modest_address_book_add_address (selected, (GtkWindow *) self);
3736 g_object_unref (selector);
3739 /****** HILDON2:END */
3742 if (recipients) {g_slist_foreach (recipients, (GFunc) g_free, NULL); g_slist_free (recipients);}
3746 _modest_msg_view_window_map_event (GtkWidget *widget,
3750 ModestMsgViewWindow *self = (ModestMsgViewWindow *) userdata;
3752 update_progress_hint (self);
3758 modest_msg_view_window_fetch_images (ModestMsgViewWindow *self)
3760 ModestMsgViewWindowPrivate *priv;
3761 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
3763 modest_msg_view_request_fetch_images (MODEST_MSG_VIEW (priv->msg_view));
3767 modest_msg_view_window_has_blocked_external_images (ModestMsgViewWindow *self)
3769 ModestMsgViewWindowPrivate *priv;
3770 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
3772 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self), FALSE);
3774 return modest_msg_view_has_blocked_external_images (MODEST_MSG_VIEW (priv->msg_view));
3778 modest_msg_view_window_reload (ModestMsgViewWindow *self)
3780 ModestMsgViewWindowPrivate *priv;
3781 const gchar *msg_uid;
3782 TnyHeader *header = NULL;
3783 TnyFolder *folder = NULL;
3785 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self));
3787 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
3789 header = modest_msg_view_window_get_header (MODEST_MSG_VIEW_WINDOW (self));
3793 folder = tny_header_get_folder (header);
3794 g_object_unref (header);
3799 msg_uid = modest_msg_view_window_get_message_uid (self);
3801 GtkTreeRowReference *row_reference;
3803 if (priv->row_reference && gtk_tree_row_reference_valid (priv->row_reference)) {
3804 row_reference = priv->row_reference;
3806 row_reference = NULL;
3808 if (!message_reader (self, priv, NULL, msg_uid, folder, row_reference))
3809 g_warning ("Shouldn't happen, trying to reload a message failed");
3812 g_object_unref (folder);
3816 update_branding (ModestMsgViewWindow *self)
3818 const gchar *account;
3819 const gchar *mailbox;
3820 ModestAccountMgr *mgr;
3821 ModestProtocol *protocol = NULL;
3822 gchar *service_name = NULL;
3823 const GdkPixbuf *service_icon = NULL;
3824 ModestMsgViewWindowPrivate *priv;
3826 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
3828 account = modest_window_get_active_account (MODEST_WINDOW (self));
3829 mailbox = modest_window_get_active_mailbox (MODEST_WINDOW (self));
3831 mgr = modest_runtime_get_account_mgr ();
3833 if (modest_account_mgr_account_is_multimailbox (mgr, account, &protocol)) {
3834 if (MODEST_IS_ACCOUNT_PROTOCOL (protocol)) {
3835 service_name = modest_account_protocol_get_service_name (MODEST_ACCOUNT_PROTOCOL (protocol),
3837 service_icon = modest_account_protocol_get_service_icon (MODEST_ACCOUNT_PROTOCOL (protocol),
3838 account, mailbox, MODEST_ICON_SIZE_SMALL);
3842 modest_msg_view_set_branding (MODEST_MSG_VIEW (priv->msg_view), service_name, service_icon);
3843 g_free (service_name);
3847 sync_flags (ModestMsgViewWindow *self)
3849 TnyHeader *header = NULL;
3851 header = modest_msg_view_window_get_header (self);
3853 TnyMsg *msg = modest_msg_view_window_get_message (self);
3855 header = tny_msg_get_header (msg);
3856 g_object_unref (msg);
3861 TnyFolder *folder = tny_header_get_folder (header);
3864 ModestMailOperation *mail_op;
3866 /* Sync folder, we need this to save the seen flag */
3867 mail_op = modest_mail_operation_new (NULL);
3868 modest_mail_operation_queue_add (modest_runtime_get_mail_operation_queue (),
3870 modest_mail_operation_sync_folder (mail_op, folder, FALSE, NULL, NULL);
3871 g_object_unref (mail_op);
3872 g_object_unref (folder);
3874 g_object_unref (header);