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-main-window-ui.h>
44 #include "modest-msg-view-window-ui-dimming.h"
45 #include <modest-widget-memory.h>
46 #include <modest-progress-object.h>
47 #include <modest-runtime.h>
48 #include <modest-window-priv.h>
49 #include <modest-tny-folder.h>
50 #include <modest-text-utils.h>
51 #include <modest-account-mgr-helpers.h>
52 #include <hildon/hildon-pannable-area.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-hildon2-window-mgr.h>
71 #include <tny-camel-msg.h>
72 #include <tny-camel-bs-mime-part.h>
73 #include <tny-camel-bs-msg.h>
75 #define MYDOCS_ENV "MYDOCSDIR"
76 #define DOCS_FOLDER ".documents"
78 typedef struct _ModestMsgViewWindowPrivate ModestMsgViewWindowPrivate;
79 struct _ModestMsgViewWindowPrivate {
82 GtkWidget *main_scroll;
83 GtkWidget *find_toolbar;
86 /* Progress observers */
87 GSList *progress_widgets;
90 GtkWidget *prev_toolitem;
91 GtkWidget *next_toolitem;
92 gboolean progress_hint;
95 /* Optimized view enabled */
96 gboolean optimized_view;
98 /* Whether this was created via the *_new_for_search_result() function. */
99 gboolean is_search_result;
101 /* Whether the message is in outbox */
104 /* A reference to the @model of the header view
105 * to allow selecting previous/next messages,
106 * if the message is currently selected in the header view.
108 const gchar *header_folder_id;
109 GtkTreeModel *header_model;
110 GtkTreeRowReference *row_reference;
111 GtkTreeRowReference *next_row_reference;
113 gulong clipboard_change_handler;
114 gulong queue_change_handler;
115 gulong account_removed_handler;
116 gulong row_changed_handler;
117 gulong row_deleted_handler;
118 gulong row_inserted_handler;
119 gulong rows_reordered_handler;
120 gulong fetch_image_redraw_handler;
123 GtkWidget *remove_attachment_banner;
126 TnyMimePart *other_body;
132 static void modest_msg_view_window_class_init (ModestMsgViewWindowClass *klass);
133 static void modest_msg_view_window_init (ModestMsgViewWindow *obj);
134 static void modest_header_view_observer_init (ModestHeaderViewObserverIface *iface_class);
135 static void modest_msg_view_window_finalize (GObject *obj);
136 static void modest_msg_view_window_show_find_toolbar (GtkWidget *obj, gpointer data);
137 static void modest_msg_view_window_find_toolbar_close (GtkWidget *widget,
138 ModestMsgViewWindow *obj);
139 static void modest_msg_view_window_find_toolbar_search (GtkWidget *widget,
140 ModestMsgViewWindow *obj);
141 static void modest_msg_view_window_toggle_find_toolbar (GtkWidget *obj,
143 static void modest_msg_view_window_disconnect_signals (ModestWindow *self);
145 static gdouble modest_msg_view_window_get_zoom (ModestWindow *window);
146 static void modest_msg_view_window_set_zoom (ModestWindow *window,
148 static gboolean modest_msg_view_window_zoom_minus (ModestWindow *window);
149 static gboolean modest_msg_view_window_zoom_plus (ModestWindow *window);
150 static gboolean modest_msg_view_window_key_event (GtkWidget *window,
153 static void modest_msg_view_window_update_priority (ModestMsgViewWindow *window);
155 static void modest_msg_view_window_show_toolbar (ModestWindow *window,
156 gboolean show_toolbar);
158 static void modest_msg_view_window_clipboard_owner_change (GtkClipboard *clipboard,
160 ModestMsgViewWindow *window);
162 static void modest_msg_view_window_on_row_changed (GtkTreeModel *header_model,
165 ModestMsgViewWindow *window);
167 static void modest_msg_view_window_on_row_deleted (GtkTreeModel *header_model,
169 ModestMsgViewWindow *window);
171 static void modest_msg_view_window_on_row_inserted (GtkTreeModel *header_model,
172 GtkTreePath *tree_path,
173 GtkTreeIter *tree_iter,
174 ModestMsgViewWindow *window);
176 static void modest_msg_view_window_on_row_reordered (GtkTreeModel *header_model,
180 ModestMsgViewWindow *window);
182 static void modest_msg_view_window_update_model_replaced (ModestHeaderViewObserver *window,
184 const gchar *tny_folder_id);
186 static void on_queue_changed (ModestMailOperationQueue *queue,
187 ModestMailOperation *mail_op,
188 ModestMailOperationQueueNotification type,
189 ModestMsgViewWindow *self);
191 static void on_account_removed (TnyAccountStore *account_store,
195 static void on_move_focus (GtkWidget *widget,
196 GtkDirectionType direction,
199 static void view_msg_cb (ModestMailOperation *mail_op,
206 static void set_progress_hint (ModestMsgViewWindow *self,
209 static void update_window_title (ModestMsgViewWindow *window);
211 static void init_window (ModestMsgViewWindow *obj);
213 static gboolean msg_is_visible (TnyHeader *header, gboolean check_outbox);
215 static void check_dimming_rules_after_change (ModestMsgViewWindow *window);
217 static gboolean on_fetch_image (ModestMsgView *msgview,
220 ModestMsgViewWindow *window);
222 static gboolean modest_msg_view_window_scroll_child (ModestMsgViewWindow *self,
223 GtkScrollType scroll_type,
226 static gboolean message_reader (ModestMsgViewWindow *window,
227 ModestMsgViewWindowPrivate *priv,
229 const gchar *msg_uid,
231 GtkTreeRowReference *row_reference);
233 static void setup_menu (ModestMsgViewWindow *self);
234 static gboolean _modest_msg_view_window_map_event (GtkWidget *widget,
237 static void update_branding (ModestMsgViewWindow *self);
238 static void sync_flags (ModestMsgViewWindow *self);
240 /* list my signals */
247 static const GtkToggleActionEntry msg_view_toggle_action_entries [] = {
248 { "FindInMessage", MODEST_TOOLBAR_ICON_FIND, N_("qgn_toolb_gene_find"), "<CTRL>F", NULL, G_CALLBACK (modest_msg_view_window_toggle_find_toolbar), FALSE },
251 #define MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE((o), \
252 MODEST_TYPE_MSG_VIEW_WINDOW, \
253 ModestMsgViewWindowPrivate))
255 static GtkWindowClass *parent_class = NULL;
257 /* uncomment the following if you have defined any signals */
258 static guint signals[LAST_SIGNAL] = {0};
261 modest_msg_view_window_get_type (void)
263 static GType my_type = 0;
265 static const GTypeInfo my_info = {
266 sizeof(ModestMsgViewWindowClass),
267 NULL, /* base init */
268 NULL, /* base finalize */
269 (GClassInitFunc) modest_msg_view_window_class_init,
270 NULL, /* class finalize */
271 NULL, /* class data */
272 sizeof(ModestMsgViewWindow),
274 (GInstanceInitFunc) modest_msg_view_window_init,
277 my_type = g_type_register_static (MODEST_TYPE_HILDON2_WINDOW,
278 "ModestMsgViewWindow",
281 static const GInterfaceInfo modest_header_view_observer_info =
283 (GInterfaceInitFunc) modest_header_view_observer_init,
284 NULL, /* interface_finalize */
285 NULL /* interface_data */
288 g_type_add_interface_static (my_type,
289 MODEST_TYPE_HEADER_VIEW_OBSERVER,
290 &modest_header_view_observer_info);
296 save_state (ModestWindow *self)
298 modest_widget_memory_save (modest_runtime_get_conf (),
300 MODEST_CONF_MSG_VIEW_WINDOW_KEY);
304 modest_msg_view_window_scroll_child (ModestMsgViewWindow *self,
305 GtkScrollType scroll_type,
309 ModestMsgViewWindowPrivate *priv;
312 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(self);
314 switch (scroll_type) {
315 case GTK_SCROLL_STEP_UP:
318 case GTK_SCROLL_STEP_DOWN:
321 case GTK_SCROLL_PAGE_UP:
324 case GTK_SCROLL_PAGE_DOWN:
327 case GTK_SCROLL_START:
338 modest_maemo_utils_scroll_pannable((HildonPannableArea *) priv->main_scroll, 0, step);
340 return (gboolean) step;
344 add_scroll_binding (GtkBindingSet *binding_set,
346 GtkScrollType scroll)
348 guint keypad_keyval = keyval - GDK_Left + GDK_KP_Left;
350 gtk_binding_entry_add_signal (binding_set, keyval, 0,
352 GTK_TYPE_SCROLL_TYPE, scroll,
353 G_TYPE_BOOLEAN, FALSE);
354 gtk_binding_entry_add_signal (binding_set, keypad_keyval, 0,
356 GTK_TYPE_SCROLL_TYPE, scroll,
357 G_TYPE_BOOLEAN, FALSE);
361 modest_msg_view_window_class_init (ModestMsgViewWindowClass *klass)
363 GObjectClass *gobject_class;
364 HildonWindowClass *hildon_window_class;
365 ModestWindowClass *modest_window_class;
366 GtkBindingSet *binding_set;
368 gobject_class = (GObjectClass*) klass;
369 hildon_window_class = (HildonWindowClass *) klass;
370 modest_window_class = (ModestWindowClass *) klass;
372 parent_class = g_type_class_peek_parent (klass);
373 gobject_class->finalize = modest_msg_view_window_finalize;
375 modest_window_class->set_zoom_func = modest_msg_view_window_set_zoom;
376 modest_window_class->get_zoom_func = modest_msg_view_window_get_zoom;
377 modest_window_class->zoom_plus_func = modest_msg_view_window_zoom_plus;
378 modest_window_class->zoom_minus_func = modest_msg_view_window_zoom_minus;
379 modest_window_class->show_toolbar_func = modest_msg_view_window_show_toolbar;
380 modest_window_class->disconnect_signals_func = modest_msg_view_window_disconnect_signals;
382 modest_window_class->save_state_func = save_state;
384 klass->scroll_child = modest_msg_view_window_scroll_child;
386 signals[MSG_CHANGED_SIGNAL] =
387 g_signal_new ("msg-changed",
388 G_TYPE_FROM_CLASS (gobject_class),
390 G_STRUCT_OFFSET (ModestMsgViewWindowClass, msg_changed),
392 modest_marshal_VOID__POINTER_POINTER,
393 G_TYPE_NONE, 2, G_TYPE_POINTER, G_TYPE_POINTER);
395 signals[SCROLL_CHILD_SIGNAL] =
396 g_signal_new ("scroll-child",
397 G_TYPE_FROM_CLASS (gobject_class),
398 G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
399 G_STRUCT_OFFSET (ModestMsgViewWindowClass, scroll_child),
401 modest_marshal_BOOLEAN__ENUM_BOOLEAN,
402 G_TYPE_BOOLEAN, 2, GTK_TYPE_SCROLL_TYPE, G_TYPE_BOOLEAN);
404 binding_set = gtk_binding_set_by_class (klass);
405 add_scroll_binding (binding_set, GDK_Up, GTK_SCROLL_STEP_UP);
406 add_scroll_binding (binding_set, GDK_Down, GTK_SCROLL_STEP_DOWN);
407 add_scroll_binding (binding_set, GDK_Page_Up, GTK_SCROLL_PAGE_UP);
408 add_scroll_binding (binding_set, GDK_Page_Down, GTK_SCROLL_PAGE_DOWN);
409 add_scroll_binding (binding_set, GDK_Home, GTK_SCROLL_START);
410 add_scroll_binding (binding_set, GDK_End, GTK_SCROLL_END);
412 g_type_class_add_private (gobject_class, sizeof(ModestMsgViewWindowPrivate));
416 static void modest_header_view_observer_init(
417 ModestHeaderViewObserverIface *iface_class)
419 iface_class->update_func = modest_msg_view_window_update_model_replaced;
423 modest_msg_view_window_init (ModestMsgViewWindow *obj)
425 ModestMsgViewWindowPrivate *priv;
426 ModestWindowPrivate *parent_priv = NULL;
427 GtkActionGroup *action_group = NULL;
428 GError *error = NULL;
430 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(obj);
431 parent_priv = MODEST_WINDOW_GET_PRIVATE(obj);
432 parent_priv->ui_manager = gtk_ui_manager_new();
434 action_group = gtk_action_group_new ("ModestMsgViewWindowActions");
435 gtk_action_group_set_translation_domain (action_group, GETTEXT_PACKAGE);
437 /* Add common actions */
438 gtk_action_group_add_actions (action_group,
439 modest_action_entries,
440 G_N_ELEMENTS (modest_action_entries),
442 gtk_action_group_add_toggle_actions (action_group,
443 msg_view_toggle_action_entries,
444 G_N_ELEMENTS (msg_view_toggle_action_entries),
447 gtk_ui_manager_insert_action_group (parent_priv->ui_manager, action_group, 0);
448 g_object_unref (action_group);
450 /* Load the UI definition */
451 gtk_ui_manager_add_ui_from_file (parent_priv->ui_manager, MODEST_UIDIR "modest-msg-view-window-ui.xml",
454 g_printerr ("modest: could not merge modest-msg-view-window-ui.xml: %s\n", error->message);
455 g_error_free (error);
460 /* Add accelerators */
461 gtk_window_add_accel_group (GTK_WINDOW (obj),
462 gtk_ui_manager_get_accel_group (parent_priv->ui_manager));
464 priv->is_search_result = FALSE;
465 priv->is_outbox = FALSE;
467 priv->msg_view = NULL;
468 priv->header_model = NULL;
469 priv->header_folder_id = NULL;
470 priv->clipboard_change_handler = 0;
471 priv->queue_change_handler = 0;
472 priv->account_removed_handler = 0;
473 priv->row_changed_handler = 0;
474 priv->row_deleted_handler = 0;
475 priv->row_inserted_handler = 0;
476 priv->rows_reordered_handler = 0;
477 priv->fetch_image_redraw_handler = 0;
478 priv->progress_hint = FALSE;
479 priv->fetching_images = 0;
481 priv->optimized_view = FALSE;
482 priv->purge_timeout = 0;
483 priv->remove_attachment_banner = NULL;
484 priv->msg_uid = NULL;
485 priv->other_body = NULL;
487 priv->sighandlers = NULL;
490 init_window (MODEST_MSG_VIEW_WINDOW(obj));
492 hildon_program_add_window (hildon_program_get_instance(),
498 update_progress_hint (ModestMsgViewWindow *self)
500 ModestMsgViewWindowPrivate *priv;
501 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(self);
503 if (GTK_WIDGET_VISIBLE (self)) {
504 hildon_gtk_window_set_progress_indicator (GTK_WINDOW (self),
505 (priv->progress_hint || (priv->fetching_images > 0))?1:0);
510 set_progress_hint (ModestMsgViewWindow *self,
513 ModestWindowPrivate *parent_priv;
514 ModestMsgViewWindowPrivate *priv;
516 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self));
518 parent_priv = MODEST_WINDOW_GET_PRIVATE(self);
519 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(self);
521 /* Sets current progress hint */
522 priv->progress_hint = enabled;
524 update_progress_hint (self);
530 init_window (ModestMsgViewWindow *obj)
532 GtkWidget *main_vbox;
533 ModestMsgViewWindowPrivate *priv;
535 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(obj);
537 priv->msg_view = GTK_WIDGET (tny_platform_factory_new_msg_view (modest_tny_platform_factory_get_instance ()));
538 modest_msg_view_set_shadow_type (MODEST_MSG_VIEW (priv->msg_view), GTK_SHADOW_NONE);
539 main_vbox = gtk_vbox_new (FALSE, 6);
540 priv->main_scroll = hildon_pannable_area_new ();
541 g_object_set (G_OBJECT (priv->main_scroll),
542 "mov-mode", HILDON_MOVEMENT_MODE_BOTH,
545 gtk_container_add (GTK_CONTAINER (priv->main_scroll), priv->msg_view);
546 gtk_box_pack_start (GTK_BOX(main_vbox), priv->main_scroll, TRUE, TRUE, 0);
547 gtk_container_add (GTK_CONTAINER(obj), main_vbox);
549 /* NULL-ize fields if the window is destroyed */
550 g_signal_connect (priv->msg_view, "destroy", G_CALLBACK (gtk_widget_destroyed), &(priv->msg_view));
552 gtk_widget_show_all (GTK_WIDGET(main_vbox));
556 modest_msg_view_window_disconnect_signals (ModestWindow *self)
558 ModestMsgViewWindowPrivate *priv;
559 GtkWidget *header_view = NULL;
560 GtkWindow *parent_window = NULL;
562 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
564 if (gtk_clipboard_get (GDK_SELECTION_PRIMARY) &&
565 g_signal_handler_is_connected (gtk_clipboard_get (GDK_SELECTION_PRIMARY),
566 priv->clipboard_change_handler))
567 g_signal_handler_disconnect (gtk_clipboard_get (GDK_SELECTION_PRIMARY),
568 priv->clipboard_change_handler);
570 if (g_signal_handler_is_connected (G_OBJECT (modest_runtime_get_mail_operation_queue ()),
571 priv->queue_change_handler))
572 g_signal_handler_disconnect (G_OBJECT (modest_runtime_get_mail_operation_queue ()),
573 priv->queue_change_handler);
575 if (g_signal_handler_is_connected (G_OBJECT (modest_runtime_get_account_store ()),
576 priv->account_removed_handler))
577 g_signal_handler_disconnect (G_OBJECT (modest_runtime_get_account_store ()),
578 priv->account_removed_handler);
580 if (priv->header_model) {
581 if (g_signal_handler_is_connected(G_OBJECT (priv->header_model),
582 priv->row_changed_handler))
583 g_signal_handler_disconnect(G_OBJECT (priv->header_model),
584 priv->row_changed_handler);
586 if (g_signal_handler_is_connected(G_OBJECT (priv->header_model),
587 priv->row_deleted_handler))
588 g_signal_handler_disconnect(G_OBJECT (priv->header_model),
589 priv->row_deleted_handler);
591 if (g_signal_handler_is_connected(G_OBJECT (priv->header_model),
592 priv->row_inserted_handler))
593 g_signal_handler_disconnect(G_OBJECT (priv->header_model),
594 priv->row_inserted_handler);
596 if (g_signal_handler_is_connected(G_OBJECT (priv->header_model),
597 priv->rows_reordered_handler))
598 g_signal_handler_disconnect(G_OBJECT (priv->header_model),
599 priv->rows_reordered_handler);
602 modest_signal_mgr_disconnect_all_and_destroy (priv->sighandlers);
603 priv->sighandlers = NULL;
605 parent_window = gtk_window_get_transient_for (GTK_WINDOW (self));
606 if (parent_window && MODEST_IS_HEADER_WINDOW (parent_window)) {
607 header_view = GTK_WIDGET (modest_header_window_get_header_view (MODEST_HEADER_WINDOW (parent_window)));
609 modest_header_view_remove_observer(MODEST_HEADER_VIEW (header_view),
610 MODEST_HEADER_VIEW_OBSERVER(self));
616 modest_msg_view_window_finalize (GObject *obj)
618 ModestMsgViewWindowPrivate *priv;
620 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (obj);
622 /* Sanity check: shouldn't be needed, the window mgr should
623 call this function before */
624 modest_msg_view_window_disconnect_signals (MODEST_WINDOW (obj));
626 if (priv->fetch_image_redraw_handler > 0) {
627 g_source_remove (priv->fetch_image_redraw_handler);
628 priv->fetch_image_redraw_handler = 0;
631 if (priv->other_body != NULL) {
632 g_object_unref (priv->other_body);
633 priv->other_body = NULL;
636 if (priv->top_msg != NULL) {
637 g_object_unref (priv->top_msg);
638 priv->top_msg = NULL;
641 if (priv->header_model != NULL) {
642 g_object_unref (priv->header_model);
643 priv->header_model = NULL;
646 if (priv->remove_attachment_banner) {
647 gtk_widget_destroy (priv->remove_attachment_banner);
648 g_object_unref (priv->remove_attachment_banner);
649 priv->remove_attachment_banner = NULL;
652 if (priv->purge_timeout > 0) {
653 g_source_remove (priv->purge_timeout);
654 priv->purge_timeout = 0;
657 if (priv->row_reference) {
658 gtk_tree_row_reference_free (priv->row_reference);
659 priv->row_reference = NULL;
662 if (priv->next_row_reference) {
663 gtk_tree_row_reference_free (priv->next_row_reference);
664 priv->next_row_reference = NULL;
668 g_free (priv->msg_uid);
669 priv->msg_uid = NULL;
672 G_OBJECT_CLASS(parent_class)->finalize (obj);
676 select_next_valid_row (GtkTreeModel *model,
677 GtkTreeRowReference **row_reference,
681 GtkTreeIter tmp_iter;
683 GtkTreePath *next = NULL;
684 gboolean retval = FALSE, finished;
686 g_return_val_if_fail (gtk_tree_row_reference_valid (*row_reference), FALSE);
688 path = gtk_tree_row_reference_get_path (*row_reference);
689 gtk_tree_model_get_iter (model, &tmp_iter, path);
690 gtk_tree_row_reference_free (*row_reference);
691 *row_reference = NULL;
695 TnyHeader *header = NULL;
697 if (gtk_tree_model_iter_next (model, &tmp_iter)) {
698 gtk_tree_model_get (model, &tmp_iter,
699 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
703 if (msg_is_visible (header, is_outbox)) {
704 next = gtk_tree_model_get_path (model, &tmp_iter);
705 *row_reference = gtk_tree_row_reference_new (model, next);
706 gtk_tree_path_free (next);
710 g_object_unref (header);
713 } else if (cycle && gtk_tree_model_get_iter_first (model, &tmp_iter)) {
714 next = gtk_tree_model_get_path (model, &tmp_iter);
716 /* Ensure that we are not selecting the same */
717 if (gtk_tree_path_compare (path, next) != 0) {
718 gtk_tree_model_get (model, &tmp_iter,
719 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
722 if (msg_is_visible (header, is_outbox)) {
723 *row_reference = gtk_tree_row_reference_new (model, next);
727 g_object_unref (header);
731 /* If we ended up in the same message
732 then there is no valid next
736 gtk_tree_path_free (next);
738 /* If there are no more messages and we don't
739 want to start again in the first one then
740 there is no valid next message */
746 gtk_tree_path_free (path);
751 /* TODO: This should be in _init(), with the parameters as properties. */
753 modest_msg_view_window_construct (ModestMsgViewWindow *self,
754 const gchar *modest_account_name,
755 const gchar *mailbox,
756 const gchar *msg_uid)
759 ModestMsgViewWindowPrivate *priv = NULL;
760 ModestWindowPrivate *parent_priv = NULL;
761 ModestDimmingRulesGroup *toolbar_rules_group = NULL;
762 ModestDimmingRulesGroup *clipboard_rules_group = NULL;
764 obj = G_OBJECT (self);
765 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(obj);
766 parent_priv = MODEST_WINDOW_GET_PRIVATE(obj);
768 priv->msg_uid = g_strdup (msg_uid);
771 parent_priv->menubar = NULL;
773 toolbar_rules_group = modest_dimming_rules_group_new (MODEST_DIMMING_RULES_TOOLBAR, TRUE);
774 clipboard_rules_group = modest_dimming_rules_group_new (MODEST_DIMMING_RULES_CLIPBOARD, FALSE);
777 /* Add common dimming rules */
778 modest_dimming_rules_group_add_rules (toolbar_rules_group,
779 modest_msg_view_toolbar_dimming_entries,
780 G_N_ELEMENTS (modest_msg_view_toolbar_dimming_entries),
781 MODEST_WINDOW (self));
782 modest_dimming_rules_group_add_rules (clipboard_rules_group,
783 modest_msg_view_clipboard_dimming_entries,
784 G_N_ELEMENTS (modest_msg_view_clipboard_dimming_entries),
785 MODEST_WINDOW (self));
787 /* Insert dimming rules group for this window */
788 modest_ui_dimming_manager_insert_rules_group (parent_priv->ui_dimming_manager, toolbar_rules_group);
789 modest_ui_dimming_manager_insert_rules_group (parent_priv->ui_dimming_manager, clipboard_rules_group);
790 g_object_unref (toolbar_rules_group);
791 g_object_unref (clipboard_rules_group);
793 /* g_signal_connect (G_OBJECT(obj), "delete-event", G_CALLBACK(on_delete_event), obj); */
795 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);
796 g_signal_connect (G_OBJECT(priv->msg_view), "activate_link",
797 G_CALLBACK (modest_ui_actions_on_msg_link_clicked), obj);
798 g_signal_connect (G_OBJECT(priv->msg_view), "link_hover",
799 G_CALLBACK (modest_ui_actions_on_msg_link_hover), obj);
800 g_signal_connect (G_OBJECT(priv->msg_view), "attachment_clicked",
801 G_CALLBACK (modest_ui_actions_on_msg_attachment_clicked), obj);
802 g_signal_connect (G_OBJECT(priv->msg_view), "recpt_activated",
803 G_CALLBACK (modest_ui_actions_on_msg_recpt_activated), obj);
804 g_signal_connect (G_OBJECT(priv->msg_view), "show_details",
805 G_CALLBACK (modest_ui_actions_on_details), obj);
806 g_signal_connect (G_OBJECT(priv->msg_view), "link_contextual",
807 G_CALLBACK (modest_ui_actions_on_msg_link_contextual), obj);
808 g_signal_connect (G_OBJECT(priv->msg_view), "limit_error",
809 G_CALLBACK (modest_ui_actions_on_limit_error), obj);
810 g_signal_connect (G_OBJECT (priv->msg_view), "fetch_image",
811 G_CALLBACK (on_fetch_image), obj);
813 g_signal_connect (G_OBJECT (obj), "key-release-event",
814 G_CALLBACK (modest_msg_view_window_key_event),
817 g_signal_connect (G_OBJECT (obj), "key-press-event",
818 G_CALLBACK (modest_msg_view_window_key_event),
821 g_signal_connect (G_OBJECT (obj), "move-focus",
822 G_CALLBACK (on_move_focus), obj);
824 g_signal_connect (G_OBJECT (obj), "map-event",
825 G_CALLBACK (_modest_msg_view_window_map_event),
828 /* Mail Operation Queue */
829 priv->queue_change_handler = g_signal_connect (G_OBJECT (modest_runtime_get_mail_operation_queue ()),
831 G_CALLBACK (on_queue_changed),
834 /* Account manager */
835 priv->account_removed_handler = g_signal_connect (G_OBJECT (modest_runtime_get_account_store ()),
837 G_CALLBACK(on_account_removed),
840 modest_window_set_active_account (MODEST_WINDOW(obj), modest_account_name);
841 modest_window_set_active_mailbox (MODEST_WINDOW(obj), mailbox);
843 /* First add out toolbar ... */
844 modest_msg_view_window_show_toolbar (MODEST_WINDOW (obj), TRUE);
846 /* ... and later the find toolbar. This way find toolbar will
847 be shown over the other */
848 priv->find_toolbar = hildon_find_toolbar_new (NULL);
849 hildon_window_add_toolbar (HILDON_WINDOW (obj), GTK_TOOLBAR (priv->find_toolbar));
850 gtk_widget_set_no_show_all (priv->find_toolbar, TRUE);
851 g_signal_connect (G_OBJECT (priv->find_toolbar), "close",
852 G_CALLBACK (modest_msg_view_window_find_toolbar_close), obj);
853 g_signal_connect (G_OBJECT (priv->find_toolbar), "search",
854 G_CALLBACK (modest_msg_view_window_find_toolbar_search), obj);
855 priv->last_search = NULL;
857 /* Init the clipboard actions dim status */
858 modest_msg_view_grab_focus(MODEST_MSG_VIEW (priv->msg_view));
860 update_window_title (MODEST_MSG_VIEW_WINDOW (obj));
865 /* FIXME: parameter checks */
867 modest_msg_view_window_new_with_header_model (TnyMsg *msg,
868 const gchar *modest_account_name,
869 const gchar *mailbox,
870 const gchar *msg_uid,
872 GtkTreeRowReference *row_reference)
874 ModestMsgViewWindow *window = NULL;
875 ModestMsgViewWindowPrivate *priv = NULL;
876 TnyFolder *header_folder = NULL;
877 ModestHeaderView *header_view = NULL;
878 ModestWindowMgr *mgr = NULL;
881 modest_tny_mime_part_to_string (TNY_MIME_PART (msg), 0);
884 mgr = modest_runtime_get_window_mgr ();
885 window = MODEST_MSG_VIEW_WINDOW (modest_window_mgr_get_msg_view_window (mgr));
886 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), NULL);
888 modest_msg_view_window_construct (window, modest_account_name, mailbox, msg_uid);
890 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
891 priv->top_msg = NULL;
893 /* Remember the message list's TreeModel so we can detect changes
894 * and change the list selection when necessary: */
895 header_folder = modest_header_view_get_folder (header_view);
897 priv->is_outbox = (modest_tny_folder_guess_folder_type (header_folder) ==
898 TNY_FOLDER_TYPE_OUTBOX);
899 priv->header_folder_id = tny_folder_get_id (header_folder);
900 g_object_unref(header_folder);
903 /* Setup row references and connect signals */
904 priv->header_model = g_object_ref (model);
906 if (row_reference && gtk_tree_row_reference_valid (row_reference)) {
907 priv->row_reference = gtk_tree_row_reference_copy (row_reference);
908 priv->next_row_reference = gtk_tree_row_reference_copy (row_reference);
909 select_next_valid_row (model, &(priv->next_row_reference), TRUE, priv->is_outbox);
911 priv->row_reference = NULL;
912 priv->next_row_reference = NULL;
915 /* Connect signals */
916 priv->row_changed_handler =
917 g_signal_connect (GTK_TREE_MODEL(model), "row-changed",
918 G_CALLBACK(modest_msg_view_window_on_row_changed),
920 priv->row_deleted_handler =
921 g_signal_connect (GTK_TREE_MODEL(model), "row-deleted",
922 G_CALLBACK(modest_msg_view_window_on_row_deleted),
924 priv->row_inserted_handler =
925 g_signal_connect (GTK_TREE_MODEL(model), "row-inserted",
926 G_CALLBACK(modest_msg_view_window_on_row_inserted),
928 priv->rows_reordered_handler =
929 g_signal_connect(GTK_TREE_MODEL(model), "rows-reordered",
930 G_CALLBACK(modest_msg_view_window_on_row_reordered),
933 if (header_view != NULL){
934 modest_header_view_add_observer(header_view,
935 MODEST_HEADER_VIEW_OBSERVER(window));
938 tny_msg_view_set_msg (TNY_MSG_VIEW (priv->msg_view), msg);
939 update_window_title (MODEST_MSG_VIEW_WINDOW (window));
940 update_branding (MODEST_MSG_VIEW_WINDOW (window));
942 /* gtk_widget_show_all (GTK_WIDGET (window)); */
943 modest_msg_view_window_update_priority (window);
944 /* Check dimming rules */
945 modest_ui_actions_check_toolbar_dimming_rules (MODEST_WINDOW (window));
946 modest_ui_actions_check_menu_dimming_rules (MODEST_WINDOW (window));
947 modest_window_check_dimming_rules_group (MODEST_WINDOW (window), MODEST_DIMMING_RULES_CLIPBOARD);
949 return MODEST_WINDOW(window);
953 modest_msg_view_window_new_from_uid (const gchar *modest_account_name,
954 const gchar *mailbox,
955 const gchar *msg_uid)
957 ModestMsgViewWindow *window = NULL;
958 ModestMsgViewWindowPrivate *priv = NULL;
959 ModestWindowMgr *mgr = NULL;
961 TnyAccount *account = NULL;
963 mgr = modest_runtime_get_window_mgr ();
964 window = MODEST_MSG_VIEW_WINDOW (modest_window_mgr_get_msg_view_window (mgr));
965 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), NULL);
967 modest_msg_view_window_construct (window, modest_account_name, mailbox, msg_uid);
969 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
970 priv->top_msg = NULL;
972 is_merge = g_str_has_prefix (msg_uid, "merge:");
974 /* Get the account */
976 account = tny_account_store_find_account (TNY_ACCOUNT_STORE (modest_runtime_get_account_store ()),
979 if (is_merge || account) {
980 TnyFolder *folder = NULL;
982 /* Try to get the message, if it's already downloaded
983 we don't need to connect */
985 folder = modest_tny_folder_store_find_folder_from_uri (TNY_FOLDER_STORE (account), msg_uid);
987 ModestTnyAccountStore *account_store;
988 ModestTnyLocalFoldersAccount *local_folders_account;
990 account_store = modest_runtime_get_account_store ();
991 local_folders_account = MODEST_TNY_LOCAL_FOLDERS_ACCOUNT (
992 modest_tny_account_store_get_local_folders_account (account_store));
993 folder = modest_tny_local_folders_account_get_merged_outbox (local_folders_account);
994 g_object_unref (local_folders_account);
998 gboolean device_online;
1000 device = modest_runtime_get_device();
1001 device_online = tny_device_is_online (device);
1002 if (device_online) {
1003 message_reader (window, priv, NULL, msg_uid, folder, NULL);
1005 TnyMsg *msg = tny_folder_find_msg (folder, msg_uid, NULL);
1007 tny_msg_view_set_msg (TNY_MSG_VIEW (priv->msg_view), msg);
1008 update_window_title (MODEST_MSG_VIEW_WINDOW (window));
1009 update_branding (MODEST_MSG_VIEW_WINDOW (window));
1010 g_object_unref (msg);
1011 /* Sync flags to server */
1012 sync_flags (MODEST_MSG_VIEW_WINDOW (window));
1014 message_reader (window, priv, NULL, msg_uid, folder, NULL);
1017 g_object_unref (folder);
1022 /* Check dimming rules */
1023 modest_ui_actions_check_toolbar_dimming_rules (MODEST_WINDOW (window));
1024 modest_ui_actions_check_menu_dimming_rules (MODEST_WINDOW (window));
1025 modest_window_check_dimming_rules_group (MODEST_WINDOW (window), MODEST_DIMMING_RULES_CLIPBOARD);
1027 return MODEST_WINDOW(window);
1031 modest_msg_view_window_new_from_header_view (ModestHeaderView *header_view,
1032 const gchar *modest_account_name,
1033 const gchar *mailbox,
1034 const gchar *msg_uid,
1035 GtkTreeRowReference *row_reference)
1037 ModestMsgViewWindow *window = NULL;
1038 ModestMsgViewWindowPrivate *priv = NULL;
1039 TnyFolder *header_folder = NULL;
1040 ModestWindowMgr *mgr = NULL;
1044 mgr = modest_runtime_get_window_mgr ();
1045 window = MODEST_MSG_VIEW_WINDOW (modest_window_mgr_get_msg_view_window (mgr));
1046 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), NULL);
1048 modest_msg_view_window_construct (window, modest_account_name, mailbox, msg_uid);
1050 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1051 priv->top_msg = NULL;
1053 /* Remember the message list's TreeModel so we can detect changes
1054 * and change the list selection when necessary: */
1056 if (header_view != NULL){
1057 header_folder = modest_header_view_get_folder(header_view);
1058 /* This could happen if the header folder was
1059 unseleted before opening this msg window (for
1060 example if the user selects an account in the
1061 folder view of the main window */
1062 if (header_folder) {
1063 priv->is_outbox = (modest_tny_folder_guess_folder_type (header_folder) ==
1064 TNY_FOLDER_TYPE_OUTBOX);
1065 priv->header_folder_id = tny_folder_get_id(header_folder);
1066 g_object_unref(header_folder);
1070 /* Setup row references and connect signals */
1071 priv->header_model = gtk_tree_view_get_model (GTK_TREE_VIEW (header_view));
1072 g_object_ref (priv->header_model);
1074 if (row_reference && gtk_tree_row_reference_valid (row_reference)) {
1075 priv->row_reference = gtk_tree_row_reference_copy (row_reference);
1076 priv->next_row_reference = gtk_tree_row_reference_copy (row_reference);
1077 select_next_valid_row (priv->header_model, &(priv->next_row_reference), TRUE, priv->is_outbox);
1079 priv->row_reference = NULL;
1080 priv->next_row_reference = NULL;
1083 /* Connect signals */
1084 priv->row_changed_handler =
1085 g_signal_connect (GTK_TREE_MODEL(priv->header_model), "row-changed",
1086 G_CALLBACK(modest_msg_view_window_on_row_changed),
1088 priv->row_deleted_handler =
1089 g_signal_connect (GTK_TREE_MODEL(priv->header_model), "row-deleted",
1090 G_CALLBACK(modest_msg_view_window_on_row_deleted),
1092 priv->row_inserted_handler =
1093 g_signal_connect (GTK_TREE_MODEL(priv->header_model), "row-inserted",
1094 G_CALLBACK(modest_msg_view_window_on_row_inserted),
1096 priv->rows_reordered_handler =
1097 g_signal_connect(GTK_TREE_MODEL(priv->header_model), "rows-reordered",
1098 G_CALLBACK(modest_msg_view_window_on_row_reordered),
1101 if (header_view != NULL){
1102 modest_header_view_add_observer(header_view,
1103 MODEST_HEADER_VIEW_OBSERVER(window));
1106 tny_msg_view_set_msg (TNY_MSG_VIEW (priv->msg_view), NULL);
1107 update_branding (MODEST_MSG_VIEW_WINDOW (window));
1109 if (priv->row_reference) {
1110 path = gtk_tree_row_reference_get_path (priv->row_reference);
1111 if (gtk_tree_model_get_iter (priv->header_model, &iter, path)) {
1113 gtk_tree_model_get (priv->header_model, &iter,
1114 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
1116 message_reader (window, priv, header, NULL, NULL, priv->row_reference);
1117 g_object_unref (header);
1119 gtk_tree_path_free (path);
1121 /* Check dimming rules */
1122 modest_ui_actions_check_toolbar_dimming_rules (MODEST_WINDOW (window));
1123 modest_ui_actions_check_menu_dimming_rules (MODEST_WINDOW (window));
1124 modest_window_check_dimming_rules_group (MODEST_WINDOW (window), MODEST_DIMMING_RULES_CLIPBOARD);
1126 return MODEST_WINDOW(window);
1130 modest_msg_view_window_new_for_search_result (TnyMsg *msg,
1131 const gchar *modest_account_name,
1132 const gchar *mailbox,
1133 const gchar *msg_uid)
1135 ModestMsgViewWindow *window = NULL;
1136 ModestMsgViewWindowPrivate *priv = NULL;
1137 ModestWindowMgr *mgr = NULL;
1139 mgr = modest_runtime_get_window_mgr ();
1140 window = MODEST_MSG_VIEW_WINDOW (modest_window_mgr_get_msg_view_window (mgr));
1141 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), NULL);
1142 modest_msg_view_window_construct (window, modest_account_name, mailbox, msg_uid);
1144 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1145 priv->top_msg = NULL;
1147 /* Remember that this is a search result,
1148 * so we can disable some UI appropriately: */
1149 priv->is_search_result = TRUE;
1151 tny_msg_view_set_msg (TNY_MSG_VIEW (priv->msg_view), msg);
1152 update_branding (MODEST_MSG_VIEW_WINDOW (window));
1154 update_window_title (window);
1155 /* gtk_widget_show_all (GTK_WIDGET (window));*/
1156 modest_msg_view_window_update_priority (window);
1158 /* Check dimming rules */
1159 modest_ui_actions_check_toolbar_dimming_rules (MODEST_WINDOW (window));
1160 modest_ui_actions_check_menu_dimming_rules (MODEST_WINDOW (window));
1161 modest_window_check_dimming_rules_group (MODEST_WINDOW (window), MODEST_DIMMING_RULES_CLIPBOARD);
1163 return MODEST_WINDOW(window);
1167 modest_msg_view_window_is_other_body (ModestMsgViewWindow *self)
1169 ModestMsgViewWindowPrivate *priv = NULL;
1171 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self), FALSE);
1172 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
1174 return (priv->other_body != NULL);
1178 modest_msg_view_window_new_with_other_body (TnyMsg *msg,
1179 TnyMimePart *other_body,
1181 const gchar *modest_account_name,
1182 const gchar *mailbox,
1183 const gchar *msg_uid)
1185 GObject *obj = NULL;
1186 ModestMsgViewWindowPrivate *priv;
1187 ModestWindowMgr *mgr = NULL;
1189 g_return_val_if_fail (msg, NULL);
1190 mgr = modest_runtime_get_window_mgr ();
1191 obj = G_OBJECT (modest_window_mgr_get_msg_view_window (mgr));
1192 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (obj);
1193 modest_msg_view_window_construct (MODEST_MSG_VIEW_WINDOW (obj),
1194 modest_account_name, mailbox, msg_uid);
1197 priv->other_body = g_object_ref (other_body);
1198 modest_msg_view_set_msg_with_other_body (MODEST_MSG_VIEW (priv->msg_view), msg, other_body);
1200 tny_msg_view_set_msg (TNY_MSG_VIEW (priv->msg_view), msg);
1203 priv->top_msg = g_object_ref (top_msg);
1205 priv->top_msg = NULL;
1207 update_window_title (MODEST_MSG_VIEW_WINDOW (obj));
1208 update_branding (MODEST_MSG_VIEW_WINDOW (obj));
1210 /* gtk_widget_show_all (GTK_WIDGET (obj)); */
1212 /* Check dimming rules */
1213 modest_ui_actions_check_toolbar_dimming_rules (MODEST_WINDOW (obj));
1214 modest_ui_actions_check_menu_dimming_rules (MODEST_WINDOW (obj));
1215 modest_window_check_dimming_rules_group (MODEST_WINDOW (obj), MODEST_DIMMING_RULES_CLIPBOARD);
1217 return MODEST_WINDOW(obj);
1221 modest_msg_view_window_new_for_attachment (TnyMsg *msg,
1223 const gchar *modest_account_name,
1224 const gchar *mailbox,
1225 const gchar *msg_uid)
1227 return modest_msg_view_window_new_with_other_body (msg, NULL, top_msg, modest_account_name, mailbox, msg_uid);
1231 modest_msg_view_window_on_row_changed (GtkTreeModel *header_model,
1234 ModestMsgViewWindow *window)
1236 check_dimming_rules_after_change (window);
1240 modest_msg_view_window_on_row_deleted(GtkTreeModel *header_model,
1242 ModestMsgViewWindow *window)
1244 check_dimming_rules_after_change (window);
1246 /* The window could have dissapeared */
1249 check_dimming_rules_after_change (ModestMsgViewWindow *window)
1251 modest_ui_actions_check_menu_dimming_rules (MODEST_WINDOW (window));
1252 modest_ui_actions_check_toolbar_dimming_rules (MODEST_WINDOW (window));
1256 /* On insertions we check if the folder still has the message we are
1257 * showing or do not. If do not, we do nothing. Which means we are still
1258 * not attached to any header folder and thus next/prev buttons are
1259 * still dimmed. Once the message that is shown by msg-view is found, the
1260 * new model of header-view will be attached and the references will be set.
1261 * On each further insertions dimming rules will be checked. However
1262 * this requires extra CPU time at least works.
1263 * (An message might be deleted from TnyFolder and thus will not be
1264 * inserted into the model again for example if it is removed by the
1265 * imap server and the header view is refreshed.)
1268 modest_msg_view_window_on_row_inserted (GtkTreeModel *model,
1269 GtkTreePath *tree_path,
1270 GtkTreeIter *tree_iter,
1271 ModestMsgViewWindow *window)
1273 ModestMsgViewWindowPrivate *priv = NULL;
1274 TnyHeader *header = NULL;
1276 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window));
1277 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1279 g_assert (model == priv->header_model);
1281 /* Check if the newly inserted message is the same we are actually
1282 * showing. IF not, we should remain detached from the header model
1283 * and thus prev and next toolbar buttons should remain dimmed. */
1284 gtk_tree_model_get (model, tree_iter,
1285 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
1288 if (TNY_IS_HEADER (header)) {
1291 uid = modest_tny_folder_get_header_unique_id (header);
1292 if (!g_str_equal(priv->msg_uid, uid)) {
1293 check_dimming_rules_after_change (window);
1295 g_object_unref (G_OBJECT(header));
1299 g_object_unref(G_OBJECT(header));
1302 if (priv->row_reference) {
1303 gtk_tree_row_reference_free (priv->row_reference);
1306 /* Setup row_reference for the actual msg. */
1307 priv->row_reference = gtk_tree_row_reference_new (priv->header_model, tree_path);
1308 if (priv->row_reference == NULL) {
1309 g_warning("%s: No reference for msg header item.", __FUNCTION__);
1313 /* Now set up next_row_reference. */
1314 if (priv->next_row_reference) {
1315 gtk_tree_row_reference_free (priv->next_row_reference);
1318 priv->next_row_reference = gtk_tree_row_reference_copy (priv->row_reference);
1319 select_next_valid_row (priv->header_model,
1320 &(priv->next_row_reference), FALSE, priv->is_outbox);
1322 /* Connect the remaining callbacks to become able to detect
1323 * changes in header-view. */
1324 priv->row_changed_handler =
1325 g_signal_connect (priv->header_model, "row-changed",
1326 G_CALLBACK (modest_msg_view_window_on_row_changed),
1328 priv->row_deleted_handler =
1329 g_signal_connect (priv->header_model, "row-deleted",
1330 G_CALLBACK (modest_msg_view_window_on_row_deleted),
1332 priv->rows_reordered_handler =
1333 g_signal_connect (priv->header_model, "rows-reordered",
1334 G_CALLBACK (modest_msg_view_window_on_row_reordered),
1337 check_dimming_rules_after_change (window);
1341 modest_msg_view_window_on_row_reordered (GtkTreeModel *header_model,
1345 ModestMsgViewWindow *window)
1347 ModestMsgViewWindowPrivate *priv = NULL;
1348 gboolean already_changed = FALSE;
1350 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(window);
1352 /* If the current row was reordered select the proper next
1353 valid row. The same if the next row reference changes */
1354 if (!priv->row_reference ||
1355 !gtk_tree_row_reference_valid (priv->row_reference))
1358 if (priv->next_row_reference &&
1359 gtk_tree_row_reference_valid (priv->next_row_reference)) {
1360 GtkTreePath *cur, *next;
1361 /* Check that the order is still the correct one */
1362 cur = gtk_tree_row_reference_get_path (priv->row_reference);
1363 next = gtk_tree_row_reference_get_path (priv->next_row_reference);
1364 gtk_tree_path_next (cur);
1365 if (gtk_tree_path_compare (cur, next) != 0) {
1366 gtk_tree_row_reference_free (priv->next_row_reference);
1367 priv->next_row_reference = gtk_tree_row_reference_copy (priv->row_reference);
1368 select_next_valid_row (header_model, &(priv->next_row_reference), FALSE, priv->is_outbox);
1369 already_changed = TRUE;
1371 gtk_tree_path_free (cur);
1372 gtk_tree_path_free (next);
1374 if (priv->next_row_reference)
1375 gtk_tree_row_reference_free (priv->next_row_reference);
1376 /* Update next row reference */
1377 priv->next_row_reference = gtk_tree_row_reference_copy (priv->row_reference);
1378 select_next_valid_row (header_model, &(priv->next_row_reference), FALSE, priv->is_outbox);
1379 already_changed = TRUE;
1382 check_dimming_rules_after_change (window);
1385 /* The modest_msg_view_window_update_model_replaced implements update
1386 * function for ModestHeaderViewObserver. Checks whether the TnyFolder
1387 * actually belongs to the header-view is the same as the TnyFolder of
1388 * the message of msg-view or not. If they are different, there is
1389 * nothing to do. If they are the same, then the model has replaced and
1390 * the reference in msg-view shall be replaced from the old model to
1391 * the new model. In this case the view will be detached from it's
1392 * header folder. From this point the next/prev buttons are dimmed.
1395 modest_msg_view_window_update_model_replaced (ModestHeaderViewObserver *observer,
1396 GtkTreeModel *model,
1397 const gchar *tny_folder_id)
1399 ModestMsgViewWindowPrivate *priv = NULL;
1400 ModestMsgViewWindow *window = NULL;
1402 g_assert(MODEST_IS_HEADER_VIEW_OBSERVER(observer));
1403 g_assert(MODEST_IS_MSG_VIEW_WINDOW(observer));
1405 window = MODEST_MSG_VIEW_WINDOW(observer);
1406 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(window);
1408 /* If there is an other folder in the header-view then we do
1409 * not care about it's model (msg list). Else if the
1410 * header-view shows the folder the msg shown by us is in, we
1411 * shall replace our model reference and make some check. */
1412 if(model == NULL || tny_folder_id == NULL ||
1413 (priv->header_folder_id && !g_str_equal(tny_folder_id, priv->header_folder_id)))
1416 /* Model is changed(replaced), so we should forget the old
1417 * one. Because there might be other references and there
1418 * might be some change on the model even if we unreferenced
1419 * it, we need to disconnect our signals here. */
1420 if (priv->header_model) {
1421 if (g_signal_handler_is_connected(G_OBJECT (priv->header_model),
1422 priv->row_changed_handler))
1423 g_signal_handler_disconnect(G_OBJECT (priv->header_model),
1424 priv->row_changed_handler);
1425 if (g_signal_handler_is_connected(G_OBJECT (priv->header_model),
1426 priv->row_deleted_handler))
1427 g_signal_handler_disconnect(G_OBJECT (priv->header_model),
1428 priv->row_deleted_handler);
1429 if (g_signal_handler_is_connected(G_OBJECT (priv->header_model),
1430 priv->row_inserted_handler))
1431 g_signal_handler_disconnect(G_OBJECT (priv->header_model),
1432 priv->row_inserted_handler);
1433 if (g_signal_handler_is_connected(G_OBJECT (priv->header_model),
1434 priv->rows_reordered_handler))
1435 g_signal_handler_disconnect(G_OBJECT (priv->header_model),
1436 priv->rows_reordered_handler);
1439 if (priv->row_reference)
1440 gtk_tree_row_reference_free (priv->row_reference);
1441 if (priv->next_row_reference)
1442 gtk_tree_row_reference_free (priv->next_row_reference);
1443 g_object_unref(priv->header_model);
1446 priv->row_changed_handler = 0;
1447 priv->row_deleted_handler = 0;
1448 priv->row_inserted_handler = 0;
1449 priv->rows_reordered_handler = 0;
1450 priv->next_row_reference = NULL;
1451 priv->row_reference = NULL;
1452 priv->header_model = NULL;
1455 priv->header_model = g_object_ref (model);
1457 /* Also we must connect to the new model for row insertions.
1458 * Only for insertions now. We will need other ones only after
1459 * the msg is show by msg-view is added to the new model. */
1460 priv->row_inserted_handler =
1461 g_signal_connect (priv->header_model, "row-inserted",
1462 G_CALLBACK(modest_msg_view_window_on_row_inserted),
1465 modest_ui_actions_check_menu_dimming_rules(MODEST_WINDOW(window));
1466 modest_ui_actions_check_toolbar_dimming_rules(MODEST_WINDOW(window));
1470 modest_msg_view_window_toolbar_on_transfer_mode (ModestMsgViewWindow *self)
1472 ModestMsgViewWindowPrivate *priv= NULL;
1474 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self), FALSE);
1475 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
1477 return priv->progress_hint;
1481 modest_msg_view_window_get_header (ModestMsgViewWindow *self)
1483 ModestMsgViewWindowPrivate *priv= NULL;
1485 TnyHeader *header = NULL;
1486 GtkTreePath *path = NULL;
1489 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self), NULL);
1490 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
1492 /* If the message was not obtained from a treemodel,
1493 * for instance if it was opened directly by the search UI:
1495 if (priv->header_model == NULL ||
1496 priv->row_reference == NULL ||
1497 !gtk_tree_row_reference_valid (priv->row_reference)) {
1498 msg = modest_msg_view_window_get_message (self);
1500 header = tny_msg_get_header (msg);
1501 g_object_unref (msg);
1506 /* Get iter of the currently selected message in the header view: */
1507 path = gtk_tree_row_reference_get_path (priv->row_reference);
1508 g_return_val_if_fail (path != NULL, NULL);
1509 gtk_tree_model_get_iter (priv->header_model,
1513 /* Get current message header */
1514 gtk_tree_model_get (priv->header_model, &iter,
1515 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
1518 gtk_tree_path_free (path);
1523 modest_msg_view_window_get_message (ModestMsgViewWindow *self)
1525 ModestMsgViewWindowPrivate *priv;
1527 g_return_val_if_fail (self, NULL);
1529 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(self);
1531 return tny_msg_view_get_msg (TNY_MSG_VIEW (priv->msg_view));
1535 modest_msg_view_window_get_top_message (ModestMsgViewWindow *self)
1537 ModestMsgViewWindowPrivate *priv;
1539 g_return_val_if_fail (self, NULL);
1541 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(self);
1544 return g_object_ref (priv->top_msg);
1550 modest_msg_view_window_get_message_uid (ModestMsgViewWindow *self)
1552 ModestMsgViewWindowPrivate *priv;
1554 g_return_val_if_fail (self, NULL);
1556 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
1558 return (const gchar*) priv->msg_uid;
1561 /* Used for the Ctrl+F accelerator */
1563 modest_msg_view_window_toggle_find_toolbar (GtkWidget *obj,
1566 ModestMsgViewWindow *window = MODEST_MSG_VIEW_WINDOW (data);
1567 ModestMsgViewWindowPrivate *priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1569 if (GTK_WIDGET_VISIBLE (priv->find_toolbar)) {
1570 modest_msg_view_window_find_toolbar_close (obj, data);
1572 modest_msg_view_window_show_find_toolbar (obj, data);
1576 /* Handler for menu option */
1578 modest_msg_view_window_show_find_toolbar (GtkWidget *obj,
1581 ModestMsgViewWindow *window = MODEST_MSG_VIEW_WINDOW (data);
1582 ModestMsgViewWindowPrivate *priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1584 gtk_widget_show (priv->find_toolbar);
1585 hildon_find_toolbar_highlight_entry (HILDON_FIND_TOOLBAR (priv->find_toolbar), TRUE);
1588 /* Handler for click on the "X" close button in find toolbar */
1590 modest_msg_view_window_find_toolbar_close (GtkWidget *widget,
1591 ModestMsgViewWindow *obj)
1593 ModestMsgViewWindowPrivate *priv;
1595 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (obj);
1598 gtk_widget_hide (priv->find_toolbar);
1599 modest_msg_view_grab_focus (MODEST_MSG_VIEW (priv->msg_view));
1603 modest_msg_view_window_find_toolbar_search (GtkWidget *widget,
1604 ModestMsgViewWindow *obj)
1606 gchar *current_search;
1607 ModestMsgViewWindowPrivate *priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (obj);
1609 if (modest_mime_part_view_is_empty (MODEST_MIME_PART_VIEW (priv->msg_view))) {
1610 hildon_banner_show_information (NULL, NULL, _("mail_ib_nothing_to_find"));
1614 g_object_get (G_OBJECT (widget), "prefix", ¤t_search, NULL);
1616 if ((current_search == NULL) || (strcmp (current_search, "") == 0)) {
1617 g_free (current_search);
1618 hildon_banner_show_information (NULL, NULL, _CS("ecdg_ib_find_rep_enter_text"));
1622 if ((priv->last_search == NULL) || (strcmp (priv->last_search, current_search) != 0)) {
1624 g_free (priv->last_search);
1625 priv->last_search = g_strdup (current_search);
1626 result = modest_isearch_view_search (MODEST_ISEARCH_VIEW (priv->msg_view),
1629 hildon_banner_show_information (NULL, NULL,
1630 _HL("ckct_ib_find_no_matches"));
1631 g_free (priv->last_search);
1632 priv->last_search = NULL;
1634 hildon_find_toolbar_highlight_entry (HILDON_FIND_TOOLBAR (priv->find_toolbar), TRUE);
1637 if (!modest_isearch_view_search_next (MODEST_ISEARCH_VIEW (priv->msg_view))) {
1638 hildon_banner_show_information (NULL, NULL,
1639 _HL("ckct_ib_find_search_complete"));
1640 g_free (priv->last_search);
1641 priv->last_search = NULL;
1643 hildon_find_toolbar_highlight_entry (HILDON_FIND_TOOLBAR (priv->find_toolbar), TRUE);
1647 g_free (current_search);
1652 modest_msg_view_window_set_zoom (ModestWindow *window,
1655 ModestMsgViewWindowPrivate *priv;
1656 ModestWindowPrivate *parent_priv;
1658 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window));
1660 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1661 parent_priv = MODEST_WINDOW_GET_PRIVATE (window);
1662 modest_zoomable_set_zoom (MODEST_ZOOMABLE (priv->msg_view), zoom);
1667 modest_msg_view_window_get_zoom (ModestWindow *window)
1669 ModestMsgViewWindowPrivate *priv;
1671 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), 1.0);
1673 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1674 return modest_zoomable_get_zoom (MODEST_ZOOMABLE (priv->msg_view));
1678 modest_msg_view_window_zoom_plus (ModestWindow *window)
1681 ModestMsgViewWindowPrivate *priv;
1685 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), 1.0);
1686 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1688 zoom_level = modest_zoomable_get_zoom (MODEST_ZOOMABLE (priv->msg_view));
1690 if (zoom_level >= 2.0) {
1691 hildon_banner_show_information (NULL, NULL,
1692 _CS("ckct_ib_max_zoom_level_reached"));
1694 } else if (zoom_level >= 1.5) {
1696 } else if (zoom_level >= 1.2) {
1698 } else if (zoom_level >= 1.0) {
1700 } else if (zoom_level >= 0.8) {
1702 } else if (zoom_level >= 0.5) {
1708 /* set zoom level */
1709 int_zoom = (gint) rint (zoom_level*100.0+0.1);
1710 banner_text = g_strdup_printf (_HL("wdgt_ib_zoom"), int_zoom);
1711 modest_platform_information_banner (GTK_WIDGET (window), NULL, banner_text);
1712 g_free (banner_text);
1713 modest_zoomable_set_zoom (MODEST_ZOOMABLE (priv->msg_view), zoom_level);
1719 modest_msg_view_window_zoom_minus (ModestWindow *window)
1722 ModestMsgViewWindowPrivate *priv;
1726 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), 1.0);
1727 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1729 zoom_level = modest_zoomable_get_zoom (MODEST_ZOOMABLE (priv->msg_view));
1731 if (zoom_level <= 0.5) {
1732 hildon_banner_show_information (NULL, NULL,
1733 _CS("ckct_ib_min_zoom_level_reached"));
1735 } else if (zoom_level <= 0.8) {
1737 } else if (zoom_level <= 1.0) {
1739 } else if (zoom_level <= 1.2) {
1741 } else if (zoom_level <= 1.5) {
1743 } else if (zoom_level <= 2.0) {
1749 /* set zoom level */
1750 int_zoom = (gint) rint (zoom_level*100.0+0.1);
1751 banner_text = g_strdup_printf (_HL("wdgt_ib_zoom"), int_zoom);
1752 modest_platform_information_banner (GTK_WIDGET (window), NULL, banner_text);
1753 g_free (banner_text);
1754 modest_zoomable_set_zoom (MODEST_ZOOMABLE (priv->msg_view), zoom_level);
1760 modest_msg_view_window_key_event (GtkWidget *window,
1766 focus = gtk_window_get_focus (GTK_WINDOW (window));
1768 /* for the find toolbar case */
1769 if (focus && GTK_IS_ENTRY (focus)) {
1770 if (event->keyval == GDK_BackSpace) {
1772 copy = gdk_event_copy ((GdkEvent *) event);
1773 gtk_widget_event (focus, copy);
1774 gdk_event_free (copy);
1784 modest_msg_view_window_last_message_selected (ModestMsgViewWindow *window)
1787 ModestMsgViewWindowPrivate *priv;
1788 GtkTreeIter tmp_iter;
1789 gboolean is_last_selected;
1791 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), TRUE);
1792 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1794 /*if no model (so no rows at all), then virtually we are the last*/
1795 if (!priv->header_model || !priv->row_reference)
1798 if (!gtk_tree_row_reference_valid (priv->row_reference))
1801 path = gtk_tree_row_reference_get_path (priv->row_reference);
1805 is_last_selected = TRUE;
1806 while (is_last_selected) {
1808 gtk_tree_path_next (path);
1809 if (!gtk_tree_model_get_iter (priv->header_model, &tmp_iter, path))
1811 gtk_tree_model_get (priv->header_model, &tmp_iter,
1812 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
1815 if (msg_is_visible (header, priv->is_outbox))
1816 is_last_selected = FALSE;
1817 g_object_unref(G_OBJECT(header));
1820 gtk_tree_path_free (path);
1821 return is_last_selected;
1825 modest_msg_view_window_has_headers_model (ModestMsgViewWindow *window)
1827 ModestMsgViewWindowPrivate *priv;
1829 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), TRUE);
1830 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1832 return priv->header_model != NULL;
1836 modest_msg_view_window_is_search_result (ModestMsgViewWindow *window)
1838 ModestMsgViewWindowPrivate *priv;
1840 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), TRUE);
1841 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1843 return priv->is_search_result;
1847 msg_is_visible (TnyHeader *header, gboolean check_outbox)
1849 if ((tny_header_get_flags(header) & TNY_HEADER_FLAG_DELETED))
1851 if (!check_outbox) {
1854 ModestTnySendQueueStatus status;
1855 status = modest_tny_all_send_queues_get_msg_status (header);
1856 return ((status != MODEST_TNY_SEND_QUEUE_FAILED) &&
1857 (status != MODEST_TNY_SEND_QUEUE_SENDING));
1862 modest_msg_view_window_first_message_selected (ModestMsgViewWindow *window)
1865 ModestMsgViewWindowPrivate *priv;
1866 gboolean is_first_selected;
1867 GtkTreeIter tmp_iter;
1869 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), TRUE);
1870 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1872 /*if no model (so no rows at all), then virtually we are the first*/
1873 if (!priv->header_model || !priv->row_reference)
1876 if (!gtk_tree_row_reference_valid (priv->row_reference))
1879 path = gtk_tree_row_reference_get_path (priv->row_reference);
1883 is_first_selected = TRUE;
1884 while (is_first_selected) {
1886 if(!gtk_tree_path_prev (path))
1888 /* Here the 'if' is needless for logic, but let make sure
1889 * iter is valid for gtk_tree_model_get. */
1890 if (!gtk_tree_model_get_iter (priv->header_model, &tmp_iter, path))
1892 gtk_tree_model_get (priv->header_model, &tmp_iter,
1893 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
1896 if (msg_is_visible (header, priv->is_outbox))
1897 is_first_selected = FALSE;
1898 g_object_unref(G_OBJECT(header));
1901 gtk_tree_path_free (path);
1902 return is_first_selected;
1909 GtkTreeRowReference *row_reference;
1913 message_reader_performer (gboolean canceled,
1915 GtkWindow *parent_window,
1916 TnyAccount *account,
1919 ModestMailOperation *mail_op = NULL;
1920 MsgReaderInfo *info;
1922 info = (MsgReaderInfo *) user_data;
1923 if (canceled || err) {
1924 update_window_title (MODEST_MSG_VIEW_WINDOW (parent_window));
1925 modest_ui_actions_on_close_window (NULL, MODEST_WINDOW (parent_window));
1929 /* Register the header - it'll be unregistered in the callback */
1931 modest_window_mgr_register_header (modest_runtime_get_window_mgr (), info->header, NULL);
1933 /* New mail operation */
1934 mail_op = modest_mail_operation_new_with_error_handling (G_OBJECT(parent_window),
1935 modest_ui_actions_disk_operations_error_handler,
1938 modest_mail_operation_queue_add (modest_runtime_get_mail_operation_queue (), mail_op);
1940 modest_mail_operation_get_msg (mail_op, info->header, TRUE, view_msg_cb, info->row_reference);
1942 modest_mail_operation_find_msg (mail_op, info->folder, info->msg_uid, TRUE, view_msg_cb, NULL);
1943 g_object_unref (mail_op);
1945 /* Update dimming rules */
1946 modest_ui_actions_check_toolbar_dimming_rules (MODEST_WINDOW (parent_window));
1947 modest_ui_actions_check_menu_dimming_rules (MODEST_WINDOW (parent_window));
1950 /* Frees. The row_reference will be freed by the view_msg_cb callback */
1951 g_free (info->msg_uid);
1953 g_object_unref (info->folder);
1955 g_object_unref (info->header);
1956 g_slice_free (MsgReaderInfo, info);
1961 * Reads the message whose summary item is @header. It takes care of
1962 * several things, among others:
1964 * If the message was not previously downloaded then ask the user
1965 * before downloading. If there is no connection launch the connection
1966 * dialog. Update toolbar dimming rules.
1968 * Returns: TRUE if the mail operation was started, otherwise if the
1969 * user do not want to download the message, or if the user do not
1970 * want to connect, then the operation is not issued
1973 message_reader (ModestMsgViewWindow *window,
1974 ModestMsgViewWindowPrivate *priv,
1976 const gchar *msg_uid,
1978 GtkTreeRowReference *row_reference)
1980 ModestWindowMgr *mgr;
1981 TnyAccount *account = NULL;
1982 MsgReaderInfo *info;
1984 /* We set the header from model while we're loading */
1985 tny_header_view_set_header (TNY_HEADER_VIEW (priv->msg_view), header);
1986 gtk_window_set_title (GTK_WINDOW (window), _CS("ckdg_pb_updating"));
1992 g_object_ref (folder);
1994 mgr = modest_runtime_get_window_mgr ();
1995 /* Msg download completed */
1996 if (!header || !(tny_header_get_flags (header) & TNY_HEADER_FLAG_CACHED)) {
1998 /* Ask the user if he wants to download the message if
2000 if (!tny_device_is_online (modest_runtime_get_device())) {
2001 GtkResponseType response;
2003 response = modest_platform_run_confirmation_dialog (GTK_WINDOW (window),
2004 _("mcen_nc_get_msg"));
2005 if (response == GTK_RESPONSE_CANCEL) {
2006 update_window_title (window);
2011 folder = tny_header_get_folder (header);
2013 info = g_slice_new (MsgReaderInfo);
2014 info->msg_uid = g_strdup (msg_uid);
2016 info->header = g_object_ref (header);
2018 info->header = NULL;
2020 info->folder = g_object_ref (folder);
2022 info->folder = NULL;
2023 if (row_reference) {
2024 info->row_reference = gtk_tree_row_reference_copy (row_reference);
2026 info->row_reference = NULL;
2029 /* Offer the connection dialog if necessary */
2030 modest_platform_connect_if_remote_and_perform ((GtkWindow *) window,
2032 TNY_FOLDER_STORE (folder),
2033 message_reader_performer,
2036 g_object_unref (folder);
2042 folder = tny_header_get_folder (header);
2045 account = tny_folder_get_account (folder);
2047 info = g_slice_new (MsgReaderInfo);
2048 info->msg_uid = g_strdup (msg_uid);
2050 info->folder = g_object_ref (folder);
2052 info->folder = NULL;
2054 info->header = g_object_ref (header);
2056 info->header = NULL;
2058 info->row_reference = gtk_tree_row_reference_copy (row_reference);
2060 info->row_reference = NULL;
2062 message_reader_performer (FALSE, NULL, (GtkWindow *) window, account, info);
2064 g_object_unref (account);
2066 g_object_unref (folder);
2072 modest_msg_view_window_select_next_message (ModestMsgViewWindow *window)
2074 ModestMsgViewWindowPrivate *priv;
2075 GtkTreePath *path= NULL;
2076 GtkTreeIter tmp_iter;
2078 gboolean retval = TRUE;
2079 GtkTreeRowReference *row_reference = NULL;
2081 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), FALSE);
2082 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
2084 if (!priv->row_reference)
2087 /* Update the next row reference if it's not valid. This could
2088 happen if for example the header which it was pointing to,
2089 was deleted. The best place to do it is in the row-deleted
2090 handler but the tinymail model do not work like the glib
2091 tree models and reports the deletion when the row is still
2093 if (!gtk_tree_row_reference_valid (priv->next_row_reference)) {
2094 if (priv->next_row_reference) {
2095 gtk_tree_row_reference_free (priv->next_row_reference);
2097 if (gtk_tree_row_reference_valid (priv->row_reference)) {
2098 priv->next_row_reference = gtk_tree_row_reference_copy (priv->row_reference);
2099 select_next_valid_row (priv->header_model, &(priv->next_row_reference), FALSE, priv->is_outbox);
2101 priv->next_row_reference = NULL;
2104 if (priv->next_row_reference)
2105 path = gtk_tree_row_reference_get_path (priv->next_row_reference);
2109 row_reference = gtk_tree_row_reference_copy (priv->next_row_reference);
2111 gtk_tree_model_get_iter (priv->header_model,
2114 gtk_tree_path_free (path);
2116 gtk_tree_model_get (priv->header_model, &tmp_iter,
2117 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
2120 /* Read the message & show it */
2121 if (!message_reader (window, priv, header, NULL, NULL, row_reference)) {
2124 gtk_tree_row_reference_free (row_reference);
2127 g_object_unref (header);
2133 modest_msg_view_window_select_previous_message (ModestMsgViewWindow *window)
2135 ModestMsgViewWindowPrivate *priv = NULL;
2137 gboolean finished = FALSE;
2138 gboolean retval = FALSE;
2140 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), FALSE);
2141 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
2143 if (priv->row_reference && !gtk_tree_row_reference_valid (priv->row_reference)) {
2144 gtk_tree_row_reference_free (priv->row_reference);
2145 priv->row_reference = NULL;
2148 /* Return inmediatly if there is no header model */
2149 if (!priv->header_model || !priv->row_reference)
2152 path = gtk_tree_row_reference_get_path (priv->row_reference);
2153 while (!finished && gtk_tree_path_prev (path)) {
2157 gtk_tree_model_get_iter (priv->header_model, &iter, path);
2158 gtk_tree_model_get (priv->header_model, &iter,
2159 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
2163 if (msg_is_visible (header, priv->is_outbox)) {
2164 GtkTreeRowReference *row_reference;
2165 row_reference = gtk_tree_row_reference_new (priv->header_model, path);
2166 /* Read the message & show it */
2167 retval = message_reader (window, priv, header, NULL, NULL, row_reference);
2168 gtk_tree_row_reference_free (row_reference);
2172 g_object_unref (header);
2176 gtk_tree_path_free (path);
2181 view_msg_cb (ModestMailOperation *mail_op,
2188 ModestMsgViewWindow *self = NULL;
2189 ModestMsgViewWindowPrivate *priv = NULL;
2190 GtkTreeRowReference *row_reference = NULL;
2192 /* Unregister the header (it was registered before creating the mail operation) */
2193 modest_window_mgr_unregister_header (modest_runtime_get_window_mgr (), header);
2195 row_reference = (GtkTreeRowReference *) user_data;
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 /* If there was any error */
2210 if (!modest_ui_actions_msg_retrieval_check (mail_op, header, msg)) {
2212 gtk_tree_row_reference_free (row_reference);
2213 self = (ModestMsgViewWindow *) modest_mail_operation_get_source (mail_op);
2215 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
2216 /* First we check if the parent is a folder window */
2217 if (priv->msg_uid && !modest_hildon2_window_mgr_get_folder_window (MODEST_HILDON2_WINDOW_MGR (modest_runtime_get_window_mgr ()))) {
2219 TnyAccount *account = NULL;
2220 GtkWidget *header_window = NULL;
2222 is_merge = g_str_has_prefix (priv->msg_uid, "merge:");
2224 /* Get the account */
2226 account = tny_account_store_find_account (TNY_ACCOUNT_STORE (modest_runtime_get_account_store ()),
2229 if (is_merge || account) {
2230 TnyFolder *folder = NULL;
2232 /* Try to get the message, if it's already downloaded
2233 we don't need to connect */
2235 folder = modest_tny_folder_store_find_folder_from_uri (TNY_FOLDER_STORE (account),
2238 ModestTnyAccountStore *account_store;
2239 ModestTnyLocalFoldersAccount *local_folders_account;
2241 account_store = modest_runtime_get_account_store ();
2242 local_folders_account = MODEST_TNY_LOCAL_FOLDERS_ACCOUNT (
2243 modest_tny_account_store_get_local_folders_account (account_store));
2244 folder = modest_tny_local_folders_account_get_merged_outbox (local_folders_account);
2245 g_object_unref (local_folders_account);
2247 if (account) g_object_unref (account);
2250 header_window = (GtkWidget *)
2251 modest_header_window_new (
2253 modest_window_get_active_account (MODEST_WINDOW (self)),
2254 modest_window_get_active_mailbox (MODEST_WINDOW (self)));
2255 if (!modest_window_mgr_register_window (modest_runtime_get_window_mgr (),
2256 MODEST_WINDOW (header_window),
2258 gtk_widget_destroy (GTK_WIDGET (header_window));
2260 gtk_widget_show_all (GTK_WIDGET (header_window));
2262 g_object_unref (folder);
2268 /* Restore window title */
2269 update_window_title (self);
2270 modest_ui_actions_on_close_window (NULL, MODEST_WINDOW (self));
2271 g_object_unref (self);
2276 if (msg && TNY_IS_CAMEL_BS_MSG (msg)) {
2278 body = modest_tny_msg_find_body_part (msg, TRUE);
2280 if (body && !tny_camel_bs_mime_part_is_fetched (TNY_CAMEL_BS_MIME_PART (body))) {
2281 /* We have body structure but not the body mime part. What we do
2282 * is restarting load of message */
2283 self = (ModestMsgViewWindow *) modest_mail_operation_get_source (mail_op);
2284 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
2286 tny_header_unset_flag (TNY_HEADER (header), TNY_HEADER_FLAG_CACHED);
2288 modest_msg_view_window_reload (self);
2291 gtk_tree_row_reference_free (row_reference);
2292 g_object_unref (body);
2297 g_object_unref (body);
2300 /* Get the window */
2301 self = (ModestMsgViewWindow *) modest_mail_operation_get_source (mail_op);
2302 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self));
2303 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
2305 /* Update the row reference */
2306 if (priv->row_reference != NULL) {
2307 gtk_tree_row_reference_free (priv->row_reference);
2308 priv->row_reference = (row_reference && gtk_tree_row_reference_valid (row_reference))?gtk_tree_row_reference_copy (row_reference):NULL;
2309 if (priv->next_row_reference != NULL) {
2310 gtk_tree_row_reference_free (priv->next_row_reference);
2312 if (priv->row_reference) {
2313 priv->next_row_reference = gtk_tree_row_reference_copy (priv->row_reference);
2314 select_next_valid_row (priv->header_model, &(priv->next_row_reference), TRUE, priv->is_outbox);
2316 priv->next_row_reference = NULL;
2320 /* Mark header as read */
2321 if (!(tny_header_get_flags (header) & TNY_HEADER_FLAG_SEEN))
2322 tny_header_set_flag (header, TNY_HEADER_FLAG_SEEN);
2324 /* Set new message */
2325 if (priv->msg_view != NULL && TNY_IS_MSG_VIEW (priv->msg_view)) {
2326 tny_msg_view_set_msg (TNY_MSG_VIEW (priv->msg_view), msg);
2327 modest_msg_view_window_update_priority (self);
2328 update_window_title (MODEST_MSG_VIEW_WINDOW (self));
2329 update_branding (MODEST_MSG_VIEW_WINDOW (self));
2330 modest_msg_view_grab_focus (MODEST_MSG_VIEW (priv->msg_view));
2333 /* Set the new message uid of the window */
2334 if (priv->msg_uid) {
2335 g_free (priv->msg_uid);
2336 priv->msg_uid = modest_tny_folder_get_header_unique_id (header);
2339 /* Notify the observers */
2340 g_signal_emit (G_OBJECT (self), signals[MSG_CHANGED_SIGNAL],
2341 0, priv->header_model, priv->row_reference);
2343 /* Sync the flags if the message is not opened from a header
2344 model, i.e, if it's opened from a notification */
2345 if (!priv->header_model)
2349 g_object_unref (self);
2351 gtk_tree_row_reference_free (row_reference);
2355 modest_msg_view_window_get_folder_type (ModestMsgViewWindow *window)
2357 ModestMsgViewWindowPrivate *priv;
2359 TnyFolderType folder_type;
2361 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
2363 folder_type = TNY_FOLDER_TYPE_UNKNOWN;
2365 msg = tny_msg_view_get_msg (TNY_MSG_VIEW (priv->msg_view));
2369 folder = tny_msg_get_folder (msg);
2371 folder_type = modest_tny_folder_guess_folder_type (folder);
2372 g_object_unref (folder);
2374 g_object_unref (msg);
2382 modest_msg_view_window_update_priority (ModestMsgViewWindow *window)
2384 ModestMsgViewWindowPrivate *priv;
2385 TnyHeader *header = NULL;
2386 TnyHeaderFlags flags = 0;
2388 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
2390 if (priv->header_model && priv->row_reference && gtk_tree_row_reference_valid (priv->row_reference)) {
2392 GtkTreePath *path = NULL;
2394 path = gtk_tree_row_reference_get_path (priv->row_reference);
2395 g_return_if_fail (path != NULL);
2396 gtk_tree_model_get_iter (priv->header_model,
2398 gtk_tree_row_reference_get_path (priv->row_reference));
2400 gtk_tree_model_get (priv->header_model, &iter, TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
2402 gtk_tree_path_free (path);
2405 msg = tny_msg_view_get_msg (TNY_MSG_VIEW (priv->msg_view));
2407 header = tny_msg_get_header (msg);
2408 g_object_unref (msg);
2413 flags = tny_header_get_flags (header);
2414 g_object_unref(G_OBJECT(header));
2417 modest_msg_view_set_priority (MODEST_MSG_VIEW(priv->msg_view), flags);
2422 toolbar_resize (ModestMsgViewWindow *self)
2424 ModestMsgViewWindowPrivate *priv = NULL;
2425 ModestWindowPrivate *parent_priv = NULL;
2427 gint static_button_size;
2428 ModestWindowMgr *mgr;
2430 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self));
2431 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
2432 parent_priv = MODEST_WINDOW_GET_PRIVATE(self);
2434 mgr = modest_runtime_get_window_mgr ();
2435 static_button_size = modest_window_mgr_get_fullscreen_mode (mgr)?120:120;
2437 if (parent_priv->toolbar) {
2438 /* Set expandable and homogeneous tool buttons */
2439 widget = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/ToolbarMessageReply");
2440 gtk_tool_item_set_expand (GTK_TOOL_ITEM (widget), TRUE);
2441 gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (widget), TRUE);
2442 widget = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/ToolbarMessageReplyAll");
2443 gtk_tool_item_set_expand (GTK_TOOL_ITEM (widget), TRUE);
2444 gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (widget), TRUE);
2445 widget = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/ToolbarMessageForward");
2446 gtk_tool_item_set_expand (GTK_TOOL_ITEM (widget), TRUE);
2447 gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (widget), TRUE);
2448 widget = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/ToolbarDeleteMessage");
2449 gtk_tool_item_set_expand (GTK_TOOL_ITEM (widget), TRUE);
2450 gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (widget), TRUE);
2451 widget = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/ToolbarDownloadExternalImages");
2452 gtk_tool_item_set_expand (GTK_TOOL_ITEM (widget), TRUE);
2453 gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (widget), TRUE);
2454 gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (priv->next_toolitem), TRUE);
2455 gtk_tool_item_set_expand (GTK_TOOL_ITEM (priv->next_toolitem), TRUE);
2456 gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (priv->prev_toolitem), TRUE);
2457 gtk_tool_item_set_expand (GTK_TOOL_ITEM (priv->prev_toolitem), TRUE);
2462 modest_msg_view_window_show_toolbar (ModestWindow *self,
2463 gboolean show_toolbar)
2465 ModestMsgViewWindowPrivate *priv = NULL;
2466 ModestWindowPrivate *parent_priv;
2468 parent_priv = MODEST_WINDOW_GET_PRIVATE(self);
2469 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(self);
2471 /* Set optimized view status */
2472 priv->optimized_view = !show_toolbar;
2474 if (!parent_priv->toolbar) {
2475 parent_priv->toolbar = gtk_ui_manager_get_widget (parent_priv->ui_manager,
2477 gtk_toolbar_set_icon_size (GTK_TOOLBAR (parent_priv->toolbar), HILDON_ICON_SIZE_FINGER);
2478 gtk_widget_set_no_show_all (parent_priv->toolbar, TRUE);
2480 priv->next_toolitem = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/ToolbarMessageNext");
2481 priv->prev_toolitem = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/ToolbarMessageBack");
2482 toolbar_resize (MODEST_MSG_VIEW_WINDOW (self));
2485 hildon_window_add_toolbar (HILDON_WINDOW (self),
2486 GTK_TOOLBAR (parent_priv->toolbar));
2491 /* Quick hack: this prevents toolbar icons "dance" when progress bar show status is changed */
2492 /* TODO: resize mode migth be GTK_RESIZE_QUEUE, in order to avoid unneccesary shows */
2493 gtk_container_set_resize_mode (GTK_CONTAINER(parent_priv->toolbar), GTK_RESIZE_IMMEDIATE);
2495 gtk_widget_show (GTK_WIDGET (parent_priv->toolbar));
2496 if (modest_msg_view_window_transfer_mode_enabled (MODEST_MSG_VIEW_WINDOW (self)))
2497 set_progress_hint (MODEST_MSG_VIEW_WINDOW (self), TRUE);
2499 set_progress_hint (MODEST_MSG_VIEW_WINDOW (self), FALSE);
2502 gtk_widget_set_no_show_all (parent_priv->toolbar, TRUE);
2503 gtk_widget_hide (GTK_WIDGET (parent_priv->toolbar));
2508 modest_msg_view_window_clipboard_owner_change (GtkClipboard *clipboard,
2510 ModestMsgViewWindow *window)
2512 if (!GTK_WIDGET_VISIBLE (window))
2515 modest_window_check_dimming_rules_group (MODEST_WINDOW (window), MODEST_DIMMING_RULES_CLIPBOARD);
2519 modest_msg_view_window_transfer_mode_enabled (ModestMsgViewWindow *self)
2521 ModestMsgViewWindowPrivate *priv;
2523 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self), FALSE);
2524 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(self);
2526 return priv->progress_hint;
2530 observers_empty (ModestMsgViewWindow *self)
2533 ModestMsgViewWindowPrivate *priv;
2534 gboolean is_empty = TRUE;
2535 guint pending_ops = 0;
2537 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(self);
2538 tmp = priv->progress_widgets;
2540 /* Check all observers */
2541 while (tmp && is_empty) {
2542 pending_ops = modest_progress_object_num_pending_operations (MODEST_PROGRESS_OBJECT(tmp->data));
2543 is_empty = pending_ops == 0;
2545 tmp = g_slist_next(tmp);
2552 on_account_removed (TnyAccountStore *account_store,
2553 TnyAccount *account,
2556 /* Do nothing if it's a transport account, because we only
2557 show the messages of a store account */
2558 if (tny_account_get_account_type(account) == TNY_ACCOUNT_TYPE_STORE) {
2559 const gchar *parent_acc = NULL;
2560 const gchar *our_acc = NULL;
2562 our_acc = modest_window_get_active_account (MODEST_WINDOW (user_data));
2563 parent_acc = modest_tny_account_get_parent_modest_account_name_for_server_account (account);
2565 /* Close this window if I'm showing a message of the removed account */
2566 if (our_acc && parent_acc && strcmp (parent_acc, our_acc) == 0)
2567 modest_ui_actions_on_close_window (NULL, MODEST_WINDOW (user_data));
2572 on_mail_operation_started (ModestMailOperation *mail_op,
2575 ModestMsgViewWindow *self;
2576 ModestMailOperationTypeOperation op_type;
2578 ModestMsgViewWindowPrivate *priv;
2579 GObject *source = NULL;
2581 self = MODEST_MSG_VIEW_WINDOW (user_data);
2582 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
2583 op_type = modest_mail_operation_get_type_operation (mail_op);
2584 tmp = priv->progress_widgets;
2585 source = modest_mail_operation_get_source(mail_op);
2586 if (G_OBJECT (self) == source) {
2587 if (op_type == MODEST_MAIL_OPERATION_TYPE_RECEIVE ||
2588 op_type == MODEST_MAIL_OPERATION_TYPE_OPEN ||
2589 op_type == MODEST_MAIL_OPERATION_TYPE_DELETE) {
2590 set_progress_hint (self, TRUE);
2592 modest_progress_object_add_operation (
2593 MODEST_PROGRESS_OBJECT (tmp->data),
2595 tmp = g_slist_next (tmp);
2599 g_object_unref (source);
2601 /* Update dimming rules */
2602 check_dimming_rules_after_change (self);
2606 on_mail_operation_finished (ModestMailOperation *mail_op,
2609 ModestMsgViewWindow *self;
2610 ModestMailOperationTypeOperation op_type;
2612 ModestMsgViewWindowPrivate *priv;
2614 self = MODEST_MSG_VIEW_WINDOW (user_data);
2615 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
2616 op_type = modest_mail_operation_get_type_operation (mail_op);
2617 tmp = priv->progress_widgets;
2619 if (op_type == MODEST_MAIL_OPERATION_TYPE_RECEIVE ||
2620 op_type == MODEST_MAIL_OPERATION_TYPE_OPEN ||
2621 op_type == MODEST_MAIL_OPERATION_TYPE_DELETE) {
2623 modest_progress_object_remove_operation (MODEST_PROGRESS_OBJECT (tmp->data),
2625 tmp = g_slist_next (tmp);
2628 /* If no more operations are being observed, NORMAL mode is enabled again */
2629 if (observers_empty (self)) {
2630 set_progress_hint (self, FALSE);
2634 /* Update dimming rules. We have to do this right here
2635 and not in view_msg_cb because at that point the
2636 transfer mode is still enabled so the dimming rule
2637 won't let the user delete the message that has been
2638 readed for example */
2639 check_dimming_rules_after_change (self);
2643 on_queue_changed (ModestMailOperationQueue *queue,
2644 ModestMailOperation *mail_op,
2645 ModestMailOperationQueueNotification type,
2646 ModestMsgViewWindow *self)
2648 ModestMsgViewWindowPrivate *priv;
2650 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
2652 /* If this operations was created by another window, do nothing */
2653 if (!modest_mail_operation_is_mine (mail_op, G_OBJECT(self)))
2656 if (type == MODEST_MAIL_OPERATION_QUEUE_OPERATION_ADDED) {
2657 priv->sighandlers = modest_signal_mgr_connect (priv->sighandlers,
2659 "operation-started",
2660 G_CALLBACK (on_mail_operation_started),
2662 priv->sighandlers = modest_signal_mgr_connect (priv->sighandlers,
2664 "operation-finished",
2665 G_CALLBACK (on_mail_operation_finished),
2667 } else if (type == MODEST_MAIL_OPERATION_QUEUE_OPERATION_REMOVED) {
2668 priv->sighandlers = modest_signal_mgr_disconnect (priv->sighandlers,
2670 "operation-started");
2671 priv->sighandlers = modest_signal_mgr_disconnect (priv->sighandlers,
2673 "operation-finished");
2678 modest_msg_view_window_get_attachments (ModestMsgViewWindow *win)
2680 ModestMsgViewWindowPrivate *priv;
2681 TnyList *selected_attachments = NULL;
2683 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (win), NULL);
2684 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (win);
2686 /* In Hildon 2.2 as there's no selection we assume we have all attachments selected */
2687 selected_attachments = modest_msg_view_get_attachments (MODEST_MSG_VIEW (priv->msg_view));
2689 return selected_attachments;
2693 ModestMsgViewWindow *self;
2695 gchar *attachment_uid;
2696 } DecodeAsyncHelper;
2699 on_decode_to_stream_async_handler (TnyMimePart *mime_part,
2705 DecodeAsyncHelper *helper = (DecodeAsyncHelper *) user_data;
2706 const gchar *content_type;
2707 ModestMsgViewWindowPrivate *priv;
2709 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (helper->self);
2711 if (cancelled || err) {
2714 if ((err->domain == TNY_ERROR_DOMAIN) &&
2715 (err->code == TNY_IO_ERROR_WRITE) &&
2716 (errno == ENOSPC)) {
2717 msg = g_strdup_printf (_KR("cerm_device_memory_full"), "");
2719 msg = g_strdup (_("mail_ib_file_operation_failed"));
2721 modest_platform_information_banner (NULL, NULL, msg);
2727 /* It could happen that the window was closed. So we
2728 assume it is a cancelation */
2729 if (!GTK_WIDGET_VISIBLE (helper->self))
2732 /* Remove the progress hint */
2733 set_progress_hint (helper->self, FALSE);
2735 content_type = tny_mime_part_get_content_type (mime_part);
2736 if (content_type && g_str_has_prefix (content_type, "message/rfc822")) {
2737 ModestWindowMgr *mgr;
2738 ModestWindow *msg_win = NULL;
2741 const gchar *mailbox;
2742 TnyStream *file_stream;
2745 fd = g_open (helper->file_path, O_RDONLY, 0644);
2748 file_stream = tny_fs_stream_new (fd);
2750 mgr = modest_runtime_get_window_mgr ();
2752 account = g_strdup (modest_window_get_active_account (MODEST_WINDOW (helper->self)));
2753 mailbox = modest_window_get_active_mailbox (MODEST_WINDOW (helper->self));
2756 account = modest_account_mgr_get_default_account (modest_runtime_get_account_mgr ());
2758 msg = tny_camel_msg_new ();
2759 tny_camel_msg_parse (msg, file_stream);
2762 top_msg = g_object_ref (priv->top_msg);
2764 top_msg = tny_msg_view_get_msg (TNY_MSG_VIEW (priv->msg_view));
2766 msg_win = modest_msg_view_window_new_for_attachment (TNY_MSG (msg), top_msg,
2767 account, mailbox, helper->attachment_uid);
2768 if (top_msg) g_object_unref (top_msg);
2769 modest_window_set_zoom (MODEST_WINDOW (msg_win),
2770 modest_window_get_zoom (MODEST_WINDOW (helper->self)));
2771 if (modest_window_mgr_register_window (mgr, msg_win, MODEST_WINDOW (helper->self)))
2772 gtk_widget_show_all (GTK_WIDGET (msg_win));
2774 gtk_widget_destroy (GTK_WIDGET (msg_win));
2775 g_object_unref (msg);
2776 g_object_unref (file_stream);
2778 modest_platform_information_banner (NULL, NULL, _("mail_ib_file_operation_failed"));
2783 /* make the file read-only */
2784 g_chmod(helper->file_path, 0444);
2786 /* Activate the file */
2787 modest_platform_activate_file (helper->file_path, tny_mime_part_get_content_type (mime_part));
2792 g_object_unref (helper->self);
2793 g_free (helper->file_path);
2794 g_free (helper->attachment_uid);
2795 g_slice_free (DecodeAsyncHelper, helper);
2799 view_attachment_connect_handler (gboolean canceled,
2801 GtkWindow *parent_window,
2802 TnyAccount *account,
2806 if (canceled || err) {
2807 g_object_unref (part);
2811 modest_msg_view_window_view_attachment (MODEST_MSG_VIEW_WINDOW (parent_window),
2813 g_object_unref (part);
2817 modest_msg_view_window_view_attachment (ModestMsgViewWindow *window,
2818 TnyMimePart *mime_part)
2820 ModestMsgViewWindowPrivate *priv;
2821 const gchar *msg_uid;
2822 gchar *attachment_uid = NULL;
2823 gint attachment_index = 0;
2824 TnyList *attachments;
2826 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window));
2827 g_return_if_fail (TNY_IS_MIME_PART (mime_part) || (mime_part == NULL));
2828 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
2830 msg_uid = modest_msg_view_window_get_message_uid (MODEST_MSG_VIEW_WINDOW (window));
2831 attachments = modest_msg_view_get_attachments (MODEST_MSG_VIEW (priv->msg_view));
2832 attachment_index = modest_list_index (attachments, (GObject *) mime_part);
2833 g_object_unref (attachments);
2835 if (msg_uid && attachment_index >= 0) {
2836 attachment_uid = g_strdup_printf ("%s/%d", msg_uid, attachment_index);
2839 if (mime_part == NULL) {
2840 gboolean error = FALSE;
2841 TnyList *selected_attachments = modest_msg_view_get_selected_attachments (MODEST_MSG_VIEW (priv->msg_view));
2842 if (selected_attachments == NULL || tny_list_get_length (selected_attachments) == 0) {
2844 } else if (tny_list_get_length (selected_attachments) > 1) {
2845 hildon_banner_show_information (NULL, NULL, _("mcen_ib_unable_to_display_more"));
2849 iter = tny_list_create_iterator (selected_attachments);
2850 mime_part = (TnyMimePart *) tny_iterator_get_current (iter);
2851 g_object_unref (iter);
2853 if (selected_attachments)
2854 g_object_unref (selected_attachments);
2859 g_object_ref (mime_part);
2862 if (tny_mime_part_is_purged (mime_part))
2865 if (TNY_IS_CAMEL_BS_MIME_PART (mime_part) &&
2866 !tny_camel_bs_mime_part_is_fetched (TNY_CAMEL_BS_MIME_PART (mime_part))) {
2868 TnyAccount *account;
2870 is_merge = g_str_has_prefix (priv->msg_uid, "merge:");
2872 /* Get the account */
2874 account = tny_account_store_find_account (TNY_ACCOUNT_STORE (modest_runtime_get_account_store ()),
2877 if (!tny_device_is_online (modest_runtime_get_device())) {
2878 modest_platform_connect_and_perform (GTK_WINDOW (window),
2880 TNY_ACCOUNT (account),
2881 (ModestConnectedPerformer) view_attachment_connect_handler,
2882 g_object_ref (mime_part));
2887 if (!modest_tny_mime_part_is_msg (mime_part) && tny_mime_part_get_filename (mime_part)) {
2888 gchar *filepath = NULL;
2889 const gchar *att_filename = tny_mime_part_get_filename (mime_part);
2890 gboolean show_error_banner = FALSE;
2891 TnyFsStream *temp_stream = NULL;
2892 temp_stream = modest_utils_create_temp_stream (att_filename, attachment_uid,
2895 if (temp_stream != NULL) {
2896 ModestAccountMgr *mgr;
2897 DecodeAsyncHelper *helper;
2898 gboolean decode_in_provider;
2899 ModestProtocol *protocol;
2900 const gchar *account;
2902 /* Activate progress hint */
2903 set_progress_hint (window, TRUE);
2905 helper = g_slice_new0 (DecodeAsyncHelper);
2906 helper->self = g_object_ref (window);
2907 helper->file_path = g_strdup (filepath);
2908 helper->attachment_uid = g_strdup (attachment_uid);
2910 decode_in_provider = FALSE;
2911 mgr = modest_runtime_get_account_mgr ();
2912 account = modest_window_get_active_account (MODEST_WINDOW (window));
2913 if (modest_account_mgr_account_is_multimailbox (mgr, account, &protocol)) {
2914 if (MODEST_IS_ACCOUNT_PROTOCOL (protocol)) {
2916 uri = g_strconcat ("file://", filepath, NULL);
2917 decode_in_provider =
2918 modest_account_protocol_decode_part_to_stream_async (
2919 MODEST_ACCOUNT_PROTOCOL (protocol),
2922 TNY_STREAM (temp_stream),
2923 on_decode_to_stream_async_handler,
2930 if (!decode_in_provider)
2931 tny_mime_part_decode_to_stream_async (mime_part, TNY_STREAM (temp_stream),
2932 on_decode_to_stream_async_handler,
2935 g_object_unref (temp_stream);
2936 /* NOTE: files in the temporary area will be automatically
2937 * cleaned after some time if they are no longer in use */
2940 const gchar *content_type;
2941 /* the file may already exist but it isn't writable,
2942 * let's try to open it anyway */
2943 content_type = tny_mime_part_get_content_type (mime_part);
2944 modest_platform_activate_file (filepath, content_type);
2946 g_warning ("%s: modest_utils_create_temp_stream failed", __FUNCTION__);
2947 show_error_banner = TRUE;
2952 if (show_error_banner)
2953 modest_platform_information_banner (NULL, NULL, _("mail_ib_file_operation_failed"));
2954 } else if (!modest_tny_mime_part_is_msg (mime_part)) {
2955 ModestWindowMgr *mgr;
2956 ModestWindow *msg_win = NULL;
2957 TnyMsg *current_msg;
2961 current_msg = modest_msg_view_window_get_message (MODEST_MSG_VIEW_WINDOW (window));
2962 mgr = modest_runtime_get_window_mgr ();
2963 header = tny_msg_get_header (TNY_MSG (current_msg));
2964 found = modest_window_mgr_find_registered_message_uid (mgr,
2969 g_debug ("window for this body is already being created");
2973 /* it's not found, so create a new window for it */
2974 modest_window_mgr_register_header (mgr, header, attachment_uid); /* register the uid before building the window */
2975 gchar *account = g_strdup (modest_window_get_active_account (MODEST_WINDOW (window)));
2976 const gchar *mailbox = modest_window_get_active_mailbox (MODEST_WINDOW (window));
2978 account = modest_account_mgr_get_default_account (modest_runtime_get_account_mgr ());
2981 top_msg = g_object_ref (priv->top_msg);
2983 top_msg = tny_msg_view_get_msg (TNY_MSG_VIEW (priv->msg_view));
2985 msg_win = modest_msg_view_window_new_with_other_body (TNY_MSG (current_msg), TNY_MIME_PART (mime_part), top_msg,
2986 account, mailbox, attachment_uid);
2988 if (top_msg) g_object_unref (top_msg);
2990 modest_window_set_zoom (MODEST_WINDOW (msg_win),
2991 modest_window_get_zoom (MODEST_WINDOW (window)));
2992 if (modest_window_mgr_register_window (mgr, msg_win, MODEST_WINDOW (window)))
2993 gtk_widget_show_all (GTK_WIDGET (msg_win));
2995 gtk_widget_destroy (GTK_WIDGET (msg_win));
2997 g_object_unref (current_msg);
2999 /* message attachment */
3000 TnyHeader *header = NULL;
3001 ModestWindowMgr *mgr;
3002 ModestWindow *msg_win = NULL;
3005 header = tny_msg_get_header (TNY_MSG (mime_part));
3006 mgr = modest_runtime_get_window_mgr ();
3007 found = modest_window_mgr_find_registered_header (mgr, header, &msg_win);
3010 /* if it's found, but there is no msg_win, it's probably in the process of being created;
3011 * thus, we don't do anything */
3012 g_debug ("window for is already being created");
3015 /* it's not found, so create a new window for it */
3016 modest_window_mgr_register_header (mgr, header, attachment_uid); /* register the uid before building the window */
3017 gchar *account = g_strdup (modest_window_get_active_account (MODEST_WINDOW (window)));
3018 const gchar *mailbox = modest_window_get_active_mailbox (MODEST_WINDOW (window));
3020 account = modest_account_mgr_get_default_account (modest_runtime_get_account_mgr ());
3022 top_msg = g_object_ref (priv->top_msg);
3024 top_msg = tny_msg_view_get_msg (TNY_MSG_VIEW (priv->msg_view));
3025 msg_win = modest_msg_view_window_new_for_attachment (
3026 TNY_MSG (mime_part), top_msg, account,
3027 mailbox, attachment_uid);
3028 modest_window_set_zoom (MODEST_WINDOW (msg_win),
3029 modest_window_get_zoom (MODEST_WINDOW (window)));
3030 if (modest_window_mgr_register_window (mgr, msg_win, MODEST_WINDOW (window)))
3031 gtk_widget_show_all (GTK_WIDGET (msg_win));
3033 gtk_widget_destroy (GTK_WIDGET (msg_win));
3039 g_free (attachment_uid);
3041 g_object_unref (mime_part);
3053 GnomeVFSResult result;
3055 ModestMsgViewWindow *window;
3058 static void save_mime_part_info_free (SaveMimePartInfo *info, gboolean with_struct);
3059 static gboolean idle_save_mime_part_show_result (SaveMimePartInfo *info);
3060 static gpointer save_mime_part_to_file (SaveMimePartInfo *info);
3061 static void save_mime_parts_to_file_with_checks (GtkWindow *parent, SaveMimePartInfo *info);
3064 save_mime_part_info_free (SaveMimePartInfo *info, gboolean with_struct)
3068 for (node = info->pairs; node != NULL; node = g_list_next (node)) {
3069 SaveMimePartPair *pair = (SaveMimePartPair *) node->data;
3070 g_free (pair->filename);
3071 g_object_unref (pair->part);
3072 g_slice_free (SaveMimePartPair, pair);
3074 g_list_free (info->pairs);
3077 g_object_unref (info->window);
3078 info->window = NULL;
3080 g_slice_free (SaveMimePartInfo, info);
3085 idle_save_mime_part_show_result (SaveMimePartInfo *info)
3087 /* This is a GDK lock because we are an idle callback and
3088 * hildon_banner_show_information is or does Gtk+ code */
3090 gdk_threads_enter (); /* CHECKED */
3091 if (info->result == GNOME_VFS_ERROR_CANCELLED) {
3093 } else if (info->result == GNOME_VFS_OK) {
3094 hildon_banner_show_information (NULL, NULL, _CS("sfil_ib_saved"));
3095 } else if (info->result == GNOME_VFS_ERROR_NO_SPACE) {
3098 /* Check if the uri belongs to the external mmc */
3099 if (g_str_has_prefix (info->uri, g_getenv (MODEST_MMC1_VOLUMEPATH_ENV)))
3100 msg = g_strdup_printf (_KR("cerm_device_memory_full"), "");
3102 msg = g_strdup (_KR("cerm_memory_card_full"));
3103 modest_platform_information_banner (NULL, NULL, msg);
3106 hildon_banner_show_information (NULL, NULL, _("mail_ib_file_operation_failed"));
3108 save_mime_part_info_free (info, FALSE);
3109 gdk_threads_leave (); /* CHECKED */
3115 save_mime_part_to_file_connect_handler (gboolean canceled,
3117 GtkWindow *parent_window,
3118 TnyAccount *account,
3119 SaveMimePartInfo *info)
3121 if (canceled || err) {
3122 if (canceled && !err) {
3123 info->result = GNOME_VFS_ERROR_CANCELLED;
3125 g_idle_add ((GSourceFunc) idle_save_mime_part_show_result, info);
3127 g_thread_create ((GThreadFunc)save_mime_part_to_file, info, FALSE, NULL);
3132 save_mime_part_to_file_connect_idle (SaveMimePartInfo *info)
3135 TnyAccount *account;
3136 ModestMsgViewWindowPrivate *priv;
3138 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (info->window);
3140 is_merge = g_str_has_prefix (priv->msg_uid, "merge:");
3143 /* Get the account */
3145 account = tny_account_store_find_account (TNY_ACCOUNT_STORE (modest_runtime_get_account_store ()),
3148 modest_platform_connect_and_perform (GTK_WINDOW (info->window),
3150 TNY_ACCOUNT (account),
3151 (ModestConnectedPerformer) save_mime_part_to_file_connect_handler,
3157 save_mime_part_to_file (SaveMimePartInfo *info)
3159 GnomeVFSHandle *handle;
3161 SaveMimePartPair *pair = (SaveMimePartPair *) info->pairs->data;
3163 if (TNY_IS_CAMEL_BS_MIME_PART (pair->part) &&
3164 !tny_camel_bs_mime_part_is_fetched (TNY_CAMEL_BS_MIME_PART (pair->part))) {
3165 g_idle_add ((GSourceFunc) save_mime_part_to_file_connect_idle, info);
3169 info->result = gnome_vfs_create (&handle, pair->filename, GNOME_VFS_OPEN_WRITE, FALSE, 0644);
3170 if (info->result == GNOME_VFS_OK) {
3171 GError *error = NULL;
3172 gboolean decode_in_provider;
3174 ModestAccountMgr *mgr;
3175 const gchar *account;
3176 ModestProtocol *protocol = NULL;
3178 stream = tny_vfs_stream_new (handle);
3180 decode_in_provider = FALSE;
3181 mgr = modest_runtime_get_account_mgr ();
3182 account = modest_window_get_active_account (MODEST_WINDOW (info->window));
3183 if (modest_account_mgr_account_is_multimailbox (mgr, account, &protocol)) {
3184 if (MODEST_IS_ACCOUNT_PROTOCOL (protocol)) {
3185 decode_in_provider =
3186 modest_account_protocol_decode_part_to_stream (
3187 MODEST_ACCOUNT_PROTOCOL (protocol),
3195 if (!decode_in_provider)
3196 written = tny_mime_part_decode_to_stream (pair->part, stream, &error);
3199 g_warning ("modest: could not save attachment %s: %d (%s)\n", pair->filename, error?error->code:-1, error?error->message:"Unknown error");
3201 if ((error->domain == TNY_ERROR_DOMAIN) &&
3202 (error->code == TNY_IO_ERROR_WRITE) &&
3203 (errno == ENOSPC)) {
3204 info->result = GNOME_VFS_ERROR_NO_SPACE;
3206 info->result = GNOME_VFS_ERROR_IO;
3209 g_object_unref (G_OBJECT (stream));
3211 g_warning ("Could not create save attachment %s: %s\n",
3212 pair->filename, gnome_vfs_result_to_string (info->result));
3215 /* Go on saving remaining files */
3216 info->pairs = g_list_remove_link (info->pairs, info->pairs);
3217 if (info->pairs != NULL) {
3218 save_mime_part_to_file (info);
3220 g_idle_add ((GSourceFunc) idle_save_mime_part_show_result, info);
3227 save_mime_parts_to_file_with_checks (GtkWindow *parent,
3228 SaveMimePartInfo *info)
3230 gboolean is_ok = TRUE;
3231 gint replaced_files = 0;
3232 const GList *files = info->pairs;
3233 const GList *iter, *to_replace = NULL;
3235 for (iter = files; (iter != NULL) && (replaced_files < 2); iter = g_list_next(iter)) {
3236 SaveMimePartPair *pair = iter->data;
3237 gchar *unescaped = g_uri_unescape_string (pair->filename, NULL);
3239 if (modest_utils_file_exists (unescaped)) {
3241 if (replaced_files == 1)
3246 if (replaced_files) {
3249 if (replaced_files == 1) {
3250 SaveMimePartPair *pair = to_replace->data;
3251 const gchar *basename = strrchr (pair->filename, G_DIR_SEPARATOR) + 1;
3252 gchar *escaped_basename, *message;
3254 escaped_basename = g_uri_unescape_string (basename, NULL);
3255 message = g_strdup_printf ("%s\n%s",
3256 _FM("docm_nc_replace_file"),
3257 (escaped_basename) ? escaped_basename : "");
3258 response = modest_platform_run_confirmation_dialog (parent, message);
3260 g_free (escaped_basename);
3262 response = modest_platform_run_confirmation_dialog (parent,
3263 _FM("docm_nc_replace_multiple"));
3265 if (response != GTK_RESPONSE_OK)
3270 save_mime_part_info_free (info, TRUE);
3272 g_thread_create ((GThreadFunc)save_mime_part_to_file, info, FALSE, NULL);
3277 typedef struct _SaveAttachmentsInfo {
3278 TnyList *attachments_list;
3279 ModestMsgViewWindow *window;
3280 } SaveAttachmentsInfo;
3283 save_attachments_response (GtkDialog *dialog,
3287 TnyList *mime_parts;
3289 GList *files_to_save = NULL;
3290 gchar *current_folder;
3291 SaveAttachmentsInfo *sa_info = (SaveAttachmentsInfo *) user_data;
3293 mime_parts = TNY_LIST (sa_info->attachments_list);
3295 if (arg1 != GTK_RESPONSE_OK)
3298 chooser_uri = gtk_file_chooser_get_uri (GTK_FILE_CHOOSER (dialog));
3299 current_folder = gtk_file_chooser_get_current_folder_uri (GTK_FILE_CHOOSER (dialog));
3300 if (current_folder && *current_folder != '\0') {
3302 modest_conf_set_string (modest_runtime_get_conf (), MODEST_CONF_LATEST_SAVE_ATTACHMENT_PATH,
3303 current_folder,&err);
3305 g_debug ("Error storing latest used folder: %s", err->message);
3309 g_free (current_folder);
3311 if (!modest_utils_folder_writable (chooser_uri)) {
3312 const gchar *err_msg;
3314 #ifdef MODEST_PLATFORM_MAEMO
3315 if (modest_maemo_utils_in_usb_mode ()) {
3316 err_msg = dgettext ("hildon-status-bar-usb", "usbh_ib_mmc_usb_connected");
3318 err_msg = _FM("sfil_ib_readonly_location");
3321 err_msg = _FM("sfil_ib_readonly_location");
3323 hildon_banner_show_information (NULL, NULL, err_msg);
3327 iter = tny_list_create_iterator (mime_parts);
3328 while (!tny_iterator_is_done (iter)) {
3329 TnyMimePart *mime_part = (TnyMimePart *) tny_iterator_get_current (iter);
3331 if ((modest_tny_mime_part_is_attachment_for_modest (mime_part)) &&
3332 !tny_mime_part_is_purged (mime_part) &&
3333 (tny_mime_part_get_filename (mime_part) != NULL)) {
3334 SaveMimePartPair *pair;
3336 pair = g_slice_new0 (SaveMimePartPair);
3338 if (tny_list_get_length (mime_parts) > 1) {
3340 gnome_vfs_escape_slashes (tny_mime_part_get_filename (mime_part));
3341 pair->filename = g_build_filename (chooser_uri, escaped, NULL);
3344 pair->filename = g_strdup (chooser_uri);
3346 pair->part = mime_part;
3347 files_to_save = g_list_prepend (files_to_save, pair);
3349 tny_iterator_next (iter);
3351 g_object_unref (iter);
3354 if (files_to_save != NULL) {
3355 SaveMimePartInfo *info = g_slice_new0 (SaveMimePartInfo);
3356 info->pairs = files_to_save;
3357 info->result = TRUE;
3358 info->uri = g_strdup (chooser_uri);
3359 info->window = g_object_ref (sa_info->window);
3360 save_mime_parts_to_file_with_checks ((GtkWindow *) dialog, info);
3362 g_free (chooser_uri);
3365 /* Free and close the dialog */
3366 g_object_unref (mime_parts);
3367 g_object_unref (sa_info->window);
3368 g_slice_free (SaveAttachmentsInfo, sa_info);
3369 gtk_widget_destroy (GTK_WIDGET (dialog));
3373 modest_msg_view_window_save_attachments (ModestMsgViewWindow *window,
3374 TnyList *mime_parts)
3376 ModestMsgViewWindowPrivate *priv;
3377 GtkWidget *save_dialog = NULL;
3378 gchar *conf_folder = NULL;
3379 gchar *filename = NULL;
3380 gchar *save_multiple_str = NULL;
3381 const gchar *root_folder = "file:///";
3383 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window));
3384 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
3386 if (mime_parts == NULL) {
3387 /* In Hildon 2.2 save and delete operate over all the attachments as there's no
3388 * selection available */
3389 mime_parts = modest_msg_view_get_attachments (MODEST_MSG_VIEW (priv->msg_view));
3390 if (mime_parts && !modest_maemo_utils_select_attachments (GTK_WINDOW (window), mime_parts, FALSE)) {
3391 g_object_unref (mime_parts);
3394 if (mime_parts == NULL || tny_list_get_length (mime_parts) == 0) {
3396 g_object_unref (mime_parts);
3402 g_object_ref (mime_parts);
3405 /* prepare dialog */
3406 if (tny_list_get_length (mime_parts) == 1) {
3408 /* only one attachment selected */
3409 iter = tny_list_create_iterator (mime_parts);
3410 TnyMimePart *mime_part = (TnyMimePart *) tny_iterator_get_current (iter);
3411 g_object_unref (iter);
3412 if (!modest_tny_mime_part_is_msg (mime_part) &&
3413 modest_tny_mime_part_is_attachment_for_modest (mime_part) &&
3414 !tny_mime_part_is_purged (mime_part)) {
3415 filename = g_strdup (tny_mime_part_get_filename (mime_part));
3417 /* TODO: show any error? */
3418 g_warning ("%s: Tried to save a non-file attachment", __FUNCTION__);
3419 g_object_unref (mime_parts);
3422 g_object_unref (mime_part);
3424 gint num = tny_list_get_length (mime_parts);
3425 save_multiple_str = g_strdup_printf (dngettext("hildon-fm",
3426 "sfil_va_number_of_objects_attachment",
3427 "sfil_va_number_of_objects_attachments",
3431 save_dialog = hildon_file_chooser_dialog_new (GTK_WINDOW (window),
3432 GTK_FILE_CHOOSER_ACTION_SAVE);
3434 /* Get last used folder */
3435 conf_folder = modest_conf_get_string (modest_runtime_get_conf (),
3436 MODEST_CONF_LATEST_SAVE_ATTACHMENT_PATH, NULL);
3438 /* File chooser stops working if we select "file:///" as current folder */
3439 if (conf_folder && g_ascii_strcasecmp (root_folder, conf_folder) != 0) {
3440 g_free (conf_folder);
3444 if (conf_folder && conf_folder[0] != '\0') {
3445 gtk_file_chooser_set_current_folder_uri (GTK_FILE_CHOOSER (save_dialog), conf_folder);
3448 /* Set the default folder to documents folder */
3449 docs_folder = (gchar *) g_strdup(g_get_user_special_dir (G_USER_DIRECTORY_DOCUMENTS));
3452 docs_folder = g_build_filename (g_getenv (MYDOCS_ENV), DOCS_FOLDER, NULL);
3454 gtk_file_chooser_set_current_folder (GTK_FILE_CHOOSER (save_dialog), docs_folder);
3455 g_free (docs_folder);
3457 g_free (conf_folder);
3461 gtk_file_chooser_set_current_name (GTK_FILE_CHOOSER (save_dialog),
3466 /* if multiple, set multiple string */
3467 if (save_multiple_str) {
3468 g_object_set (G_OBJECT (save_dialog), "save-multiple", save_multiple_str, NULL);
3469 gtk_window_set_title (GTK_WINDOW (save_dialog), _FM("sfil_ti_save_objects_files"));
3470 g_free (save_multiple_str);
3473 /* We must run this asynchronously, because the hildon dialog
3474 performs a gtk_dialog_run by itself which leads to gdk
3476 SaveAttachmentsInfo *sa_info;
3477 sa_info = g_slice_new (SaveAttachmentsInfo);
3478 sa_info->attachments_list = mime_parts;
3479 sa_info->window = g_object_ref (window);
3480 g_signal_connect (save_dialog, "response",
3481 G_CALLBACK (save_attachments_response), sa_info);
3483 gtk_widget_show_all (save_dialog);
3487 show_remove_attachment_information (gpointer userdata)
3489 ModestMsgViewWindow *window = (ModestMsgViewWindow *) userdata;
3490 ModestMsgViewWindowPrivate *priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
3492 /* We're outside the main lock */
3493 gdk_threads_enter ();
3495 if (priv->remove_attachment_banner != NULL) {
3496 gtk_widget_destroy (priv->remove_attachment_banner);
3497 g_object_unref (priv->remove_attachment_banner);
3500 priv->remove_attachment_banner = g_object_ref (
3501 hildon_banner_show_animation (NULL, NULL, _("mcen_me_inbox_remove_attachments")));
3503 gdk_threads_leave ();
3509 modest_msg_view_window_remove_attachments (ModestMsgViewWindow *window, gboolean get_all)
3511 ModestMsgViewWindowPrivate *priv;
3512 TnyList *mime_parts = NULL, *tmp;
3513 gchar *confirmation_message;
3519 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window));
3520 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
3522 /* In hildon 2.2 we ignore the get_all flag as we always get all attachments. This is
3523 * because we don't have selection
3525 mime_parts = modest_msg_view_get_attachments (MODEST_MSG_VIEW (priv->msg_view));
3527 /* Remove already purged messages from mime parts list. We use
3528 a copy of the list to remove items in the original one */
3529 tmp = tny_list_copy (mime_parts);
3530 iter = tny_list_create_iterator (tmp);
3531 while (!tny_iterator_is_done (iter)) {
3532 TnyMimePart *part = TNY_MIME_PART (tny_iterator_get_current (iter));
3533 if (tny_mime_part_is_purged (part))
3534 tny_list_remove (mime_parts, (GObject *) part);
3536 g_object_unref (part);
3537 tny_iterator_next (iter);
3539 g_object_unref (tmp);
3540 g_object_unref (iter);
3542 if (!modest_maemo_utils_select_attachments (GTK_WINDOW (window), mime_parts, TRUE) ||
3543 tny_list_get_length (mime_parts) == 0) {
3544 g_object_unref (mime_parts);
3548 n_attachments = tny_list_get_length (mime_parts);
3549 if (n_attachments == 1) {
3553 iter = tny_list_create_iterator (mime_parts);
3554 part = (TnyMimePart *) tny_iterator_get_current (iter);
3555 g_object_unref (iter);
3556 if (modest_tny_mime_part_is_msg (part)) {
3558 header = tny_msg_get_header (TNY_MSG (part));
3559 filename = tny_header_dup_subject (header);
3560 g_object_unref (header);
3561 if (filename == NULL)
3562 filename = g_strdup (_("mail_va_no_subject"));
3564 filename = g_strdup (tny_mime_part_get_filename (TNY_MIME_PART (part)));
3566 confirmation_message = g_strdup_printf (_("mcen_nc_purge_file_text"), filename);
3568 g_object_unref (part);
3570 confirmation_message = g_strdup_printf (ngettext("mcen_nc_purge_file_text",
3571 "mcen_nc_purge_files_text",
3572 n_attachments), n_attachments);
3574 response = modest_platform_run_confirmation_dialog (GTK_WINDOW (window),
3575 confirmation_message);
3576 g_free (confirmation_message);
3578 if (response != GTK_RESPONSE_OK) {
3579 g_object_unref (mime_parts);
3583 priv->purge_timeout = g_timeout_add (2000, show_remove_attachment_information, window);
3585 iter = tny_list_create_iterator (mime_parts);
3586 while (!tny_iterator_is_done (iter)) {
3589 part = (TnyMimePart *) tny_iterator_get_current (iter);
3590 tny_mime_part_set_purged (TNY_MIME_PART (part));
3591 g_object_unref (part);
3592 tny_iterator_next (iter);
3594 g_object_unref (iter);
3596 msg = tny_msg_view_get_msg (TNY_MSG_VIEW (priv->msg_view));
3597 tny_msg_view_clear (TNY_MSG_VIEW (priv->msg_view));
3598 tny_msg_rewrite_cache (msg);
3599 tny_msg_view_set_msg (TNY_MSG_VIEW (priv->msg_view), msg);
3600 g_object_unref (msg);
3601 update_branding (MODEST_MSG_VIEW_WINDOW (window));
3603 g_object_unref (mime_parts);
3605 if (priv->purge_timeout > 0) {
3606 g_source_remove (priv->purge_timeout);
3607 priv->purge_timeout = 0;
3610 if (priv->remove_attachment_banner) {
3611 gtk_widget_destroy (priv->remove_attachment_banner);
3612 g_object_unref (priv->remove_attachment_banner);
3613 priv->remove_attachment_banner = NULL;
3619 update_window_title (ModestMsgViewWindow *window)
3621 ModestMsgViewWindowPrivate *priv;
3623 TnyHeader *header = NULL;
3624 gchar *subject = NULL;
3626 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
3628 /* Note that if the window is closed while we're retrieving
3629 the message, this widget could de deleted */
3630 if (!priv->msg_view)
3633 msg = tny_msg_view_get_msg (TNY_MSG_VIEW (priv->msg_view));
3635 if (priv->other_body) {
3638 description = modest_tny_mime_part_get_header_value (priv->other_body, "Content-Description");
3640 g_strstrip (description);
3641 subject = description;
3643 } else if (msg != NULL) {
3644 header = tny_msg_get_header (msg);
3645 subject = tny_header_dup_subject (header);
3646 g_object_unref (header);
3647 g_object_unref (msg);
3650 if ((subject == NULL)||(subject[0] == '\0')) {
3652 subject = g_strdup (_("mail_va_no_subject"));
3655 gtk_window_set_title (GTK_WINDOW (window), subject);
3660 on_move_focus (GtkWidget *widget,
3661 GtkDirectionType direction,
3664 g_signal_stop_emission_by_name (G_OBJECT (widget), "move-focus");
3668 fetch_image_open_stream (TnyStreamCache *self, gint64 *expected_size, gchar *uri)
3670 GnomeVFSResult result;
3671 GnomeVFSHandle *handle = NULL;
3672 GnomeVFSFileInfo *info = NULL;
3675 result = gnome_vfs_open (&handle, uri, GNOME_VFS_OPEN_READ);
3676 if (result != GNOME_VFS_OK) {
3681 info = gnome_vfs_file_info_new ();
3682 result = gnome_vfs_get_file_info_from_handle (handle, info, GNOME_VFS_FILE_INFO_DEFAULT);
3683 if (result != GNOME_VFS_OK || ! (info->valid_fields & GNOME_VFS_FILE_INFO_FIELDS_SIZE)) {
3684 /* We put a "safe" default size for going to cache */
3685 *expected_size = (300*1024);
3687 *expected_size = info->size;
3689 gnome_vfs_file_info_unref (info);
3691 stream = tny_vfs_stream_new (handle);
3700 TnyStream *output_stream;
3701 GtkWidget *msg_view;
3706 on_fetch_image_timeout_refresh_view (gpointer userdata)
3708 ModestMsgViewWindowPrivate *priv;
3710 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (userdata);
3711 update_progress_hint (MODEST_MSG_VIEW_WINDOW (userdata));
3712 /* Note that priv->msg_view is set to NULL when this window is
3714 if (priv->msg_view && GTK_WIDGET_DRAWABLE (priv->msg_view)) {
3715 gtk_widget_queue_draw (GTK_WIDGET (priv->msg_view));
3717 priv->fetch_image_redraw_handler = 0;
3718 g_object_unref (userdata);
3723 on_fetch_image_idle_refresh_view (gpointer userdata)
3726 FetchImageData *fidata = (FetchImageData *) userdata;
3728 gdk_threads_enter ();
3729 if (GTK_WIDGET_DRAWABLE (fidata->msg_view)) {
3730 ModestMsgViewWindowPrivate *priv;
3732 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (fidata->window);
3733 priv->fetching_images--;
3734 if (priv->fetch_image_redraw_handler == 0) {
3735 priv->fetch_image_redraw_handler = g_timeout_add (500, on_fetch_image_timeout_refresh_view, g_object_ref (fidata->window));
3739 gdk_threads_leave ();
3741 g_object_unref (fidata->msg_view);
3742 g_object_unref (fidata->window);
3743 g_slice_free (FetchImageData, fidata);
3748 on_fetch_image_thread (gpointer userdata)
3750 FetchImageData *fidata = (FetchImageData *) userdata;
3751 TnyStreamCache *cache;
3752 TnyStream *cache_stream;
3754 cache = modest_runtime_get_images_cache ();
3756 tny_stream_cache_get_stream (cache,
3758 (TnyStreamCacheOpenStreamFetcher) fetch_image_open_stream,
3759 (gpointer) fidata->uri);
3760 g_free (fidata->cache_id);
3761 g_free (fidata->uri);
3763 if (cache_stream != NULL) {
3766 while (G_LIKELY (!tny_stream_is_eos (cache_stream))) {
3769 nb_read = tny_stream_read (cache_stream, buffer, sizeof (buffer));
3770 if (G_UNLIKELY (nb_read < 0)) {
3772 } else if (G_LIKELY (nb_read > 0)) {
3773 gssize nb_written = 0;
3775 while (G_UNLIKELY (nb_written < nb_read)) {
3778 len = tny_stream_write (fidata->output_stream, buffer + nb_written,
3779 nb_read - nb_written);
3780 if (G_UNLIKELY (len < 0))
3786 tny_stream_close (cache_stream);
3787 g_object_unref (cache_stream);
3790 tny_stream_close (fidata->output_stream);
3791 g_object_unref (fidata->output_stream);
3793 g_idle_add (on_fetch_image_idle_refresh_view, fidata);
3799 on_fetch_image (ModestMsgView *msgview,
3802 ModestMsgViewWindow *window)
3804 const gchar *current_account;
3805 ModestMsgViewWindowPrivate *priv;
3806 FetchImageData *fidata;
3808 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
3810 current_account = modest_window_get_active_account (MODEST_WINDOW (window));
3812 fidata = g_slice_new0 (FetchImageData);
3813 fidata->msg_view = g_object_ref (msgview);
3814 fidata->window = g_object_ref (window);
3815 fidata->uri = g_strdup (uri);
3816 fidata->cache_id = modest_images_cache_get_id (current_account, uri);
3817 fidata->output_stream = g_object_ref (stream);
3819 priv->fetching_images++;
3820 if (g_thread_create (on_fetch_image_thread, fidata, FALSE, NULL) == NULL) {
3821 g_object_unref (fidata->output_stream);
3822 g_free (fidata->cache_id);
3823 g_free (fidata->uri);
3824 g_object_unref (fidata->msg_view);
3825 g_slice_free (FetchImageData, fidata);
3826 tny_stream_close (stream);
3827 priv->fetching_images--;
3828 update_progress_hint (window);
3831 update_progress_hint (window);
3837 setup_menu (ModestMsgViewWindow *self)
3839 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW(self));
3841 /* Settings menu buttons */
3842 modest_hildon2_window_add_to_menu (MODEST_HILDON2_WINDOW (self), _("mcen_me_viewer_find"), NULL,
3843 APP_MENU_CALLBACK (modest_msg_view_window_show_find_toolbar),
3844 MODEST_DIMMING_CALLBACK (modest_ui_dimming_rules_on_find_in_msg));
3846 modest_hildon2_window_add_to_menu (MODEST_HILDON2_WINDOW (self),
3847 dngettext(GETTEXT_PACKAGE,
3848 "mcen_me_move_message",
3849 "mcen_me_move_messages",
3852 APP_MENU_CALLBACK (modest_ui_actions_on_move_to),
3853 MODEST_DIMMING_CALLBACK (modest_ui_dimming_rules_on_move_to));
3855 modest_hildon2_window_add_to_menu (MODEST_HILDON2_WINDOW (self), _("mcen_me_inbox_mark_as_read"), NULL,
3856 APP_MENU_CALLBACK (modest_ui_actions_on_mark_as_read),
3857 MODEST_DIMMING_CALLBACK (modest_ui_dimming_rules_on_mark_as_read_msg_in_view));
3859 modest_hildon2_window_add_to_menu (MODEST_HILDON2_WINDOW (self), _("mcen_me_inbox_mark_as_unread"), NULL,
3860 APP_MENU_CALLBACK (modest_ui_actions_on_mark_as_unread),
3861 MODEST_DIMMING_CALLBACK (modest_ui_dimming_rules_on_mark_as_unread_msg_in_view));
3863 modest_hildon2_window_add_to_menu (MODEST_HILDON2_WINDOW (self), _("mcen_me_viewer_save_attachments"), NULL,
3864 APP_MENU_CALLBACK (modest_ui_actions_save_attachments),
3865 MODEST_DIMMING_CALLBACK (modest_ui_dimming_rules_on_save_attachments));
3866 modest_hildon2_window_add_to_menu (MODEST_HILDON2_WINDOW (self), _("mcen_me_inbox_remove_attachments"), NULL,
3867 APP_MENU_CALLBACK (modest_ui_actions_remove_attachments),
3868 MODEST_DIMMING_CALLBACK (modest_ui_dimming_rules_on_remove_attachments));
3870 modest_hildon2_window_add_to_menu (MODEST_HILDON2_WINDOW (self), _("mcen_me_new_message"), "<Control>n",
3871 APP_MENU_CALLBACK (modest_ui_actions_on_new_msg),
3872 MODEST_DIMMING_CALLBACK (modest_ui_dimming_rules_on_new_msg));
3873 modest_hildon2_window_add_to_menu (MODEST_HILDON2_WINDOW (self), _("mcen_me_viewer_addtocontacts"), NULL,
3874 APP_MENU_CALLBACK (modest_ui_actions_add_to_contacts),
3875 MODEST_DIMMING_CALLBACK (modest_ui_dimming_rules_on_add_to_contacts));
3877 modest_hildon2_window_add_to_menu (MODEST_HILDON2_WINDOW (self), _("mcen_ti_message_properties"), NULL,
3878 APP_MENU_CALLBACK (modest_ui_actions_on_details),
3879 MODEST_DIMMING_CALLBACK (modest_ui_dimming_rules_on_details));
3883 modest_msg_view_window_add_to_contacts (ModestMsgViewWindow *self)
3885 ModestMsgViewWindowPrivate *priv;
3886 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
3887 GSList *recipients = NULL;
3890 msg = tny_msg_view_get_msg (TNY_MSG_VIEW (priv->msg_view));
3894 header = modest_msg_view_window_get_header (self);
3897 recipients = modest_tny_msg_header_get_all_recipients_list (header);
3898 g_object_unref (header);
3900 recipients = modest_tny_msg_get_all_recipients_list (msg);
3901 g_object_unref (msg);
3905 /* Offer the user to add recipients to the address book */
3906 modest_address_book_add_address_list_with_selector (recipients, (GtkWindow *) self);
3907 g_slist_foreach (recipients, (GFunc) g_free, NULL); g_slist_free (recipients);
3912 _modest_msg_view_window_map_event (GtkWidget *widget,
3916 ModestMsgViewWindow *self = (ModestMsgViewWindow *) userdata;
3918 update_progress_hint (self);
3924 modest_msg_view_window_fetch_images (ModestMsgViewWindow *self)
3926 ModestMsgViewWindowPrivate *priv;
3927 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
3929 modest_msg_view_request_fetch_images (MODEST_MSG_VIEW (priv->msg_view));
3933 modest_msg_view_window_has_blocked_external_images (ModestMsgViewWindow *self)
3935 ModestMsgViewWindowPrivate *priv;
3936 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
3938 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self), FALSE);
3940 return modest_msg_view_has_blocked_external_images (MODEST_MSG_VIEW (priv->msg_view));
3944 modest_msg_view_window_reload (ModestMsgViewWindow *self)
3946 ModestMsgViewWindowPrivate *priv;
3947 const gchar *msg_uid;
3948 TnyHeader *header = NULL;
3949 TnyFolder *folder = NULL;
3951 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self));
3953 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
3955 header = modest_msg_view_window_get_header (MODEST_MSG_VIEW_WINDOW (self));
3959 folder = tny_header_get_folder (header);
3960 g_object_unref (header);
3965 msg_uid = modest_msg_view_window_get_message_uid (self);
3967 GtkTreeRowReference *row_reference;
3969 if (priv->row_reference && gtk_tree_row_reference_valid (priv->row_reference)) {
3970 row_reference = priv->row_reference;
3972 row_reference = NULL;
3974 if (!message_reader (self, priv, NULL, msg_uid, folder, row_reference))
3975 g_warning ("Shouldn't happen, trying to reload a message failed");
3978 g_object_unref (folder);
3982 update_branding (ModestMsgViewWindow *self)
3984 const gchar *account;
3985 const gchar *mailbox;
3986 ModestAccountMgr *mgr;
3987 ModestProtocol *protocol = NULL;
3988 gchar *service_name = NULL;
3989 const GdkPixbuf *service_icon = NULL;
3990 ModestMsgViewWindowPrivate *priv;
3992 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
3994 account = modest_window_get_active_account (MODEST_WINDOW (self));
3995 mailbox = modest_window_get_active_mailbox (MODEST_WINDOW (self));
3997 mgr = modest_runtime_get_account_mgr ();
3999 if (modest_account_mgr_account_is_multimailbox (mgr, account, &protocol)) {
4000 if (MODEST_IS_ACCOUNT_PROTOCOL (protocol)) {
4001 service_name = modest_account_protocol_get_service_name (MODEST_ACCOUNT_PROTOCOL (protocol),
4003 service_icon = modest_account_protocol_get_service_icon (MODEST_ACCOUNT_PROTOCOL (protocol),
4004 account, mailbox, MODEST_ICON_SIZE_SMALL);
4008 modest_msg_view_set_branding (MODEST_MSG_VIEW (priv->msg_view), service_name, service_icon);
4009 g_free (service_name);
4013 sync_flags (ModestMsgViewWindow *self)
4015 TnyHeader *header = NULL;
4017 header = modest_msg_view_window_get_header (self);
4019 TnyMsg *msg = modest_msg_view_window_get_message (self);
4021 header = tny_msg_get_header (msg);
4022 g_object_unref (msg);
4027 TnyFolder *folder = tny_header_get_folder (header);
4030 ModestMailOperation *mail_op;
4032 /* Sync folder, we need this to save the seen flag */
4033 mail_op = modest_mail_operation_new (NULL);
4034 modest_mail_operation_queue_add (modest_runtime_get_mail_operation_queue (),
4036 modest_mail_operation_sync_folder (mail_op, folder, FALSE, NULL, NULL);
4037 g_object_unref (mail_op);
4038 g_object_unref (folder);
4040 g_object_unref (header);