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 modest_window_add_toolbar (MODEST_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 modest_window_set_title (MODEST_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 modest_window_add_toolbar (MODEST_WINDOW (self),
2394 GTK_TOOLBAR (parent_priv->toolbar));
2399 /* Quick hack: this prevents toolbar icons "dance" when progress bar show status is changed */
2400 /* TODO: resize mode migth be GTK_RESIZE_QUEUE, in order to avoid unneccesary shows */
2401 gtk_container_set_resize_mode (GTK_CONTAINER(parent_priv->toolbar), GTK_RESIZE_IMMEDIATE);
2403 gtk_widget_show (GTK_WIDGET (parent_priv->toolbar));
2404 if (modest_msg_view_window_transfer_mode_enabled (MODEST_MSG_VIEW_WINDOW (self)))
2405 set_progress_hint (MODEST_MSG_VIEW_WINDOW (self), TRUE);
2407 set_progress_hint (MODEST_MSG_VIEW_WINDOW (self), FALSE);
2410 gtk_widget_set_no_show_all (parent_priv->toolbar, TRUE);
2411 gtk_widget_hide (GTK_WIDGET (parent_priv->toolbar));
2416 modest_msg_view_window_clipboard_owner_change (GtkClipboard *clipboard,
2418 ModestMsgViewWindow *window)
2420 if (!GTK_WIDGET_VISIBLE (window))
2423 modest_window_check_dimming_rules_group (MODEST_WINDOW (window), MODEST_DIMMING_RULES_CLIPBOARD);
2427 modest_msg_view_window_transfer_mode_enabled (ModestMsgViewWindow *self)
2429 ModestMsgViewWindowPrivate *priv;
2431 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self), FALSE);
2432 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(self);
2434 return priv->progress_hint;
2438 observers_empty (ModestMsgViewWindow *self)
2441 ModestMsgViewWindowPrivate *priv;
2442 gboolean is_empty = TRUE;
2443 guint pending_ops = 0;
2445 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(self);
2446 tmp = priv->progress_widgets;
2448 /* Check all observers */
2449 while (tmp && is_empty) {
2450 pending_ops = modest_progress_object_num_pending_operations (MODEST_PROGRESS_OBJECT(tmp->data));
2451 is_empty = pending_ops == 0;
2453 tmp = g_slist_next(tmp);
2460 on_account_removed (TnyAccountStore *account_store,
2461 TnyAccount *account,
2464 /* Do nothing if it's a transport account, because we only
2465 show the messages of a store account */
2466 if (tny_account_get_account_type(account) == TNY_ACCOUNT_TYPE_STORE) {
2467 const gchar *parent_acc = NULL;
2468 const gchar *our_acc = NULL;
2470 our_acc = modest_window_get_active_account (MODEST_WINDOW (user_data));
2471 parent_acc = modest_tny_account_get_parent_modest_account_name_for_server_account (account);
2473 /* Close this window if I'm showing a message of the removed account */
2474 if (our_acc && parent_acc && strcmp (parent_acc, our_acc) == 0)
2475 modest_ui_actions_on_close_window (NULL, MODEST_WINDOW (user_data));
2480 on_mail_operation_started (ModestMailOperation *mail_op,
2483 ModestMsgViewWindow *self;
2484 ModestMailOperationTypeOperation op_type;
2486 ModestMsgViewWindowPrivate *priv;
2487 GObject *source = NULL;
2489 self = MODEST_MSG_VIEW_WINDOW (user_data);
2490 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
2491 op_type = modest_mail_operation_get_type_operation (mail_op);
2492 tmp = priv->progress_widgets;
2493 source = modest_mail_operation_get_source(mail_op);
2494 if (G_OBJECT (self) == source) {
2495 if (op_type == MODEST_MAIL_OPERATION_TYPE_RECEIVE ||
2496 op_type == MODEST_MAIL_OPERATION_TYPE_OPEN ||
2497 op_type == MODEST_MAIL_OPERATION_TYPE_DELETE) {
2498 set_progress_hint (self, TRUE);
2500 modest_progress_object_add_operation (
2501 MODEST_PROGRESS_OBJECT (tmp->data),
2503 tmp = g_slist_next (tmp);
2507 g_object_unref (source);
2509 /* Update dimming rules */
2510 check_dimming_rules_after_change (self);
2514 on_mail_operation_finished (ModestMailOperation *mail_op,
2517 ModestMsgViewWindow *self;
2518 ModestMailOperationTypeOperation op_type;
2520 ModestMsgViewWindowPrivate *priv;
2522 self = MODEST_MSG_VIEW_WINDOW (user_data);
2523 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
2524 op_type = modest_mail_operation_get_type_operation (mail_op);
2525 tmp = priv->progress_widgets;
2527 if (op_type == MODEST_MAIL_OPERATION_TYPE_RECEIVE ||
2528 op_type == MODEST_MAIL_OPERATION_TYPE_OPEN ||
2529 op_type == MODEST_MAIL_OPERATION_TYPE_DELETE) {
2531 modest_progress_object_remove_operation (MODEST_PROGRESS_OBJECT (tmp->data),
2533 tmp = g_slist_next (tmp);
2536 /* If no more operations are being observed, NORMAL mode is enabled again */
2537 if (observers_empty (self)) {
2538 set_progress_hint (self, FALSE);
2542 /* Update dimming rules. We have to do this right here
2543 and not in view_msg_cb because at that point the
2544 transfer mode is still enabled so the dimming rule
2545 won't let the user delete the message that has been
2546 readed for example */
2547 check_dimming_rules_after_change (self);
2551 on_queue_changed (ModestMailOperationQueue *queue,
2552 ModestMailOperation *mail_op,
2553 ModestMailOperationQueueNotification type,
2554 ModestMsgViewWindow *self)
2556 ModestMsgViewWindowPrivate *priv;
2558 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
2560 /* If this operations was created by another window, do nothing */
2561 if (!modest_mail_operation_is_mine (mail_op, G_OBJECT(self)))
2564 if (type == MODEST_MAIL_OPERATION_QUEUE_OPERATION_ADDED) {
2565 priv->sighandlers = modest_signal_mgr_connect (priv->sighandlers,
2567 "operation-started",
2568 G_CALLBACK (on_mail_operation_started),
2570 priv->sighandlers = modest_signal_mgr_connect (priv->sighandlers,
2572 "operation-finished",
2573 G_CALLBACK (on_mail_operation_finished),
2575 } else if (type == MODEST_MAIL_OPERATION_QUEUE_OPERATION_REMOVED) {
2576 priv->sighandlers = modest_signal_mgr_disconnect (priv->sighandlers,
2578 "operation-started");
2579 priv->sighandlers = modest_signal_mgr_disconnect (priv->sighandlers,
2581 "operation-finished");
2586 modest_msg_view_window_get_attachments (ModestMsgViewWindow *win)
2588 ModestMsgViewWindowPrivate *priv;
2589 TnyList *selected_attachments = NULL;
2591 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (win), NULL);
2592 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (win);
2594 /* In Hildon 2.2 as there's no selection we assume we have all attachments selected */
2595 selected_attachments = modest_msg_view_get_attachments (MODEST_MSG_VIEW (priv->msg_view));
2597 return selected_attachments;
2601 ModestMsgViewWindow *self;
2603 gchar *attachment_uid;
2604 } DecodeAsyncHelper;
2607 on_decode_to_stream_async_handler (TnyMimePart *mime_part,
2613 DecodeAsyncHelper *helper = (DecodeAsyncHelper *) user_data;
2614 const gchar *content_type;
2616 if (cancelled || err) {
2619 if ((err->domain == TNY_ERROR_DOMAIN) &&
2620 (err->code == TNY_IO_ERROR_WRITE) &&
2621 (errno == ENOSPC)) {
2622 msg = g_strdup_printf (_KR("cerm_device_memory_full"), "");
2624 msg = g_strdup (_("mail_ib_file_operation_failed"));
2626 modest_platform_information_banner (NULL, NULL, msg);
2632 /* It could happen that the window was closed. So we
2633 assume it is a cancelation */
2634 if (!GTK_WIDGET_VISIBLE (helper->self))
2637 /* Remove the progress hint */
2638 set_progress_hint (helper->self, FALSE);
2640 content_type = tny_mime_part_get_content_type (mime_part);
2641 if (g_str_has_prefix (content_type, "message/rfc822")) {
2642 ModestWindowMgr *mgr;
2643 ModestWindow *msg_win = NULL;
2646 const gchar *mailbox;
2647 TnyStream *file_stream;
2650 fd = g_open (helper->file_path, O_RDONLY, 0644);
2652 file_stream = tny_fs_stream_new (fd);
2654 mgr = modest_runtime_get_window_mgr ();
2656 account = g_strdup (modest_window_get_active_account (MODEST_WINDOW (helper->self)));
2657 mailbox = modest_window_get_active_mailbox (MODEST_WINDOW (helper->self));
2660 account = modest_account_mgr_get_default_account (modest_runtime_get_account_mgr ());
2662 msg = tny_camel_msg_new ();
2663 tny_camel_msg_parse (msg, file_stream);
2664 msg_win = modest_msg_view_window_new_for_attachment (TNY_MSG (msg), account, mailbox, helper->attachment_uid);
2665 modest_window_set_zoom (MODEST_WINDOW (msg_win),
2666 modest_window_get_zoom (MODEST_WINDOW (helper->self)));
2667 if (modest_window_mgr_register_window (mgr, msg_win, MODEST_WINDOW (helper->self)))
2668 gtk_widget_show_all (GTK_WIDGET (msg_win));
2670 gtk_widget_destroy (GTK_WIDGET (msg_win));
2671 g_object_unref (msg);
2672 g_object_unref (file_stream);
2674 modest_platform_information_banner (NULL, NULL, _("mail_ib_file_operation_failed"));
2679 /* make the file read-only */
2680 g_chmod(helper->file_path, 0444);
2682 /* Activate the file */
2683 modest_platform_activate_file (helper->file_path, tny_mime_part_get_content_type (mime_part));
2688 g_object_unref (helper->self);
2689 g_free (helper->file_path);
2690 g_free (helper->attachment_uid);
2691 g_slice_free (DecodeAsyncHelper, helper);
2695 modest_msg_view_window_view_attachment (ModestMsgViewWindow *window,
2696 TnyMimePart *mime_part)
2698 ModestMsgViewWindowPrivate *priv;
2699 const gchar *msg_uid;
2700 gchar *attachment_uid = NULL;
2701 gint attachment_index = 0;
2702 TnyList *attachments;
2704 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window));
2705 g_return_if_fail (TNY_IS_MIME_PART (mime_part) || (mime_part == NULL));
2706 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
2708 msg_uid = modest_msg_view_window_get_message_uid (MODEST_MSG_VIEW_WINDOW (window));
2709 attachments = modest_msg_view_get_attachments (MODEST_MSG_VIEW (priv->msg_view));
2710 attachment_index = modest_list_index (attachments, (GObject *) mime_part);
2711 g_object_unref (attachments);
2713 if (msg_uid && attachment_index >= 0) {
2714 attachment_uid = g_strdup_printf ("%s/%d", msg_uid, attachment_index);
2717 if (mime_part == NULL) {
2718 gboolean error = FALSE;
2719 TnyList *selected_attachments = modest_msg_view_get_selected_attachments (MODEST_MSG_VIEW (priv->msg_view));
2720 if (selected_attachments == NULL || tny_list_get_length (selected_attachments) == 0) {
2722 } else if (tny_list_get_length (selected_attachments) > 1) {
2723 modest_platform_system_banner (NULL, NULL, _("mcen_ib_unable_to_display_more"));
2727 iter = tny_list_create_iterator (selected_attachments);
2728 mime_part = (TnyMimePart *) tny_iterator_get_current (iter);
2729 g_object_unref (iter);
2731 if (selected_attachments)
2732 g_object_unref (selected_attachments);
2737 g_object_ref (mime_part);
2740 if (tny_mime_part_is_purged (mime_part))
2743 if (!modest_tny_mime_part_is_msg (mime_part) && tny_mime_part_get_filename (mime_part)) {
2744 gchar *filepath = NULL;
2745 const gchar *att_filename = tny_mime_part_get_filename (mime_part);
2746 gboolean show_error_banner = FALSE;
2747 TnyFsStream *temp_stream = NULL;
2748 temp_stream = modest_utils_create_temp_stream (att_filename, attachment_uid,
2751 if (temp_stream != NULL) {
2752 ModestAccountMgr *mgr;
2753 DecodeAsyncHelper *helper;
2754 gboolean decode_in_provider;
2755 ModestProtocol *protocol;
2756 const gchar *account;
2758 /* Activate progress hint */
2759 set_progress_hint (window, TRUE);
2761 helper = g_slice_new0 (DecodeAsyncHelper);
2762 helper->self = g_object_ref (window);
2763 helper->file_path = g_strdup (filepath);
2764 helper->attachment_uid = g_strdup (attachment_uid);
2766 decode_in_provider = FALSE;
2767 mgr = modest_runtime_get_account_mgr ();
2768 account = modest_window_get_active_account (MODEST_WINDOW (window));
2769 if (modest_account_mgr_account_is_multimailbox (mgr, account, &protocol)) {
2770 if (MODEST_IS_ACCOUNT_PROTOCOL (protocol)) {
2772 uri = g_strconcat ("file://", filepath, NULL);
2773 decode_in_provider =
2774 modest_account_protocol_decode_part_to_stream_async (
2775 MODEST_ACCOUNT_PROTOCOL (protocol),
2778 TNY_STREAM (temp_stream),
2779 on_decode_to_stream_async_handler,
2786 if (!decode_in_provider)
2787 tny_mime_part_decode_to_stream_async (mime_part, TNY_STREAM (temp_stream),
2788 on_decode_to_stream_async_handler,
2791 g_object_unref (temp_stream);
2792 /* NOTE: files in the temporary area will be automatically
2793 * cleaned after some time if they are no longer in use */
2796 const gchar *content_type;
2797 /* the file may already exist but it isn't writable,
2798 * let's try to open it anyway */
2799 content_type = tny_mime_part_get_content_type (mime_part);
2800 modest_platform_activate_file (filepath, content_type);
2802 g_warning ("%s: modest_utils_create_temp_stream failed", __FUNCTION__);
2803 show_error_banner = TRUE;
2808 if (show_error_banner)
2809 modest_platform_information_banner (NULL, NULL, _("mail_ib_file_operation_failed"));
2810 } else if (!modest_tny_mime_part_is_msg (mime_part)) {
2811 ModestWindowMgr *mgr;
2812 ModestWindow *msg_win = NULL;
2813 TnyMsg *current_msg;
2817 current_msg = modest_msg_view_window_get_message (MODEST_MSG_VIEW_WINDOW (window));
2818 mgr = modest_runtime_get_window_mgr ();
2819 header = tny_msg_get_header (TNY_MSG (current_msg));
2820 found = modest_window_mgr_find_registered_message_uid (mgr,
2825 g_debug ("window for this body is already being created");
2828 /* it's not found, so create a new window for it */
2829 modest_window_mgr_register_header (mgr, header, attachment_uid); /* register the uid before building the window */
2830 gchar *account = g_strdup (modest_window_get_active_account (MODEST_WINDOW (window)));
2831 const gchar *mailbox = modest_window_get_active_mailbox (MODEST_WINDOW (window));
2833 account = modest_account_mgr_get_default_account (modest_runtime_get_account_mgr ());
2835 msg_win = modest_msg_view_window_new_with_other_body (TNY_MSG (current_msg), TNY_MIME_PART (mime_part),
2836 account, mailbox, attachment_uid);
2838 modest_window_set_zoom (MODEST_WINDOW (msg_win),
2839 modest_window_get_zoom (MODEST_WINDOW (window)));
2840 if (modest_window_mgr_register_window (mgr, msg_win, MODEST_WINDOW (window)))
2841 gtk_widget_show_all (GTK_WIDGET (msg_win));
2843 gtk_widget_destroy (GTK_WIDGET (msg_win));
2845 g_object_unref (current_msg);
2847 /* message attachment */
2848 TnyHeader *header = NULL;
2849 ModestWindowMgr *mgr;
2850 ModestWindow *msg_win = NULL;
2853 header = tny_msg_get_header (TNY_MSG (mime_part));
2854 mgr = modest_runtime_get_window_mgr ();
2855 found = modest_window_mgr_find_registered_header (mgr, header, &msg_win);
2858 /* if it's found, but there is no msg_win, it's probably in the process of being created;
2859 * thus, we don't do anything */
2860 g_debug ("window for is already being created");
2862 /* it's not found, so create a new window for it */
2863 modest_window_mgr_register_header (mgr, header, attachment_uid); /* register the uid before building the window */
2864 gchar *account = g_strdup (modest_window_get_active_account (MODEST_WINDOW (window)));
2865 const gchar *mailbox = modest_window_get_active_mailbox (MODEST_WINDOW (window));
2867 account = modest_account_mgr_get_default_account (modest_runtime_get_account_mgr ());
2868 msg_win = modest_msg_view_window_new_for_attachment (TNY_MSG (mime_part), account,
2869 mailbox, attachment_uid);
2870 modest_window_set_zoom (MODEST_WINDOW (msg_win),
2871 modest_window_get_zoom (MODEST_WINDOW (window)));
2872 if (modest_window_mgr_register_window (mgr, msg_win, MODEST_WINDOW (window)))
2873 gtk_widget_show_all (GTK_WIDGET (msg_win));
2875 gtk_widget_destroy (GTK_WIDGET (msg_win));
2881 g_free (attachment_uid);
2883 g_object_unref (mime_part);
2895 GnomeVFSResult result;
2897 ModestMsgViewWindow *window;
2900 static void save_mime_part_info_free (SaveMimePartInfo *info, gboolean with_struct);
2901 static gboolean idle_save_mime_part_show_result (SaveMimePartInfo *info);
2902 static gpointer save_mime_part_to_file (SaveMimePartInfo *info);
2903 static void save_mime_parts_to_file_with_checks (GtkWindow *parent, SaveMimePartInfo *info);
2906 save_mime_part_info_free (SaveMimePartInfo *info, gboolean with_struct)
2910 for (node = info->pairs; node != NULL; node = g_list_next (node)) {
2911 SaveMimePartPair *pair = (SaveMimePartPair *) node->data;
2912 g_free (pair->filename);
2913 g_object_unref (pair->part);
2914 g_slice_free (SaveMimePartPair, pair);
2916 g_list_free (info->pairs);
2919 g_object_unref (info->window);
2920 info->window = NULL;
2922 g_slice_free (SaveMimePartInfo, info);
2927 idle_save_mime_part_show_result (SaveMimePartInfo *info)
2929 /* This is a GDK lock because we are an idle callback and
2930 * modest_platform_system_banner is or does Gtk+ code */
2932 gdk_threads_enter (); /* CHECKED */
2933 if (info->result == GNOME_VFS_OK) {
2934 modest_platform_system_banner (NULL, NULL, _CS("sfil_ib_saved"));
2935 } else if (info->result == GNOME_VFS_ERROR_NO_SPACE) {
2938 /* Check if the uri belongs to the external mmc */
2939 if (g_str_has_prefix (info->uri, g_getenv (MODEST_MMC1_VOLUMEPATH_ENV)))
2940 msg = g_strdup_printf (_KR("cerm_device_memory_full"), "");
2942 msg = g_strdup (_KR("cerm_memory_card_full"));
2943 modest_platform_information_banner (NULL, NULL, msg);
2946 modest_platform_system_banner (NULL, NULL, _("mail_ib_file_operation_failed"));
2948 save_mime_part_info_free (info, FALSE);
2949 gdk_threads_leave (); /* CHECKED */
2955 save_mime_part_to_file (SaveMimePartInfo *info)
2957 GnomeVFSHandle *handle;
2959 SaveMimePartPair *pair = (SaveMimePartPair *) info->pairs->data;
2961 info->result = gnome_vfs_create (&handle, pair->filename, GNOME_VFS_OPEN_WRITE, FALSE, 0644);
2962 if (info->result == GNOME_VFS_OK) {
2963 GError *error = NULL;
2964 gboolean decode_in_provider;
2966 ModestAccountMgr *mgr;
2967 const gchar *account;
2968 ModestProtocol *protocol = NULL;
2970 stream = tny_vfs_stream_new (handle);
2972 decode_in_provider = FALSE;
2973 mgr = modest_runtime_get_account_mgr ();
2974 account = modest_window_get_active_account (MODEST_WINDOW (info->window));
2975 if (modest_account_mgr_account_is_multimailbox (mgr, account, &protocol)) {
2976 if (MODEST_IS_ACCOUNT_PROTOCOL (protocol)) {
2977 decode_in_provider =
2978 modest_account_protocol_decode_part_to_stream (
2979 MODEST_ACCOUNT_PROTOCOL (protocol),
2987 if (!decode_in_provider)
2988 written = tny_mime_part_decode_to_stream (pair->part, stream, &error);
2991 g_warning ("modest: could not save attachment %s: %d (%s)\n", pair->filename, error?error->code:-1, error?error->message:"Unknown error");
2993 if ((error->domain == TNY_ERROR_DOMAIN) &&
2994 (error->code == TNY_IO_ERROR_WRITE) &&
2995 (errno == ENOSPC)) {
2996 info->result = GNOME_VFS_ERROR_NO_SPACE;
2998 info->result = GNOME_VFS_ERROR_IO;
3001 g_object_unref (G_OBJECT (stream));
3003 g_warning ("Could not create save attachment %s: %s\n",
3004 pair->filename, gnome_vfs_result_to_string (info->result));
3007 /* Go on saving remaining files */
3008 info->pairs = g_list_remove_link (info->pairs, info->pairs);
3009 if (info->pairs != NULL) {
3010 save_mime_part_to_file (info);
3012 g_idle_add ((GSourceFunc) idle_save_mime_part_show_result, info);
3019 save_mime_parts_to_file_with_checks (GtkWindow *parent,
3020 SaveMimePartInfo *info)
3022 gboolean is_ok = TRUE;
3023 gint replaced_files = 0;
3024 const GList *files = info->pairs;
3025 const GList *iter, *to_replace = NULL;
3027 for (iter = files; (iter != NULL) && (replaced_files < 2); iter = g_list_next(iter)) {
3028 SaveMimePartPair *pair = iter->data;
3029 gchar *unescaped = g_uri_unescape_string (pair->filename, NULL);
3031 if (modest_utils_file_exists (unescaped)) {
3033 if (replaced_files == 1)
3038 if (replaced_files) {
3041 if (replaced_files == 1) {
3042 SaveMimePartPair *pair = to_replace->data;
3043 const gchar *basename = strrchr (pair->filename, G_DIR_SEPARATOR) + 1;
3044 gchar *escaped_basename, *message;
3046 escaped_basename = g_uri_unescape_string (basename, NULL);
3047 message = g_strdup_printf ("%s\n%s",
3048 _FM("docm_nc_replace_file"),
3049 (escaped_basename) ? escaped_basename : "");
3050 response = modest_platform_run_confirmation_dialog (parent, message);
3052 g_free (escaped_basename);
3054 response = modest_platform_run_confirmation_dialog (parent,
3055 _FM("docm_nc_replace_multiple"));
3057 if (response != GTK_RESPONSE_OK)
3062 save_mime_part_info_free (info, TRUE);
3064 g_thread_create ((GThreadFunc)save_mime_part_to_file, info, FALSE, NULL);
3069 typedef struct _SaveAttachmentsInfo {
3070 TnyList *attachments_list;
3071 ModestMsgViewWindow *window;
3072 } SaveAttachmentsInfo;
3075 save_attachments_response (GtkDialog *dialog,
3079 TnyList *mime_parts;
3081 GList *files_to_save = NULL;
3082 gchar *current_folder;
3083 SaveAttachmentsInfo *sa_info = (SaveAttachmentsInfo *) user_data;
3085 mime_parts = TNY_LIST (sa_info->attachments_list);
3087 if (arg1 != GTK_RESPONSE_OK)
3090 chooser_uri = gtk_file_chooser_get_uri (GTK_FILE_CHOOSER (dialog));
3091 current_folder = gtk_file_chooser_get_current_folder_uri (GTK_FILE_CHOOSER (dialog));
3092 if (current_folder && *current_folder != '\0') {
3094 modest_conf_set_string (modest_runtime_get_conf (), MODEST_CONF_LATEST_SAVE_ATTACHMENT_PATH,
3095 current_folder,&err);
3097 g_debug ("Error storing latest used folder: %s", err->message);
3101 g_free (current_folder);
3103 if (!modest_utils_folder_writable (chooser_uri)) {
3104 const gchar *err_msg;
3106 #ifdef MODEST_PLATFORM_MAEMO
3107 if (modest_maemo_utils_in_usb_mode ()) {
3108 err_msg = dgettext ("hildon-status-bar-usb", "usbh_ib_mmc_usb_connected");
3110 err_msg = _FM("sfil_ib_readonly_location");
3113 err_msg = _FM("sfil_ib_readonly_location");
3115 modest_platform_system_banner (NULL, NULL, err_msg);
3119 iter = tny_list_create_iterator (mime_parts);
3120 while (!tny_iterator_is_done (iter)) {
3121 TnyMimePart *mime_part = (TnyMimePart *) tny_iterator_get_current (iter);
3123 if ((modest_tny_mime_part_is_attachment_for_modest (mime_part)) &&
3124 !tny_mime_part_is_purged (mime_part) &&
3125 (tny_mime_part_get_filename (mime_part) != NULL)) {
3126 SaveMimePartPair *pair;
3128 pair = g_slice_new0 (SaveMimePartPair);
3130 if (tny_list_get_length (mime_parts) > 1) {
3132 gnome_vfs_escape_slashes (tny_mime_part_get_filename (mime_part));
3133 pair->filename = g_build_filename (chooser_uri, escaped, NULL);
3136 pair->filename = g_strdup (chooser_uri);
3138 pair->part = mime_part;
3139 files_to_save = g_list_prepend (files_to_save, pair);
3141 tny_iterator_next (iter);
3143 g_object_unref (iter);
3146 if (files_to_save != NULL) {
3147 SaveMimePartInfo *info = g_slice_new0 (SaveMimePartInfo);
3148 info->pairs = files_to_save;
3149 info->result = TRUE;
3150 info->uri = g_strdup (chooser_uri);
3151 info->window = g_object_ref (sa_info->window);
3152 save_mime_parts_to_file_with_checks ((GtkWindow *) dialog, info);
3154 g_free (chooser_uri);
3157 /* Free and close the dialog */
3158 g_object_unref (mime_parts);
3159 g_object_unref (sa_info->window);
3160 g_slice_free (SaveAttachmentsInfo, sa_info);
3161 gtk_widget_destroy (GTK_WIDGET (dialog));
3165 modest_msg_view_window_save_attachments (ModestMsgViewWindow *window,
3166 TnyList *mime_parts)
3168 ModestMsgViewWindowPrivate *priv;
3169 GtkWidget *save_dialog = NULL;
3170 gchar *conf_folder = NULL;
3171 gchar *filename = NULL;
3172 gchar *save_multiple_str = NULL;
3173 const gchar *root_folder = "file:///";
3175 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window));
3176 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
3178 if (mime_parts == NULL) {
3179 /* In Hildon 2.2 save and delete operate over all the attachments as there's no
3180 * selection available */
3181 mime_parts = modest_msg_view_get_attachments (MODEST_MSG_VIEW (priv->msg_view));
3182 if (mime_parts && !modest_maemo_utils_select_attachments (GTK_WINDOW (window), mime_parts, FALSE)) {
3183 g_object_unref (mime_parts);
3186 if (mime_parts == NULL || tny_list_get_length (mime_parts) == 0) {
3188 g_object_unref (mime_parts);
3194 g_object_ref (mime_parts);
3197 /* prepare dialog */
3198 if (tny_list_get_length (mime_parts) == 1) {
3200 /* only one attachment selected */
3201 iter = tny_list_create_iterator (mime_parts);
3202 TnyMimePart *mime_part = (TnyMimePart *) tny_iterator_get_current (iter);
3203 g_object_unref (iter);
3204 if (!modest_tny_mime_part_is_msg (mime_part) &&
3205 modest_tny_mime_part_is_attachment_for_modest (mime_part) &&
3206 !tny_mime_part_is_purged (mime_part)) {
3207 filename = g_strdup (tny_mime_part_get_filename (mime_part));
3209 /* TODO: show any error? */
3210 g_warning ("%s: Tried to save a non-file attachment", __FUNCTION__);
3211 g_object_unref (mime_parts);
3214 g_object_unref (mime_part);
3216 gint num = tny_list_get_length (mime_parts);
3217 save_multiple_str = g_strdup_printf (dngettext("hildon-fm",
3218 "sfil_va_number_of_objects_attachment",
3219 "sfil_va_number_of_objects_attachments",
3223 /****** HILDON2:START
3224 * creation of hildon file chooser dialog for saving
3226 save_dialog = hildon_file_chooser_dialog_new (GTK_WINDOW (window),
3227 GTK_FILE_CHOOSER_ACTION_SAVE);
3228 /****** HILDON2:END */
3230 /* Get last used folder */
3231 conf_folder = modest_conf_get_string (modest_runtime_get_conf (),
3232 MODEST_CONF_LATEST_SAVE_ATTACHMENT_PATH, NULL);
3234 /* File chooser stops working if we select "file:///" as current folder */
3235 if (conf_folder && g_ascii_strcasecmp (root_folder, conf_folder) != 0) {
3236 g_free (conf_folder);
3240 if (conf_folder && conf_folder[0] != '\0') {
3241 gtk_file_chooser_set_current_folder_uri (GTK_FILE_CHOOSER (save_dialog), conf_folder);
3244 /* Set the default folder to documents folder */
3245 docs_folder = (gchar *) g_strdup(g_get_user_special_dir (G_USER_DIRECTORY_DOCUMENTS));
3248 docs_folder = g_build_filename (g_getenv (MYDOCS_ENV), DOCS_FOLDER, NULL);
3250 gtk_file_chooser_set_current_folder (GTK_FILE_CHOOSER (save_dialog), docs_folder);
3251 g_free (docs_folder);
3253 g_free (conf_folder);
3257 gtk_file_chooser_set_current_name (GTK_FILE_CHOOSER (save_dialog),
3262 /* if multiple, set multiple string */
3263 if (save_multiple_str) {
3264 g_object_set (G_OBJECT (save_dialog), "save-multiple", save_multiple_str, NULL);
3265 gtk_window_set_title (GTK_WINDOW (save_dialog), _FM("sfil_ti_save_objects_files"));
3266 g_free (save_multiple_str);
3269 /* We must run this asynchronously, because the hildon dialog
3270 performs a gtk_dialog_run by itself which leads to gdk
3272 SaveAttachmentsInfo *sa_info;
3273 sa_info = g_slice_new (SaveAttachmentsInfo);
3274 sa_info->attachments_list = mime_parts;
3275 sa_info->window = g_object_ref (window);
3276 g_signal_connect (save_dialog, "response",
3277 G_CALLBACK (save_attachments_response), sa_info);
3279 gtk_widget_show_all (save_dialog);
3283 show_remove_attachment_information (gpointer userdata)
3285 ModestMsgViewWindow *window = (ModestMsgViewWindow *) userdata;
3286 ModestMsgViewWindowPrivate *priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
3288 /* We're outside the main lock */
3289 gdk_threads_enter ();
3291 if (priv->remove_attachment_banner != NULL) {
3292 gtk_widget_destroy (priv->remove_attachment_banner);
3293 g_object_unref (priv->remove_attachment_banner);
3296 priv->remove_attachment_banner = g_object_ref (
3297 modest_platform_animation_banner (NULL, NULL, _("mcen_me_inbox_remove_attachments")));
3299 gdk_threads_leave ();
3305 modest_msg_view_window_remove_attachments (ModestMsgViewWindow *window, gboolean get_all)
3307 ModestMsgViewWindowPrivate *priv;
3308 TnyList *mime_parts = NULL, *tmp;
3309 gchar *confirmation_message;
3315 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window));
3316 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
3318 /* In hildon 2.2 we ignore the get_all flag as we always get all attachments. This is
3319 * because we don't have selection
3321 mime_parts = modest_msg_view_get_attachments (MODEST_MSG_VIEW (priv->msg_view));
3323 /* Remove already purged messages from mime parts list. We use
3324 a copy of the list to remove items in the original one */
3325 tmp = tny_list_copy (mime_parts);
3326 iter = tny_list_create_iterator (tmp);
3327 while (!tny_iterator_is_done (iter)) {
3328 TnyMimePart *part = TNY_MIME_PART (tny_iterator_get_current (iter));
3329 if (tny_mime_part_is_purged (part))
3330 tny_list_remove (mime_parts, (GObject *) part);
3332 g_object_unref (part);
3333 tny_iterator_next (iter);
3335 g_object_unref (tmp);
3336 g_object_unref (iter);
3338 if (!modest_maemo_utils_select_attachments (GTK_WINDOW (window), mime_parts, TRUE) ||
3339 tny_list_get_length (mime_parts) == 0) {
3340 g_object_unref (mime_parts);
3344 n_attachments = tny_list_get_length (mime_parts);
3345 if (n_attachments == 1) {
3349 iter = tny_list_create_iterator (mime_parts);
3350 part = (TnyMimePart *) tny_iterator_get_current (iter);
3351 g_object_unref (iter);
3352 if (modest_tny_mime_part_is_msg (part)) {
3354 header = tny_msg_get_header (TNY_MSG (part));
3355 filename = tny_header_dup_subject (header);
3356 g_object_unref (header);
3357 if (filename == NULL)
3358 filename = g_strdup (_("mail_va_no_subject"));
3360 filename = g_strdup (tny_mime_part_get_filename (TNY_MIME_PART (part)));
3362 confirmation_message = g_strdup_printf (_("mcen_nc_purge_file_text"), filename);
3364 g_object_unref (part);
3366 confirmation_message = g_strdup_printf (ngettext("mcen_nc_purge_file_text",
3367 "mcen_nc_purge_files_text",
3368 n_attachments), n_attachments);
3370 response = modest_platform_run_confirmation_dialog (GTK_WINDOW (window),
3371 confirmation_message);
3372 g_free (confirmation_message);
3374 if (response != GTK_RESPONSE_OK) {
3375 g_object_unref (mime_parts);
3379 priv->purge_timeout = g_timeout_add (2000, show_remove_attachment_information, window);
3381 iter = tny_list_create_iterator (mime_parts);
3382 while (!tny_iterator_is_done (iter)) {
3385 part = (TnyMimePart *) tny_iterator_get_current (iter);
3386 tny_mime_part_set_purged (TNY_MIME_PART (part));
3387 g_object_unref (part);
3388 tny_iterator_next (iter);
3390 g_object_unref (iter);
3392 msg = tny_msg_view_get_msg (TNY_MSG_VIEW (priv->msg_view));
3393 tny_msg_view_clear (TNY_MSG_VIEW (priv->msg_view));
3394 tny_msg_rewrite_cache (msg);
3395 tny_msg_view_set_msg (TNY_MSG_VIEW (priv->msg_view), msg);
3396 g_object_unref (msg);
3397 update_branding (MODEST_MSG_VIEW_WINDOW (window));
3399 g_object_unref (mime_parts);
3401 if (priv->purge_timeout > 0) {
3402 g_source_remove (priv->purge_timeout);
3403 priv->purge_timeout = 0;
3406 if (priv->remove_attachment_banner) {
3407 gtk_widget_destroy (priv->remove_attachment_banner);
3408 g_object_unref (priv->remove_attachment_banner);
3409 priv->remove_attachment_banner = NULL;
3415 update_window_title (ModestMsgViewWindow *window)
3417 ModestMsgViewWindowPrivate *priv;
3419 TnyHeader *header = NULL;
3420 gchar *subject = NULL;
3422 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
3424 /* Note that if the window is closed while we're retrieving
3425 the message, this widget could de deleted */
3426 if (!priv->msg_view)
3429 msg = tny_msg_view_get_msg (TNY_MSG_VIEW (priv->msg_view));
3431 if (priv->other_body) {
3434 description = modest_tny_mime_part_get_header_value (priv->other_body, "Content-Description");
3436 g_strstrip (description);
3437 subject = description;
3439 } else if (msg != NULL) {
3440 header = tny_msg_get_header (msg);
3441 subject = tny_header_dup_subject (header);
3442 g_object_unref (header);
3443 g_object_unref (msg);
3446 if ((subject == NULL)||(subject[0] == '\0')) {
3448 subject = g_strdup (_("mail_va_no_subject"));
3451 modest_window_set_title (MODEST_WINDOW (window), subject);
3456 on_move_focus (GtkWidget *widget,
3457 GtkDirectionType direction,
3460 g_signal_stop_emission_by_name (G_OBJECT (widget), "move-focus");
3464 fetch_image_open_stream (TnyStreamCache *self, gint64 *expected_size, gchar *uri)
3466 GnomeVFSResult result;
3467 GnomeVFSHandle *handle = NULL;
3468 GnomeVFSFileInfo *info = NULL;
3471 result = gnome_vfs_open (&handle, uri, GNOME_VFS_OPEN_READ);
3472 if (result != GNOME_VFS_OK) {
3477 info = gnome_vfs_file_info_new ();
3478 result = gnome_vfs_get_file_info_from_handle (handle, info, GNOME_VFS_FILE_INFO_DEFAULT);
3479 if (result != GNOME_VFS_OK || ! (info->valid_fields & GNOME_VFS_FILE_INFO_FIELDS_SIZE)) {
3480 /* We put a "safe" default size for going to cache */
3481 *expected_size = (300*1024);
3483 *expected_size = info->size;
3485 gnome_vfs_file_info_unref (info);
3487 stream = tny_vfs_stream_new (handle);
3496 TnyStream *output_stream;
3497 GtkWidget *msg_view;
3502 on_fetch_image_idle_refresh_view (gpointer userdata)
3505 FetchImageData *fidata = (FetchImageData *) userdata;
3507 gdk_threads_enter ();
3508 if (GTK_WIDGET_DRAWABLE (fidata->msg_view)) {
3509 ModestMsgViewWindowPrivate *priv;
3511 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (fidata->window);
3512 priv->fetching_images--;
3513 gtk_widget_queue_draw (fidata->msg_view);
3514 update_progress_hint (MODEST_MSG_VIEW_WINDOW (fidata->window));
3516 gdk_threads_leave ();
3518 g_object_unref (fidata->msg_view);
3519 g_object_unref (fidata->window);
3520 g_slice_free (FetchImageData, fidata);
3525 on_fetch_image_thread (gpointer userdata)
3527 FetchImageData *fidata = (FetchImageData *) userdata;
3528 TnyStreamCache *cache;
3529 TnyStream *cache_stream;
3531 cache = modest_runtime_get_images_cache ();
3533 tny_stream_cache_get_stream (cache,
3535 (TnyStreamCacheOpenStreamFetcher) fetch_image_open_stream,
3536 (gpointer) fidata->uri);
3537 g_free (fidata->cache_id);
3538 g_free (fidata->uri);
3540 if (cache_stream != NULL) {
3543 while (G_LIKELY (!tny_stream_is_eos (cache_stream))) {
3546 nb_read = tny_stream_read (cache_stream, buffer, sizeof (buffer));
3547 if (G_UNLIKELY (nb_read < 0)) {
3549 } else if (G_LIKELY (nb_read > 0)) {
3550 gssize nb_written = 0;
3552 while (G_UNLIKELY (nb_written < nb_read)) {
3555 len = tny_stream_write (fidata->output_stream, buffer + nb_written,
3556 nb_read - nb_written);
3557 if (G_UNLIKELY (len < 0))
3563 tny_stream_close (cache_stream);
3564 g_object_unref (cache_stream);
3567 tny_stream_close (fidata->output_stream);
3568 g_object_unref (fidata->output_stream);
3570 g_idle_add (on_fetch_image_idle_refresh_view, fidata);
3576 on_fetch_image (ModestMsgView *msgview,
3579 ModestMsgViewWindow *window)
3581 const gchar *current_account;
3582 ModestMsgViewWindowPrivate *priv;
3583 FetchImageData *fidata;
3585 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
3587 current_account = modest_window_get_active_account (MODEST_WINDOW (window));
3589 fidata = g_slice_new0 (FetchImageData);
3590 fidata->msg_view = g_object_ref (msgview);
3591 fidata->window = g_object_ref (window);
3592 fidata->uri = g_strdup (uri);
3593 fidata->cache_id = modest_images_cache_get_id (current_account, uri);
3594 fidata->output_stream = g_object_ref (stream);
3596 priv->fetching_images++;
3597 if (g_thread_create (on_fetch_image_thread, fidata, FALSE, NULL) == NULL) {
3598 g_object_unref (fidata->output_stream);
3599 g_free (fidata->cache_id);
3600 g_free (fidata->uri);
3601 g_object_unref (fidata->msg_view);
3602 g_slice_free (FetchImageData, fidata);
3603 tny_stream_close (stream);
3604 priv->fetching_images--;
3605 update_progress_hint (window);
3608 update_progress_hint (window);
3614 setup_menu (ModestMsgViewWindow *self)
3616 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW(self));
3618 /* Settings menu buttons */
3619 modest_window_add_to_menu (MODEST_WINDOW (self), _("mcen_me_viewer_find"), NULL,
3620 MODEST_WINDOW_MENU_CALLBACK (modest_msg_view_window_show_find_toolbar),
3621 MODEST_DIMMING_CALLBACK (modest_ui_dimming_rules_on_find_in_msg));
3623 modest_window_add_to_menu (MODEST_WINDOW (self),
3624 dngettext(GETTEXT_PACKAGE,
3625 "mcen_me_move_message",
3626 "mcen_me_move_messages",
3629 MODEST_WINDOW_MENU_CALLBACK (modest_ui_actions_on_move_to),
3630 MODEST_DIMMING_CALLBACK (modest_ui_dimming_rules_on_move_to));
3632 modest_window_add_to_menu (MODEST_WINDOW (self), _("mcen_me_inbox_mark_as_read"), NULL,
3633 MODEST_WINDOW_MENU_CALLBACK (modest_ui_actions_on_mark_as_read),
3634 MODEST_DIMMING_CALLBACK (modest_ui_dimming_rules_on_mark_as_read_msg_in_view));
3636 modest_window_add_to_menu (MODEST_WINDOW (self), _("mcen_me_inbox_mark_as_unread"), NULL,
3637 MODEST_WINDOW_MENU_CALLBACK (modest_ui_actions_on_mark_as_unread),
3638 MODEST_DIMMING_CALLBACK (modest_ui_dimming_rules_on_mark_as_unread_msg_in_view));
3640 modest_window_add_to_menu (MODEST_WINDOW (self), _("mcen_me_viewer_save_attachments"), NULL,
3641 MODEST_WINDOW_MENU_CALLBACK (modest_ui_actions_save_attachments),
3642 MODEST_DIMMING_CALLBACK (modest_ui_dimming_rules_on_save_attachments));
3643 modest_window_add_to_menu (MODEST_WINDOW (self), _("mcen_me_inbox_remove_attachments"), NULL,
3644 MODEST_WINDOW_MENU_CALLBACK (modest_ui_actions_remove_attachments),
3645 MODEST_DIMMING_CALLBACK (modest_ui_dimming_rules_on_remove_attachments));
3647 modest_window_add_to_menu (MODEST_WINDOW (self), _("mcen_me_new_message"), "<Control>n",
3648 MODEST_WINDOW_MENU_CALLBACK (modest_ui_actions_on_new_msg),
3649 MODEST_DIMMING_CALLBACK (modest_ui_dimming_rules_on_new_msg));
3650 modest_window_add_to_menu (MODEST_WINDOW (self), _("mcen_me_viewer_addtocontacts"), NULL,
3651 MODEST_WINDOW_MENU_CALLBACK (modest_ui_actions_add_to_contacts),
3652 MODEST_DIMMING_CALLBACK (modest_ui_dimming_rules_on_add_to_contacts));
3654 modest_window_add_to_menu (MODEST_WINDOW (self), _("mcen_ti_message_properties"), NULL,
3655 MODEST_WINDOW_MENU_CALLBACK (modest_ui_actions_on_details),
3656 MODEST_DIMMING_CALLBACK (modest_ui_dimming_rules_on_details));
3660 modest_msg_view_window_add_to_contacts (ModestMsgViewWindow *self)
3662 ModestMsgViewWindowPrivate *priv;
3663 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
3664 GSList *recipients = NULL;
3666 gboolean contacts_to_add = FALSE;
3668 msg = tny_msg_view_get_msg (TNY_MSG_VIEW (priv->msg_view));
3672 header = modest_msg_view_window_get_header (self);
3675 recipients = modest_tny_msg_header_get_all_recipients_list (header);
3676 g_object_unref (header);
3678 recipients = modest_tny_msg_get_all_recipients_list (msg);
3679 g_object_unref (msg);
3682 if (recipients != NULL) {
3683 /****** HILDON2:START
3684 * shows dialog with addresses not present in addressbook. User can choose one to
3685 * add it to addressbook.
3687 GtkWidget *picker_dialog;
3688 GtkWidget *selector;
3690 gchar *selected = NULL;
3692 selector = hildon_touch_selector_new_text ();
3693 g_object_ref (selector);
3695 for (node = recipients; node != NULL; node = g_slist_next (node)) {
3696 if (!modest_address_book_has_address ((const gchar *) node->data)) {
3697 hildon_touch_selector_append_text (HILDON_TOUCH_SELECTOR (selector),
3698 (const gchar *) node->data);
3699 contacts_to_add = TRUE;
3703 if (contacts_to_add) {
3706 picker_dialog = hildon_picker_dialog_new (GTK_WINDOW (self));
3707 gtk_window_set_title (GTK_WINDOW (picker_dialog), _("mcen_me_viewer_addtocontacts"));
3709 hildon_picker_dialog_set_selector (HILDON_PICKER_DIALOG (picker_dialog),
3710 HILDON_TOUCH_SELECTOR (selector));
3712 picker_result = gtk_dialog_run (GTK_DIALOG (picker_dialog));
3714 if (picker_result == GTK_RESPONSE_OK) {
3715 selected = hildon_touch_selector_get_current_text (HILDON_TOUCH_SELECTOR (selector));
3717 gtk_widget_destroy (picker_dialog);
3720 modest_address_book_add_address (selected, (GtkWindow *) self);
3725 g_object_unref (selector);
3728 /****** HILDON2:END */
3731 if (recipients) {g_slist_foreach (recipients, (GFunc) g_free, NULL); g_slist_free (recipients);}
3735 _modest_msg_view_window_map_event (GtkWidget *widget,
3739 ModestMsgViewWindow *self = (ModestMsgViewWindow *) userdata;
3741 update_progress_hint (self);
3747 modest_msg_view_window_fetch_images (ModestMsgViewWindow *self)
3749 ModestMsgViewWindowPrivate *priv;
3750 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
3752 modest_msg_view_request_fetch_images (MODEST_MSG_VIEW (priv->msg_view));
3756 modest_msg_view_window_has_blocked_external_images (ModestMsgViewWindow *self)
3758 ModestMsgViewWindowPrivate *priv;
3759 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
3761 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self), FALSE);
3763 return modest_msg_view_has_blocked_external_images (MODEST_MSG_VIEW (priv->msg_view));
3767 modest_msg_view_window_reload (ModestMsgViewWindow *self)
3769 ModestMsgViewWindowPrivate *priv;
3770 const gchar *msg_uid;
3771 TnyHeader *header = NULL;
3772 TnyFolder *folder = NULL;
3774 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self));
3776 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
3778 header = modest_msg_view_window_get_header (MODEST_MSG_VIEW_WINDOW (self));
3782 folder = tny_header_get_folder (header);
3783 g_object_unref (header);
3788 msg_uid = modest_msg_view_window_get_message_uid (self);
3790 GtkTreeRowReference *row_reference;
3792 if (priv->row_reference && gtk_tree_row_reference_valid (priv->row_reference)) {
3793 row_reference = priv->row_reference;
3795 row_reference = NULL;
3797 if (!message_reader (self, priv, NULL, msg_uid, folder, row_reference))
3798 g_warning ("Shouldn't happen, trying to reload a message failed");
3801 g_object_unref (folder);
3805 update_branding (ModestMsgViewWindow *self)
3807 const gchar *account;
3808 const gchar *mailbox;
3809 ModestAccountMgr *mgr;
3810 ModestProtocol *protocol = NULL;
3811 gchar *service_name = NULL;
3812 const GdkPixbuf *service_icon = NULL;
3813 ModestMsgViewWindowPrivate *priv;
3815 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
3817 account = modest_window_get_active_account (MODEST_WINDOW (self));
3818 mailbox = modest_window_get_active_mailbox (MODEST_WINDOW (self));
3820 mgr = modest_runtime_get_account_mgr ();
3822 if (modest_account_mgr_account_is_multimailbox (mgr, account, &protocol)) {
3823 if (MODEST_IS_ACCOUNT_PROTOCOL (protocol)) {
3824 service_name = modest_account_protocol_get_service_name (MODEST_ACCOUNT_PROTOCOL (protocol),
3826 service_icon = modest_account_protocol_get_service_icon (MODEST_ACCOUNT_PROTOCOL (protocol),
3827 account, mailbox, MODEST_ICON_SIZE_SMALL);
3831 modest_msg_view_set_branding (MODEST_MSG_VIEW (priv->msg_view), service_name, service_icon);
3832 g_free (service_name);
3836 sync_flags (ModestMsgViewWindow *self)
3838 TnyHeader *header = NULL;
3840 header = modest_msg_view_window_get_header (self);
3842 TnyMsg *msg = modest_msg_view_window_get_message (self);
3844 header = tny_msg_get_header (msg);
3845 g_object_unref (msg);
3850 TnyFolder *folder = tny_header_get_folder (header);
3853 ModestMailOperation *mail_op;
3855 /* Sync folder, we need this to save the seen flag */
3856 mail_op = modest_mail_operation_new (NULL);
3857 modest_mail_operation_queue_add (modest_runtime_get_mail_operation_queue (),
3859 modest_mail_operation_sync_folder (mail_op, folder, FALSE, NULL, NULL);
3860 g_object_unref (mail_op);
3861 g_object_unref (folder);
3863 g_object_unref (header);