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 /* Get the window */
2277 self = (ModestMsgViewWindow *) modest_mail_operation_get_source (mail_op);
2278 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self));
2279 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
2281 /* Update the row reference */
2282 if (priv->row_reference != NULL) {
2283 gtk_tree_row_reference_free (priv->row_reference);
2284 priv->row_reference = (row_reference && gtk_tree_row_reference_valid (row_reference))?gtk_tree_row_reference_copy (row_reference):NULL;
2285 if (priv->next_row_reference != NULL) {
2286 gtk_tree_row_reference_free (priv->next_row_reference);
2288 if (priv->row_reference) {
2289 priv->next_row_reference = gtk_tree_row_reference_copy (priv->row_reference);
2290 select_next_valid_row (priv->header_model, &(priv->next_row_reference), TRUE, priv->is_outbox);
2292 priv->next_row_reference = NULL;
2296 /* Mark header as read */
2297 if (!(tny_header_get_flags (header) & TNY_HEADER_FLAG_SEEN))
2298 tny_header_set_flag (header, TNY_HEADER_FLAG_SEEN);
2300 /* Set new message */
2301 if (priv->msg_view != NULL && TNY_IS_MSG_VIEW (priv->msg_view)) {
2302 tny_msg_view_set_msg (TNY_MSG_VIEW (priv->msg_view), msg);
2303 modest_msg_view_window_update_priority (self);
2304 update_window_title (MODEST_MSG_VIEW_WINDOW (self));
2305 update_branding (MODEST_MSG_VIEW_WINDOW (self));
2306 modest_msg_view_grab_focus (MODEST_MSG_VIEW (priv->msg_view));
2309 /* Set the new message uid of the window */
2310 if (priv->msg_uid) {
2311 g_free (priv->msg_uid);
2312 priv->msg_uid = modest_tny_folder_get_header_unique_id (header);
2315 /* Notify the observers */
2316 g_signal_emit (G_OBJECT (self), signals[MSG_CHANGED_SIGNAL],
2317 0, priv->header_model, priv->row_reference);
2319 /* Sync the flags if the message is not opened from a header
2320 model, i.e, if it's opened from a notification */
2321 if (!priv->header_model)
2325 g_object_unref (self);
2327 gtk_tree_row_reference_free (row_reference);
2331 modest_msg_view_window_get_folder_type (ModestMsgViewWindow *window)
2333 ModestMsgViewWindowPrivate *priv;
2335 TnyFolderType folder_type;
2337 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
2339 folder_type = TNY_FOLDER_TYPE_UNKNOWN;
2341 msg = tny_msg_view_get_msg (TNY_MSG_VIEW (priv->msg_view));
2345 folder = tny_msg_get_folder (msg);
2347 folder_type = modest_tny_folder_guess_folder_type (folder);
2348 g_object_unref (folder);
2350 g_object_unref (msg);
2358 modest_msg_view_window_update_priority (ModestMsgViewWindow *window)
2360 ModestMsgViewWindowPrivate *priv;
2361 TnyHeader *header = NULL;
2362 TnyHeaderFlags flags = 0;
2364 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
2366 if (priv->header_model && priv->row_reference && gtk_tree_row_reference_valid (priv->row_reference)) {
2368 GtkTreePath *path = NULL;
2370 path = gtk_tree_row_reference_get_path (priv->row_reference);
2371 g_return_if_fail (path != NULL);
2372 gtk_tree_model_get_iter (priv->header_model,
2374 gtk_tree_row_reference_get_path (priv->row_reference));
2376 gtk_tree_model_get (priv->header_model, &iter, TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
2378 gtk_tree_path_free (path);
2381 msg = tny_msg_view_get_msg (TNY_MSG_VIEW (priv->msg_view));
2383 header = tny_msg_get_header (msg);
2384 g_object_unref (msg);
2389 flags = tny_header_get_flags (header);
2390 g_object_unref(G_OBJECT(header));
2393 modest_msg_view_set_priority (MODEST_MSG_VIEW(priv->msg_view), flags);
2398 toolbar_resize (ModestMsgViewWindow *self)
2400 ModestMsgViewWindowPrivate *priv = NULL;
2401 ModestWindowPrivate *parent_priv = NULL;
2403 gint static_button_size;
2404 ModestWindowMgr *mgr;
2406 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self));
2407 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
2408 parent_priv = MODEST_WINDOW_GET_PRIVATE(self);
2410 mgr = modest_runtime_get_window_mgr ();
2411 static_button_size = modest_window_mgr_get_fullscreen_mode (mgr)?120:120;
2413 if (parent_priv->toolbar) {
2414 /* Set expandable and homogeneous tool buttons */
2415 widget = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/ToolbarMessageReply");
2416 gtk_tool_item_set_expand (GTK_TOOL_ITEM (widget), TRUE);
2417 gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (widget), TRUE);
2418 widget = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/ToolbarMessageReplyAll");
2419 gtk_tool_item_set_expand (GTK_TOOL_ITEM (widget), TRUE);
2420 gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (widget), TRUE);
2421 widget = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/ToolbarMessageForward");
2422 gtk_tool_item_set_expand (GTK_TOOL_ITEM (widget), TRUE);
2423 gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (widget), TRUE);
2424 widget = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/ToolbarDeleteMessage");
2425 gtk_tool_item_set_expand (GTK_TOOL_ITEM (widget), TRUE);
2426 gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (widget), TRUE);
2427 widget = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/ToolbarDownloadExternalImages");
2428 gtk_tool_item_set_expand (GTK_TOOL_ITEM (widget), TRUE);
2429 gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (widget), TRUE);
2430 gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (priv->next_toolitem), TRUE);
2431 gtk_tool_item_set_expand (GTK_TOOL_ITEM (priv->next_toolitem), TRUE);
2432 gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (priv->prev_toolitem), TRUE);
2433 gtk_tool_item_set_expand (GTK_TOOL_ITEM (priv->prev_toolitem), TRUE);
2438 modest_msg_view_window_show_toolbar (ModestWindow *self,
2439 gboolean show_toolbar)
2441 ModestMsgViewWindowPrivate *priv = NULL;
2442 ModestWindowPrivate *parent_priv;
2444 parent_priv = MODEST_WINDOW_GET_PRIVATE(self);
2445 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(self);
2447 /* Set optimized view status */
2448 priv->optimized_view = !show_toolbar;
2450 if (!parent_priv->toolbar) {
2451 parent_priv->toolbar = gtk_ui_manager_get_widget (parent_priv->ui_manager,
2453 gtk_toolbar_set_icon_size (GTK_TOOLBAR (parent_priv->toolbar), HILDON_ICON_SIZE_FINGER);
2454 gtk_widget_set_no_show_all (parent_priv->toolbar, TRUE);
2456 priv->next_toolitem = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/ToolbarMessageNext");
2457 priv->prev_toolitem = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/ToolbarMessageBack");
2458 toolbar_resize (MODEST_MSG_VIEW_WINDOW (self));
2461 hildon_window_add_toolbar (HILDON_WINDOW (self),
2462 GTK_TOOLBAR (parent_priv->toolbar));
2467 /* Quick hack: this prevents toolbar icons "dance" when progress bar show status is changed */
2468 /* TODO: resize mode migth be GTK_RESIZE_QUEUE, in order to avoid unneccesary shows */
2469 gtk_container_set_resize_mode (GTK_CONTAINER(parent_priv->toolbar), GTK_RESIZE_IMMEDIATE);
2471 gtk_widget_show (GTK_WIDGET (parent_priv->toolbar));
2472 if (modest_msg_view_window_transfer_mode_enabled (MODEST_MSG_VIEW_WINDOW (self)))
2473 set_progress_hint (MODEST_MSG_VIEW_WINDOW (self), TRUE);
2475 set_progress_hint (MODEST_MSG_VIEW_WINDOW (self), FALSE);
2478 gtk_widget_set_no_show_all (parent_priv->toolbar, TRUE);
2479 gtk_widget_hide (GTK_WIDGET (parent_priv->toolbar));
2484 modest_msg_view_window_clipboard_owner_change (GtkClipboard *clipboard,
2486 ModestMsgViewWindow *window)
2488 if (!GTK_WIDGET_VISIBLE (window))
2491 modest_window_check_dimming_rules_group (MODEST_WINDOW (window), MODEST_DIMMING_RULES_CLIPBOARD);
2495 modest_msg_view_window_transfer_mode_enabled (ModestMsgViewWindow *self)
2497 ModestMsgViewWindowPrivate *priv;
2499 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self), FALSE);
2500 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(self);
2502 return priv->progress_hint;
2506 observers_empty (ModestMsgViewWindow *self)
2509 ModestMsgViewWindowPrivate *priv;
2510 gboolean is_empty = TRUE;
2511 guint pending_ops = 0;
2513 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(self);
2514 tmp = priv->progress_widgets;
2516 /* Check all observers */
2517 while (tmp && is_empty) {
2518 pending_ops = modest_progress_object_num_pending_operations (MODEST_PROGRESS_OBJECT(tmp->data));
2519 is_empty = pending_ops == 0;
2521 tmp = g_slist_next(tmp);
2528 on_account_removed (TnyAccountStore *account_store,
2529 TnyAccount *account,
2532 /* Do nothing if it's a transport account, because we only
2533 show the messages of a store account */
2534 if (tny_account_get_account_type(account) == TNY_ACCOUNT_TYPE_STORE) {
2535 const gchar *parent_acc = NULL;
2536 const gchar *our_acc = NULL;
2538 our_acc = modest_window_get_active_account (MODEST_WINDOW (user_data));
2539 parent_acc = modest_tny_account_get_parent_modest_account_name_for_server_account (account);
2541 /* Close this window if I'm showing a message of the removed account */
2542 if (our_acc && parent_acc && strcmp (parent_acc, our_acc) == 0)
2543 modest_ui_actions_on_close_window (NULL, MODEST_WINDOW (user_data));
2548 on_mail_operation_started (ModestMailOperation *mail_op,
2551 ModestMsgViewWindow *self;
2552 ModestMailOperationTypeOperation op_type;
2554 ModestMsgViewWindowPrivate *priv;
2555 GObject *source = NULL;
2557 self = MODEST_MSG_VIEW_WINDOW (user_data);
2558 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
2559 op_type = modest_mail_operation_get_type_operation (mail_op);
2560 tmp = priv->progress_widgets;
2561 source = modest_mail_operation_get_source(mail_op);
2562 if (G_OBJECT (self) == source) {
2563 if (op_type == MODEST_MAIL_OPERATION_TYPE_RECEIVE ||
2564 op_type == MODEST_MAIL_OPERATION_TYPE_OPEN ||
2565 op_type == MODEST_MAIL_OPERATION_TYPE_DELETE) {
2566 set_progress_hint (self, TRUE);
2568 modest_progress_object_add_operation (
2569 MODEST_PROGRESS_OBJECT (tmp->data),
2571 tmp = g_slist_next (tmp);
2575 g_object_unref (source);
2577 /* Update dimming rules */
2578 check_dimming_rules_after_change (self);
2582 on_mail_operation_finished (ModestMailOperation *mail_op,
2585 ModestMsgViewWindow *self;
2586 ModestMailOperationTypeOperation op_type;
2588 ModestMsgViewWindowPrivate *priv;
2590 self = MODEST_MSG_VIEW_WINDOW (user_data);
2591 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
2592 op_type = modest_mail_operation_get_type_operation (mail_op);
2593 tmp = priv->progress_widgets;
2595 if (op_type == MODEST_MAIL_OPERATION_TYPE_RECEIVE ||
2596 op_type == MODEST_MAIL_OPERATION_TYPE_OPEN ||
2597 op_type == MODEST_MAIL_OPERATION_TYPE_DELETE) {
2599 modest_progress_object_remove_operation (MODEST_PROGRESS_OBJECT (tmp->data),
2601 tmp = g_slist_next (tmp);
2604 /* If no more operations are being observed, NORMAL mode is enabled again */
2605 if (observers_empty (self)) {
2606 set_progress_hint (self, FALSE);
2610 /* Update dimming rules. We have to do this right here
2611 and not in view_msg_cb because at that point the
2612 transfer mode is still enabled so the dimming rule
2613 won't let the user delete the message that has been
2614 readed for example */
2615 check_dimming_rules_after_change (self);
2619 on_queue_changed (ModestMailOperationQueue *queue,
2620 ModestMailOperation *mail_op,
2621 ModestMailOperationQueueNotification type,
2622 ModestMsgViewWindow *self)
2624 ModestMsgViewWindowPrivate *priv;
2626 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
2628 /* If this operations was created by another window, do nothing */
2629 if (!modest_mail_operation_is_mine (mail_op, G_OBJECT(self)))
2632 if (type == MODEST_MAIL_OPERATION_QUEUE_OPERATION_ADDED) {
2633 priv->sighandlers = modest_signal_mgr_connect (priv->sighandlers,
2635 "operation-started",
2636 G_CALLBACK (on_mail_operation_started),
2638 priv->sighandlers = modest_signal_mgr_connect (priv->sighandlers,
2640 "operation-finished",
2641 G_CALLBACK (on_mail_operation_finished),
2643 } else if (type == MODEST_MAIL_OPERATION_QUEUE_OPERATION_REMOVED) {
2644 priv->sighandlers = modest_signal_mgr_disconnect (priv->sighandlers,
2646 "operation-started");
2647 priv->sighandlers = modest_signal_mgr_disconnect (priv->sighandlers,
2649 "operation-finished");
2654 modest_msg_view_window_get_attachments (ModestMsgViewWindow *win)
2656 ModestMsgViewWindowPrivate *priv;
2657 TnyList *selected_attachments = NULL;
2659 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (win), NULL);
2660 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (win);
2662 /* In Hildon 2.2 as there's no selection we assume we have all attachments selected */
2663 selected_attachments = modest_msg_view_get_attachments (MODEST_MSG_VIEW (priv->msg_view));
2665 return selected_attachments;
2669 ModestMsgViewWindow *self;
2671 gchar *attachment_uid;
2672 } DecodeAsyncHelper;
2675 on_decode_to_stream_async_handler (TnyMimePart *mime_part,
2681 DecodeAsyncHelper *helper = (DecodeAsyncHelper *) user_data;
2682 const gchar *content_type;
2683 ModestMsgViewWindowPrivate *priv;
2685 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (helper->self);
2687 if (cancelled || err) {
2690 if ((err->domain == TNY_ERROR_DOMAIN) &&
2691 (err->code == TNY_IO_ERROR_WRITE) &&
2692 (errno == ENOSPC)) {
2693 msg = g_strdup_printf (_KR("cerm_device_memory_full"), "");
2695 msg = g_strdup (_("mail_ib_file_operation_failed"));
2697 modest_platform_information_banner (NULL, NULL, msg);
2703 /* It could happen that the window was closed. So we
2704 assume it is a cancelation */
2705 if (!GTK_WIDGET_VISIBLE (helper->self))
2708 /* Remove the progress hint */
2709 set_progress_hint (helper->self, FALSE);
2711 content_type = tny_mime_part_get_content_type (mime_part);
2712 if (content_type && g_str_has_prefix (content_type, "message/rfc822")) {
2713 ModestWindowMgr *mgr;
2714 ModestWindow *msg_win = NULL;
2717 const gchar *mailbox;
2718 TnyStream *file_stream;
2721 fd = g_open (helper->file_path, O_RDONLY, 0644);
2724 file_stream = tny_fs_stream_new (fd);
2726 mgr = modest_runtime_get_window_mgr ();
2728 account = g_strdup (modest_window_get_active_account (MODEST_WINDOW (helper->self)));
2729 mailbox = modest_window_get_active_mailbox (MODEST_WINDOW (helper->self));
2732 account = modest_account_mgr_get_default_account (modest_runtime_get_account_mgr ());
2734 msg = tny_camel_msg_new ();
2735 tny_camel_msg_parse (msg, file_stream);
2738 top_msg = g_object_ref (priv->top_msg);
2740 top_msg = tny_msg_view_get_msg (TNY_MSG_VIEW (priv->msg_view));
2742 msg_win = modest_msg_view_window_new_for_attachment (TNY_MSG (msg), top_msg,
2743 account, mailbox, helper->attachment_uid);
2744 if (top_msg) g_object_unref (top_msg);
2745 modest_window_set_zoom (MODEST_WINDOW (msg_win),
2746 modest_window_get_zoom (MODEST_WINDOW (helper->self)));
2747 if (modest_window_mgr_register_window (mgr, msg_win, MODEST_WINDOW (helper->self)))
2748 gtk_widget_show_all (GTK_WIDGET (msg_win));
2750 gtk_widget_destroy (GTK_WIDGET (msg_win));
2751 g_object_unref (msg);
2752 g_object_unref (file_stream);
2754 modest_platform_information_banner (NULL, NULL, _("mail_ib_file_operation_failed"));
2759 /* make the file read-only */
2760 g_chmod(helper->file_path, 0444);
2762 /* Activate the file */
2763 modest_platform_activate_file (helper->file_path, content_type);
2768 g_object_unref (helper->self);
2769 g_free (helper->file_path);
2770 g_free (helper->attachment_uid);
2771 g_slice_free (DecodeAsyncHelper, helper);
2775 view_attachment_connect_handler (gboolean canceled,
2777 GtkWindow *parent_window,
2778 TnyAccount *account,
2782 if (canceled || err) {
2783 g_object_unref (part);
2787 modest_msg_view_window_view_attachment (MODEST_MSG_VIEW_WINDOW (parent_window),
2789 g_object_unref (part);
2793 modest_msg_view_window_view_attachment (ModestMsgViewWindow *window,
2794 TnyMimePart *mime_part)
2796 ModestMsgViewWindowPrivate *priv;
2797 const gchar *msg_uid;
2798 gchar *attachment_uid = NULL;
2799 gint attachment_index = 0;
2800 TnyList *attachments;
2802 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window));
2803 g_return_if_fail (TNY_IS_MIME_PART (mime_part) || (mime_part == NULL));
2804 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
2806 msg_uid = modest_msg_view_window_get_message_uid (MODEST_MSG_VIEW_WINDOW (window));
2807 attachments = modest_msg_view_get_attachments (MODEST_MSG_VIEW (priv->msg_view));
2808 attachment_index = modest_list_index (attachments, (GObject *) mime_part);
2809 g_object_unref (attachments);
2811 if (msg_uid && attachment_index >= 0) {
2812 attachment_uid = g_strdup_printf ("%s/%d", msg_uid, attachment_index);
2815 if (mime_part == NULL) {
2816 gboolean error = FALSE;
2817 TnyList *selected_attachments = modest_msg_view_get_selected_attachments (MODEST_MSG_VIEW (priv->msg_view));
2818 if (selected_attachments == NULL || tny_list_get_length (selected_attachments) == 0) {
2820 } else if (tny_list_get_length (selected_attachments) > 1) {
2821 hildon_banner_show_information (NULL, NULL, _("mcen_ib_unable_to_display_more"));
2825 iter = tny_list_create_iterator (selected_attachments);
2826 mime_part = (TnyMimePart *) tny_iterator_get_current (iter);
2827 g_object_unref (iter);
2829 if (selected_attachments)
2830 g_object_unref (selected_attachments);
2835 g_object_ref (mime_part);
2838 if (tny_mime_part_is_purged (mime_part))
2841 if (TNY_IS_CAMEL_BS_MIME_PART (mime_part) &&
2842 !tny_camel_bs_mime_part_is_fetched (TNY_CAMEL_BS_MIME_PART (mime_part))) {
2844 TnyAccount *account;
2846 is_merge = g_str_has_prefix (priv->msg_uid, "merge:");
2848 /* Get the account */
2850 account = tny_account_store_find_account (TNY_ACCOUNT_STORE (modest_runtime_get_account_store ()),
2853 if (!tny_device_is_online (modest_runtime_get_device())) {
2854 modest_platform_connect_and_perform (GTK_WINDOW (window),
2856 TNY_ACCOUNT (account),
2857 (ModestConnectedPerformer) view_attachment_connect_handler,
2858 g_object_ref (mime_part));
2863 if (!modest_tny_mime_part_is_msg (mime_part) && tny_mime_part_get_filename (mime_part)) {
2864 gchar *filepath = NULL;
2865 const gchar *att_filename = tny_mime_part_get_filename (mime_part);
2866 gboolean show_error_banner = FALSE;
2867 TnyFsStream *temp_stream = NULL;
2868 temp_stream = modest_utils_create_temp_stream (att_filename, attachment_uid,
2871 if (temp_stream != NULL) {
2872 ModestAccountMgr *mgr;
2873 DecodeAsyncHelper *helper;
2874 gboolean decode_in_provider;
2875 ModestProtocol *protocol;
2876 const gchar *account;
2878 /* Activate progress hint */
2879 set_progress_hint (window, TRUE);
2881 helper = g_slice_new0 (DecodeAsyncHelper);
2882 helper->self = g_object_ref (window);
2883 helper->file_path = g_strdup (filepath);
2884 helper->attachment_uid = g_strdup (attachment_uid);
2886 decode_in_provider = FALSE;
2887 mgr = modest_runtime_get_account_mgr ();
2888 account = modest_window_get_active_account (MODEST_WINDOW (window));
2889 if (modest_account_mgr_account_is_multimailbox (mgr, account, &protocol)) {
2890 if (MODEST_IS_ACCOUNT_PROTOCOL (protocol)) {
2892 uri = g_strconcat ("file://", filepath, NULL);
2893 decode_in_provider =
2894 modest_account_protocol_decode_part_to_stream_async (
2895 MODEST_ACCOUNT_PROTOCOL (protocol),
2898 TNY_STREAM (temp_stream),
2899 on_decode_to_stream_async_handler,
2906 if (!decode_in_provider)
2907 tny_mime_part_decode_to_stream_async (mime_part, TNY_STREAM (temp_stream),
2908 on_decode_to_stream_async_handler,
2911 g_object_unref (temp_stream);
2912 /* NOTE: files in the temporary area will be automatically
2913 * cleaned after some time if they are no longer in use */
2916 const gchar *content_type;
2917 /* the file may already exist but it isn't writable,
2918 * let's try to open it anyway */
2919 content_type = tny_mime_part_get_content_type (mime_part);
2920 modest_platform_activate_file (filepath, content_type);
2922 g_warning ("%s: modest_utils_create_temp_stream failed", __FUNCTION__);
2923 show_error_banner = TRUE;
2928 if (show_error_banner)
2929 modest_platform_information_banner (NULL, NULL, _("mail_ib_file_operation_failed"));
2930 } else if (!modest_tny_mime_part_is_msg (mime_part)) {
2931 ModestWindowMgr *mgr;
2932 ModestWindow *msg_win = NULL;
2933 TnyMsg *current_msg;
2937 current_msg = modest_msg_view_window_get_message (MODEST_MSG_VIEW_WINDOW (window));
2938 mgr = modest_runtime_get_window_mgr ();
2939 header = tny_msg_get_header (TNY_MSG (current_msg));
2940 found = modest_window_mgr_find_registered_message_uid (mgr,
2945 g_debug ("window for this body is already being created");
2949 /* it's not found, so create a new window for it */
2950 modest_window_mgr_register_header (mgr, header, attachment_uid); /* register the uid before building the window */
2951 gchar *account = g_strdup (modest_window_get_active_account (MODEST_WINDOW (window)));
2952 const gchar *mailbox = modest_window_get_active_mailbox (MODEST_WINDOW (window));
2954 account = modest_account_mgr_get_default_account (modest_runtime_get_account_mgr ());
2957 top_msg = g_object_ref (priv->top_msg);
2959 top_msg = tny_msg_view_get_msg (TNY_MSG_VIEW (priv->msg_view));
2961 msg_win = modest_msg_view_window_new_with_other_body (TNY_MSG (current_msg), TNY_MIME_PART (mime_part), top_msg,
2962 account, mailbox, attachment_uid);
2964 if (top_msg) g_object_unref (top_msg);
2966 modest_window_set_zoom (MODEST_WINDOW (msg_win),
2967 modest_window_get_zoom (MODEST_WINDOW (window)));
2968 if (modest_window_mgr_register_window (mgr, msg_win, MODEST_WINDOW (window)))
2969 gtk_widget_show_all (GTK_WIDGET (msg_win));
2971 gtk_widget_destroy (GTK_WIDGET (msg_win));
2973 g_object_unref (current_msg);
2975 /* message attachment */
2976 TnyHeader *header = NULL;
2977 ModestWindowMgr *mgr;
2978 ModestWindow *msg_win = NULL;
2981 header = tny_msg_get_header (TNY_MSG (mime_part));
2982 mgr = modest_runtime_get_window_mgr ();
2983 found = modest_window_mgr_find_registered_header (mgr, header, &msg_win);
2986 /* if it's found, but there is no msg_win, it's probably in the process of being created;
2987 * thus, we don't do anything */
2988 g_debug ("window for is already being created");
2991 /* it's not found, so create a new window for it */
2992 modest_window_mgr_register_header (mgr, header, attachment_uid); /* register the uid before building the window */
2993 gchar *account = g_strdup (modest_window_get_active_account (MODEST_WINDOW (window)));
2994 const gchar *mailbox = modest_window_get_active_mailbox (MODEST_WINDOW (window));
2996 account = modest_account_mgr_get_default_account (modest_runtime_get_account_mgr ());
2998 top_msg = g_object_ref (priv->top_msg);
3000 top_msg = tny_msg_view_get_msg (TNY_MSG_VIEW (priv->msg_view));
3001 msg_win = modest_msg_view_window_new_for_attachment (
3002 TNY_MSG (mime_part), top_msg, account,
3003 mailbox, attachment_uid);
3004 modest_window_set_zoom (MODEST_WINDOW (msg_win),
3005 modest_window_get_zoom (MODEST_WINDOW (window)));
3006 if (modest_window_mgr_register_window (mgr, msg_win, MODEST_WINDOW (window)))
3007 gtk_widget_show_all (GTK_WIDGET (msg_win));
3009 gtk_widget_destroy (GTK_WIDGET (msg_win));
3015 g_free (attachment_uid);
3017 g_object_unref (mime_part);
3029 GnomeVFSResult result;
3031 ModestMsgViewWindow *window;
3034 static void save_mime_part_info_free (SaveMimePartInfo *info, gboolean with_struct);
3035 static gboolean idle_save_mime_part_show_result (SaveMimePartInfo *info);
3036 static gpointer save_mime_part_to_file (SaveMimePartInfo *info);
3037 static void save_mime_parts_to_file_with_checks (GtkWindow *parent, SaveMimePartInfo *info);
3040 save_mime_part_info_free (SaveMimePartInfo *info, gboolean with_struct)
3044 for (node = info->pairs; node != NULL; node = g_list_next (node)) {
3045 SaveMimePartPair *pair = (SaveMimePartPair *) node->data;
3046 g_free (pair->filename);
3047 g_object_unref (pair->part);
3048 g_slice_free (SaveMimePartPair, pair);
3050 g_list_free (info->pairs);
3053 g_object_unref (info->window);
3054 info->window = NULL;
3056 g_slice_free (SaveMimePartInfo, info);
3061 idle_save_mime_part_show_result (SaveMimePartInfo *info)
3063 /* This is a GDK lock because we are an idle callback and
3064 * hildon_banner_show_information is or does Gtk+ code */
3066 gdk_threads_enter (); /* CHECKED */
3067 if (info->result == GNOME_VFS_ERROR_CANCELLED) {
3069 } else if (info->result == GNOME_VFS_OK) {
3070 hildon_banner_show_information (NULL, NULL, _CS("sfil_ib_saved"));
3071 } else if (info->result == GNOME_VFS_ERROR_NO_SPACE) {
3074 /* Check if the uri belongs to the external mmc */
3075 if (g_str_has_prefix (info->uri, g_getenv (MODEST_MMC1_VOLUMEPATH_ENV)))
3076 msg = g_strdup_printf (_KR("cerm_device_memory_full"), "");
3078 msg = g_strdup (_KR("cerm_memory_card_full"));
3079 modest_platform_information_banner (NULL, NULL, msg);
3082 hildon_banner_show_information (NULL, NULL, _("mail_ib_file_operation_failed"));
3084 save_mime_part_info_free (info, FALSE);
3085 gdk_threads_leave (); /* CHECKED */
3091 save_mime_part_to_file_connect_handler (gboolean canceled,
3093 GtkWindow *parent_window,
3094 TnyAccount *account,
3095 SaveMimePartInfo *info)
3097 if (canceled || err) {
3098 if (canceled && !err) {
3099 info->result = GNOME_VFS_ERROR_CANCELLED;
3101 g_idle_add ((GSourceFunc) idle_save_mime_part_show_result, info);
3103 g_thread_create ((GThreadFunc)save_mime_part_to_file, info, FALSE, NULL);
3108 save_mime_part_to_file_connect_idle (SaveMimePartInfo *info)
3111 TnyAccount *account;
3112 ModestMsgViewWindowPrivate *priv;
3114 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (info->window);
3116 is_merge = g_str_has_prefix (priv->msg_uid, "merge:");
3119 /* Get the account */
3121 account = tny_account_store_find_account (TNY_ACCOUNT_STORE (modest_runtime_get_account_store ()),
3124 modest_platform_connect_and_perform (GTK_WINDOW (info->window),
3126 TNY_ACCOUNT (account),
3127 (ModestConnectedPerformer) save_mime_part_to_file_connect_handler,
3131 g_object_unref (account);
3137 save_mime_part_to_file (SaveMimePartInfo *info)
3139 GnomeVFSHandle *handle;
3141 SaveMimePartPair *pair = (SaveMimePartPair *) info->pairs->data;
3143 if (TNY_IS_CAMEL_BS_MIME_PART (pair->part) &&
3144 !tny_camel_bs_mime_part_is_fetched (TNY_CAMEL_BS_MIME_PART (pair->part))) {
3145 gboolean check_online = TRUE;
3146 ModestMsgViewWindowPrivate *priv = NULL;
3148 /* Check if we really need to connect to save the mime part */
3149 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (info->window);
3150 if (g_str_has_prefix (priv->msg_uid, "merge:")) {
3151 check_online = FALSE;
3153 TnyAccountStore *acc_store;
3154 TnyAccount *account = NULL;
3156 acc_store = (TnyAccountStore*) modest_runtime_get_account_store ();
3157 account = tny_account_store_find_account (acc_store, priv->msg_uid);
3160 if (tny_account_get_connection_status (account) ==
3161 TNY_CONNECTION_STATUS_CONNECTED)
3162 check_online = FALSE;
3163 g_object_unref (account);
3165 check_online = !tny_device_is_online (tny_account_store_get_device (acc_store));
3170 g_idle_add ((GSourceFunc) save_mime_part_to_file_connect_idle, info);
3175 info->result = gnome_vfs_create (&handle, pair->filename, GNOME_VFS_OPEN_WRITE, FALSE, 0644);
3176 if (info->result == GNOME_VFS_OK) {
3177 GError *error = NULL;
3178 gboolean decode_in_provider;
3180 ModestAccountMgr *mgr;
3181 const gchar *account;
3182 ModestProtocol *protocol = NULL;
3184 stream = tny_vfs_stream_new (handle);
3186 decode_in_provider = FALSE;
3187 mgr = modest_runtime_get_account_mgr ();
3188 account = modest_window_get_active_account (MODEST_WINDOW (info->window));
3189 if (modest_account_mgr_account_is_multimailbox (mgr, account, &protocol)) {
3190 if (MODEST_IS_ACCOUNT_PROTOCOL (protocol)) {
3191 decode_in_provider =
3192 modest_account_protocol_decode_part_to_stream (
3193 MODEST_ACCOUNT_PROTOCOL (protocol),
3201 if (!decode_in_provider)
3202 written = tny_mime_part_decode_to_stream (pair->part, stream, &error);
3205 g_warning ("modest: could not save attachment %s: %d (%s)\n", pair->filename, error?error->code:-1, error?error->message:"Unknown error");
3207 if ((error->domain == TNY_ERROR_DOMAIN) &&
3208 (error->code == TNY_IO_ERROR_WRITE) &&
3209 (errno == ENOSPC)) {
3210 info->result = GNOME_VFS_ERROR_NO_SPACE;
3212 info->result = GNOME_VFS_ERROR_IO;
3215 g_object_unref (G_OBJECT (stream));
3217 g_warning ("Could not create save attachment %s: %s\n",
3218 pair->filename, gnome_vfs_result_to_string (info->result));
3221 /* Go on saving remaining files */
3222 info->pairs = g_list_remove_link (info->pairs, info->pairs);
3223 if (info->pairs != NULL) {
3224 save_mime_part_to_file (info);
3226 g_idle_add ((GSourceFunc) idle_save_mime_part_show_result, info);
3233 save_mime_parts_to_file_with_checks (GtkWindow *parent,
3234 SaveMimePartInfo *info)
3236 gboolean is_ok = TRUE;
3237 gint replaced_files = 0;
3238 const GList *files = info->pairs;
3239 const GList *iter, *to_replace = NULL;
3241 for (iter = files; (iter != NULL) && (replaced_files < 2); iter = g_list_next(iter)) {
3242 SaveMimePartPair *pair = iter->data;
3243 gchar *unescaped = g_uri_unescape_string (pair->filename, NULL);
3245 if (modest_utils_file_exists (unescaped)) {
3247 if (replaced_files == 1)
3252 if (replaced_files) {
3255 if (replaced_files == 1) {
3256 SaveMimePartPair *pair = to_replace->data;
3257 const gchar *basename = strrchr (pair->filename, G_DIR_SEPARATOR) + 1;
3258 gchar *escaped_basename, *message;
3260 escaped_basename = g_uri_unescape_string (basename, NULL);
3261 message = g_strdup_printf ("%s\n%s",
3262 _FM("docm_nc_replace_file"),
3263 (escaped_basename) ? escaped_basename : "");
3264 response = modest_platform_run_confirmation_dialog (parent, message);
3266 g_free (escaped_basename);
3268 response = modest_platform_run_confirmation_dialog (parent,
3269 _FM("docm_nc_replace_multiple"));
3271 if (response != GTK_RESPONSE_OK)
3276 save_mime_part_info_free (info, TRUE);
3278 g_thread_create ((GThreadFunc)save_mime_part_to_file, info, FALSE, NULL);
3283 typedef struct _SaveAttachmentsInfo {
3284 TnyList *attachments_list;
3285 ModestMsgViewWindow *window;
3286 } SaveAttachmentsInfo;
3289 save_attachments_response (GtkDialog *dialog,
3293 TnyList *mime_parts;
3295 GList *files_to_save = NULL;
3296 gchar *current_folder;
3297 SaveAttachmentsInfo *sa_info = (SaveAttachmentsInfo *) user_data;
3299 mime_parts = TNY_LIST (sa_info->attachments_list);
3301 if (arg1 != GTK_RESPONSE_OK)
3304 chooser_uri = gtk_file_chooser_get_uri (GTK_FILE_CHOOSER (dialog));
3305 current_folder = gtk_file_chooser_get_current_folder_uri (GTK_FILE_CHOOSER (dialog));
3306 if (current_folder && *current_folder != '\0') {
3308 modest_conf_set_string (modest_runtime_get_conf (), MODEST_CONF_LATEST_SAVE_ATTACHMENT_PATH,
3309 current_folder,&err);
3311 g_debug ("Error storing latest used folder: %s", err->message);
3315 g_free (current_folder);
3317 if (!modest_utils_folder_writable (chooser_uri)) {
3318 const gchar *err_msg;
3320 #ifdef MODEST_PLATFORM_MAEMO
3321 if (modest_maemo_utils_in_usb_mode ()) {
3322 err_msg = dgettext ("hildon-status-bar-usb", "usbh_ib_mmc_usb_connected");
3324 err_msg = _FM("sfil_ib_readonly_location");
3327 err_msg = _FM("sfil_ib_readonly_location");
3329 hildon_banner_show_information (NULL, NULL, err_msg);
3333 iter = tny_list_create_iterator (mime_parts);
3334 while (!tny_iterator_is_done (iter)) {
3335 TnyMimePart *mime_part = (TnyMimePart *) tny_iterator_get_current (iter);
3337 if ((modest_tny_mime_part_is_attachment_for_modest (mime_part)) &&
3338 !tny_mime_part_is_purged (mime_part) &&
3339 (tny_mime_part_get_filename (mime_part) != NULL)) {
3340 SaveMimePartPair *pair;
3342 pair = g_slice_new0 (SaveMimePartPair);
3344 if (tny_list_get_length (mime_parts) > 1) {
3346 gnome_vfs_escape_slashes (tny_mime_part_get_filename (mime_part));
3347 pair->filename = g_build_filename (chooser_uri, escaped, NULL);
3350 pair->filename = g_strdup (chooser_uri);
3352 pair->part = mime_part;
3353 files_to_save = g_list_prepend (files_to_save, pair);
3355 tny_iterator_next (iter);
3357 g_object_unref (iter);
3360 if (files_to_save != NULL) {
3361 SaveMimePartInfo *info = g_slice_new0 (SaveMimePartInfo);
3362 info->pairs = files_to_save;
3363 info->result = TRUE;
3364 info->uri = g_strdup (chooser_uri);
3365 info->window = g_object_ref (sa_info->window);
3366 save_mime_parts_to_file_with_checks ((GtkWindow *) dialog, info);
3368 g_free (chooser_uri);
3371 /* Free and close the dialog */
3372 g_object_unref (mime_parts);
3373 g_object_unref (sa_info->window);
3374 g_slice_free (SaveAttachmentsInfo, sa_info);
3375 gtk_widget_destroy (GTK_WIDGET (dialog));
3379 modest_msg_view_window_save_attachments (ModestMsgViewWindow *window,
3380 TnyList *mime_parts)
3382 ModestMsgViewWindowPrivate *priv;
3383 GtkWidget *save_dialog = NULL;
3384 gchar *conf_folder = NULL;
3385 gchar *filename = NULL;
3386 gchar *save_multiple_str = NULL;
3387 const gchar *root_folder = "file:///";
3389 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window));
3390 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
3392 if (mime_parts == NULL) {
3393 /* In Hildon 2.2 save and delete operate over all the attachments as there's no
3394 * selection available */
3395 mime_parts = modest_msg_view_get_attachments (MODEST_MSG_VIEW (priv->msg_view));
3396 if (mime_parts && !modest_maemo_utils_select_attachments (GTK_WINDOW (window), mime_parts, FALSE)) {
3397 g_object_unref (mime_parts);
3400 if (mime_parts == NULL || tny_list_get_length (mime_parts) == 0) {
3402 g_object_unref (mime_parts);
3408 g_object_ref (mime_parts);
3411 /* prepare dialog */
3412 if (tny_list_get_length (mime_parts) == 1) {
3414 /* only one attachment selected */
3415 iter = tny_list_create_iterator (mime_parts);
3416 TnyMimePart *mime_part = (TnyMimePart *) tny_iterator_get_current (iter);
3417 g_object_unref (iter);
3418 if (!modest_tny_mime_part_is_msg (mime_part) &&
3419 modest_tny_mime_part_is_attachment_for_modest (mime_part) &&
3420 !tny_mime_part_is_purged (mime_part)) {
3421 filename = g_strdup (tny_mime_part_get_filename (mime_part));
3423 /* TODO: show any error? */
3424 g_warning ("%s: Tried to save a non-file attachment", __FUNCTION__);
3425 g_object_unref (mime_parts);
3428 g_object_unref (mime_part);
3430 gint num = tny_list_get_length (mime_parts);
3431 save_multiple_str = g_strdup_printf (dngettext("hildon-fm",
3432 "sfil_va_number_of_objects_attachment",
3433 "sfil_va_number_of_objects_attachments",
3437 save_dialog = hildon_file_chooser_dialog_new (GTK_WINDOW (window),
3438 GTK_FILE_CHOOSER_ACTION_SAVE);
3440 /* Get last used folder */
3441 conf_folder = modest_conf_get_string (modest_runtime_get_conf (),
3442 MODEST_CONF_LATEST_SAVE_ATTACHMENT_PATH, NULL);
3444 /* File chooser stops working if we select "file:///" as current folder */
3445 if (conf_folder && g_ascii_strcasecmp (root_folder, conf_folder) != 0) {
3446 g_free (conf_folder);
3450 if (conf_folder && conf_folder[0] != '\0') {
3451 gtk_file_chooser_set_current_folder_uri (GTK_FILE_CHOOSER (save_dialog), conf_folder);
3454 /* Set the default folder to documents folder */
3455 docs_folder = (gchar *) g_strdup(g_get_user_special_dir (G_USER_DIRECTORY_DOCUMENTS));
3458 docs_folder = g_build_filename (g_getenv (MYDOCS_ENV), DOCS_FOLDER, NULL);
3460 gtk_file_chooser_set_current_folder (GTK_FILE_CHOOSER (save_dialog), docs_folder);
3461 g_free (docs_folder);
3463 g_free (conf_folder);
3467 gtk_file_chooser_set_current_name (GTK_FILE_CHOOSER (save_dialog),
3472 /* if multiple, set multiple string */
3473 if (save_multiple_str) {
3474 g_object_set (G_OBJECT (save_dialog), "save-multiple", save_multiple_str, NULL);
3475 gtk_window_set_title (GTK_WINDOW (save_dialog), _FM("sfil_ti_save_objects_files"));
3476 g_free (save_multiple_str);
3479 /* We must run this asynchronously, because the hildon dialog
3480 performs a gtk_dialog_run by itself which leads to gdk
3482 SaveAttachmentsInfo *sa_info;
3483 sa_info = g_slice_new (SaveAttachmentsInfo);
3484 sa_info->attachments_list = mime_parts;
3485 sa_info->window = g_object_ref (window);
3486 g_signal_connect (save_dialog, "response",
3487 G_CALLBACK (save_attachments_response), sa_info);
3489 gtk_widget_show_all (save_dialog);
3493 show_remove_attachment_information (gpointer userdata)
3495 ModestMsgViewWindow *window = (ModestMsgViewWindow *) userdata;
3496 ModestMsgViewWindowPrivate *priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
3498 /* We're outside the main lock */
3499 gdk_threads_enter ();
3501 if (priv->remove_attachment_banner != NULL) {
3502 gtk_widget_destroy (priv->remove_attachment_banner);
3503 g_object_unref (priv->remove_attachment_banner);
3506 priv->remove_attachment_banner = g_object_ref (
3507 hildon_banner_show_animation (NULL, NULL, _("mcen_me_inbox_remove_attachments")));
3509 gdk_threads_leave ();
3515 modest_msg_view_window_remove_attachments (ModestMsgViewWindow *window, gboolean get_all)
3517 ModestMsgViewWindowPrivate *priv;
3518 TnyList *mime_parts = NULL, *tmp;
3519 gchar *confirmation_message;
3525 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window));
3526 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
3528 /* In hildon 2.2 we ignore the get_all flag as we always get all attachments. This is
3529 * because we don't have selection
3531 mime_parts = modest_msg_view_get_attachments (MODEST_MSG_VIEW (priv->msg_view));
3533 /* Remove already purged messages from mime parts list. We use
3534 a copy of the list to remove items in the original one */
3535 tmp = tny_list_copy (mime_parts);
3536 iter = tny_list_create_iterator (tmp);
3537 while (!tny_iterator_is_done (iter)) {
3538 TnyMimePart *part = TNY_MIME_PART (tny_iterator_get_current (iter));
3539 if (tny_mime_part_is_purged (part))
3540 tny_list_remove (mime_parts, (GObject *) part);
3542 g_object_unref (part);
3543 tny_iterator_next (iter);
3545 g_object_unref (tmp);
3546 g_object_unref (iter);
3548 if (!modest_maemo_utils_select_attachments (GTK_WINDOW (window), mime_parts, TRUE) ||
3549 tny_list_get_length (mime_parts) == 0) {
3550 g_object_unref (mime_parts);
3554 n_attachments = tny_list_get_length (mime_parts);
3555 if (n_attachments == 1) {
3559 iter = tny_list_create_iterator (mime_parts);
3560 part = (TnyMimePart *) tny_iterator_get_current (iter);
3561 g_object_unref (iter);
3562 if (modest_tny_mime_part_is_msg (part)) {
3564 header = tny_msg_get_header (TNY_MSG (part));
3565 filename = tny_header_dup_subject (header);
3566 g_object_unref (header);
3567 if (filename == NULL)
3568 filename = g_strdup (_("mail_va_no_subject"));
3570 filename = g_strdup (tny_mime_part_get_filename (TNY_MIME_PART (part)));
3572 confirmation_message = g_strdup_printf (_("mcen_nc_purge_file_text"), filename);
3574 g_object_unref (part);
3576 confirmation_message = g_strdup_printf (ngettext("mcen_nc_purge_file_text",
3577 "mcen_nc_purge_files_text",
3578 n_attachments), n_attachments);
3580 response = modest_platform_run_confirmation_dialog (GTK_WINDOW (window),
3581 confirmation_message);
3582 g_free (confirmation_message);
3584 if (response != GTK_RESPONSE_OK) {
3585 g_object_unref (mime_parts);
3589 priv->purge_timeout = g_timeout_add (2000, show_remove_attachment_information, window);
3591 iter = tny_list_create_iterator (mime_parts);
3592 while (!tny_iterator_is_done (iter)) {
3595 part = (TnyMimePart *) tny_iterator_get_current (iter);
3596 tny_mime_part_set_purged (TNY_MIME_PART (part));
3597 g_object_unref (part);
3598 tny_iterator_next (iter);
3600 g_object_unref (iter);
3602 msg = tny_msg_view_get_msg (TNY_MSG_VIEW (priv->msg_view));
3603 tny_msg_view_clear (TNY_MSG_VIEW (priv->msg_view));
3604 tny_msg_rewrite_cache (msg);
3605 tny_msg_view_set_msg (TNY_MSG_VIEW (priv->msg_view), msg);
3606 g_object_unref (msg);
3607 update_branding (MODEST_MSG_VIEW_WINDOW (window));
3609 g_object_unref (mime_parts);
3611 if (priv->purge_timeout > 0) {
3612 g_source_remove (priv->purge_timeout);
3613 priv->purge_timeout = 0;
3616 if (priv->remove_attachment_banner) {
3617 gtk_widget_destroy (priv->remove_attachment_banner);
3618 g_object_unref (priv->remove_attachment_banner);
3619 priv->remove_attachment_banner = NULL;
3625 update_window_title (ModestMsgViewWindow *window)
3627 ModestMsgViewWindowPrivate *priv;
3629 TnyHeader *header = NULL;
3630 gchar *subject = NULL;
3632 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
3634 /* Note that if the window is closed while we're retrieving
3635 the message, this widget could de deleted */
3636 if (!priv->msg_view)
3639 msg = tny_msg_view_get_msg (TNY_MSG_VIEW (priv->msg_view));
3641 if (priv->other_body) {
3644 description = modest_tny_mime_part_get_header_value (priv->other_body, "Content-Description");
3646 g_strstrip (description);
3647 subject = description;
3649 } else if (msg != NULL) {
3650 header = tny_msg_get_header (msg);
3651 subject = tny_header_dup_subject (header);
3652 g_object_unref (header);
3653 g_object_unref (msg);
3656 if ((subject == NULL)||(subject[0] == '\0')) {
3658 subject = g_strdup (_("mail_va_no_subject"));
3661 gtk_window_set_title (GTK_WINDOW (window), subject);
3666 on_move_focus (GtkWidget *widget,
3667 GtkDirectionType direction,
3670 g_signal_stop_emission_by_name (G_OBJECT (widget), "move-focus");
3674 fetch_image_open_stream (TnyStreamCache *self, gint64 *expected_size, gchar *uri)
3676 GnomeVFSResult result;
3677 GnomeVFSHandle *handle = NULL;
3678 GnomeVFSFileInfo *info = NULL;
3681 result = gnome_vfs_open (&handle, uri, GNOME_VFS_OPEN_READ);
3682 if (result != GNOME_VFS_OK) {
3687 info = gnome_vfs_file_info_new ();
3688 result = gnome_vfs_get_file_info_from_handle (handle, info, GNOME_VFS_FILE_INFO_DEFAULT);
3689 if (result != GNOME_VFS_OK || ! (info->valid_fields & GNOME_VFS_FILE_INFO_FIELDS_SIZE)) {
3690 /* We put a "safe" default size for going to cache */
3691 *expected_size = (300*1024);
3693 *expected_size = info->size;
3695 gnome_vfs_file_info_unref (info);
3697 stream = tny_vfs_stream_new (handle);
3706 TnyStream *output_stream;
3707 GtkWidget *msg_view;
3712 on_fetch_image_timeout_refresh_view (gpointer userdata)
3714 ModestMsgViewWindowPrivate *priv;
3716 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (userdata);
3717 update_progress_hint (MODEST_MSG_VIEW_WINDOW (userdata));
3718 /* Note that priv->msg_view is set to NULL when this window is
3720 if (priv->msg_view && GTK_WIDGET_DRAWABLE (priv->msg_view)) {
3721 gtk_widget_queue_draw (GTK_WIDGET (priv->msg_view));
3723 priv->fetch_image_redraw_handler = 0;
3724 g_object_unref (userdata);
3729 on_fetch_image_idle_refresh_view (gpointer userdata)
3732 FetchImageData *fidata = (FetchImageData *) userdata;
3734 gdk_threads_enter ();
3735 if (GTK_WIDGET_DRAWABLE (fidata->msg_view)) {
3736 ModestMsgViewWindowPrivate *priv;
3738 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (fidata->window);
3739 priv->fetching_images--;
3740 if (priv->fetch_image_redraw_handler == 0) {
3741 priv->fetch_image_redraw_handler = g_timeout_add (500, on_fetch_image_timeout_refresh_view, g_object_ref (fidata->window));
3745 gdk_threads_leave ();
3747 g_object_unref (fidata->msg_view);
3748 g_object_unref (fidata->window);
3749 g_slice_free (FetchImageData, fidata);
3754 on_fetch_image_thread (gpointer userdata)
3756 FetchImageData *fidata = (FetchImageData *) userdata;
3757 TnyStreamCache *cache;
3758 TnyStream *cache_stream;
3760 cache = modest_runtime_get_images_cache ();
3762 tny_stream_cache_get_stream (cache,
3764 (TnyStreamCacheOpenStreamFetcher) fetch_image_open_stream,
3765 (gpointer) fidata->uri);
3766 g_free (fidata->cache_id);
3767 g_free (fidata->uri);
3769 if (cache_stream != NULL) {
3772 while (G_LIKELY (!tny_stream_is_eos (cache_stream))) {
3775 nb_read = tny_stream_read (cache_stream, buffer, sizeof (buffer));
3776 if (G_UNLIKELY (nb_read < 0)) {
3778 } else if (G_LIKELY (nb_read > 0)) {
3779 gssize nb_written = 0;
3781 while (G_UNLIKELY (nb_written < nb_read)) {
3784 len = tny_stream_write (fidata->output_stream, buffer + nb_written,
3785 nb_read - nb_written);
3786 if (G_UNLIKELY (len < 0))
3792 tny_stream_close (cache_stream);
3793 g_object_unref (cache_stream);
3796 tny_stream_close (fidata->output_stream);
3797 g_object_unref (fidata->output_stream);
3799 g_idle_add (on_fetch_image_idle_refresh_view, fidata);
3805 on_fetch_image (ModestMsgView *msgview,
3808 ModestMsgViewWindow *window)
3810 const gchar *current_account;
3811 ModestMsgViewWindowPrivate *priv;
3812 FetchImageData *fidata;
3814 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
3816 current_account = modest_window_get_active_account (MODEST_WINDOW (window));
3818 fidata = g_slice_new0 (FetchImageData);
3819 fidata->msg_view = g_object_ref (msgview);
3820 fidata->window = g_object_ref (window);
3821 fidata->uri = g_strdup (uri);
3822 fidata->cache_id = modest_images_cache_get_id (current_account, uri);
3823 fidata->output_stream = g_object_ref (stream);
3825 priv->fetching_images++;
3826 if (g_thread_create (on_fetch_image_thread, fidata, FALSE, NULL) == NULL) {
3827 g_object_unref (fidata->output_stream);
3828 g_free (fidata->cache_id);
3829 g_free (fidata->uri);
3830 g_object_unref (fidata->msg_view);
3831 g_slice_free (FetchImageData, fidata);
3832 tny_stream_close (stream);
3833 priv->fetching_images--;
3834 update_progress_hint (window);
3837 update_progress_hint (window);
3843 setup_menu (ModestMsgViewWindow *self)
3845 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW(self));
3847 /* Settings menu buttons */
3848 modest_hildon2_window_add_to_menu (MODEST_HILDON2_WINDOW (self), _("mcen_me_viewer_find"), NULL,
3849 APP_MENU_CALLBACK (modest_msg_view_window_show_find_toolbar),
3850 MODEST_DIMMING_CALLBACK (modest_ui_dimming_rules_on_find_in_msg));
3852 modest_hildon2_window_add_to_menu (MODEST_HILDON2_WINDOW (self),
3853 dngettext(GETTEXT_PACKAGE,
3854 "mcen_me_move_message",
3855 "mcen_me_move_messages",
3858 APP_MENU_CALLBACK (modest_ui_actions_on_move_to),
3859 MODEST_DIMMING_CALLBACK (modest_ui_dimming_rules_on_move_to));
3861 modest_hildon2_window_add_to_menu (MODEST_HILDON2_WINDOW (self), _("mcen_me_inbox_mark_as_read"), NULL,
3862 APP_MENU_CALLBACK (modest_ui_actions_on_mark_as_read),
3863 MODEST_DIMMING_CALLBACK (modest_ui_dimming_rules_on_mark_as_read_msg_in_view));
3865 modest_hildon2_window_add_to_menu (MODEST_HILDON2_WINDOW (self), _("mcen_me_inbox_mark_as_unread"), NULL,
3866 APP_MENU_CALLBACK (modest_ui_actions_on_mark_as_unread),
3867 MODEST_DIMMING_CALLBACK (modest_ui_dimming_rules_on_mark_as_unread_msg_in_view));
3869 modest_hildon2_window_add_to_menu (MODEST_HILDON2_WINDOW (self), _("mcen_me_viewer_save_attachments"), NULL,
3870 APP_MENU_CALLBACK (modest_ui_actions_save_attachments),
3871 MODEST_DIMMING_CALLBACK (modest_ui_dimming_rules_on_save_attachments));
3872 modest_hildon2_window_add_to_menu (MODEST_HILDON2_WINDOW (self), _("mcen_me_inbox_remove_attachments"), NULL,
3873 APP_MENU_CALLBACK (modest_ui_actions_remove_attachments),
3874 MODEST_DIMMING_CALLBACK (modest_ui_dimming_rules_on_remove_attachments));
3876 modest_hildon2_window_add_to_menu (MODEST_HILDON2_WINDOW (self), _("mcen_me_new_message"), "<Control>n",
3877 APP_MENU_CALLBACK (modest_ui_actions_on_new_msg),
3878 MODEST_DIMMING_CALLBACK (modest_ui_dimming_rules_on_new_msg));
3879 modest_hildon2_window_add_to_menu (MODEST_HILDON2_WINDOW (self), _("mcen_me_viewer_addtocontacts"), NULL,
3880 APP_MENU_CALLBACK (modest_ui_actions_add_to_contacts),
3881 MODEST_DIMMING_CALLBACK (modest_ui_dimming_rules_on_add_to_contacts));
3883 modest_hildon2_window_add_to_menu (MODEST_HILDON2_WINDOW (self), _("mcen_ti_message_properties"), NULL,
3884 APP_MENU_CALLBACK (modest_ui_actions_on_details),
3885 MODEST_DIMMING_CALLBACK (modest_ui_dimming_rules_on_details));
3889 modest_msg_view_window_add_to_contacts (ModestMsgViewWindow *self)
3891 ModestMsgViewWindowPrivate *priv;
3892 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
3893 GSList *recipients = NULL;
3896 msg = tny_msg_view_get_msg (TNY_MSG_VIEW (priv->msg_view));
3900 header = modest_msg_view_window_get_header (self);
3903 recipients = modest_tny_msg_header_get_all_recipients_list (header);
3904 g_object_unref (header);
3906 recipients = modest_tny_msg_get_all_recipients_list (msg);
3907 g_object_unref (msg);
3911 /* Offer the user to add recipients to the address book */
3912 modest_address_book_add_address_list_with_selector (recipients, (GtkWindow *) self);
3913 g_slist_foreach (recipients, (GFunc) g_free, NULL); g_slist_free (recipients);
3918 _modest_msg_view_window_map_event (GtkWidget *widget,
3922 ModestMsgViewWindow *self = (ModestMsgViewWindow *) userdata;
3924 update_progress_hint (self);
3930 modest_msg_view_window_fetch_images (ModestMsgViewWindow *self)
3932 ModestMsgViewWindowPrivate *priv;
3933 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
3935 modest_msg_view_request_fetch_images (MODEST_MSG_VIEW (priv->msg_view));
3939 modest_msg_view_window_has_blocked_external_images (ModestMsgViewWindow *self)
3941 ModestMsgViewWindowPrivate *priv;
3942 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
3944 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self), FALSE);
3946 return modest_msg_view_has_blocked_external_images (MODEST_MSG_VIEW (priv->msg_view));
3950 modest_msg_view_window_reload (ModestMsgViewWindow *self)
3952 ModestMsgViewWindowPrivate *priv;
3953 const gchar *msg_uid;
3954 TnyHeader *header = NULL;
3955 TnyFolder *folder = NULL;
3957 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self));
3959 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
3961 header = modest_msg_view_window_get_header (MODEST_MSG_VIEW_WINDOW (self));
3965 folder = tny_header_get_folder (header);
3966 g_object_unref (header);
3971 msg_uid = modest_msg_view_window_get_message_uid (self);
3973 GtkTreeRowReference *row_reference;
3975 if (priv->row_reference && gtk_tree_row_reference_valid (priv->row_reference)) {
3976 row_reference = priv->row_reference;
3978 row_reference = NULL;
3980 if (!message_reader (self, priv, NULL, msg_uid, folder, row_reference))
3981 g_warning ("Shouldn't happen, trying to reload a message failed");
3984 g_object_unref (folder);
3988 update_branding (ModestMsgViewWindow *self)
3990 const gchar *account;
3991 const gchar *mailbox;
3992 ModestAccountMgr *mgr;
3993 ModestProtocol *protocol = NULL;
3994 gchar *service_name = NULL;
3995 const GdkPixbuf *service_icon = NULL;
3996 ModestMsgViewWindowPrivate *priv;
3998 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
4000 account = modest_window_get_active_account (MODEST_WINDOW (self));
4001 mailbox = modest_window_get_active_mailbox (MODEST_WINDOW (self));
4003 mgr = modest_runtime_get_account_mgr ();
4005 if (modest_account_mgr_account_is_multimailbox (mgr, account, &protocol)) {
4006 if (MODEST_IS_ACCOUNT_PROTOCOL (protocol)) {
4007 service_name = modest_account_protocol_get_service_name (MODEST_ACCOUNT_PROTOCOL (protocol),
4009 service_icon = modest_account_protocol_get_service_icon (MODEST_ACCOUNT_PROTOCOL (protocol),
4010 account, mailbox, MODEST_ICON_SIZE_SMALL);
4014 modest_msg_view_set_branding (MODEST_MSG_VIEW (priv->msg_view), service_name, service_icon);
4015 g_free (service_name);
4019 sync_flags (ModestMsgViewWindow *self)
4021 TnyHeader *header = NULL;
4023 header = modest_msg_view_window_get_header (self);
4025 TnyMsg *msg = modest_msg_view_window_get_message (self);
4027 header = tny_msg_get_header (msg);
4028 g_object_unref (msg);
4033 TnyFolder *folder = tny_header_get_folder (header);
4036 ModestMailOperation *mail_op;
4038 /* Sync folder, we need this to save the seen flag */
4039 mail_op = modest_mail_operation_new (NULL);
4040 modest_mail_operation_queue_add (modest_runtime_get_mail_operation_queue (),
4042 modest_mail_operation_sync_folder (mail_op, folder, FALSE, NULL, NULL);
4043 g_object_unref (mail_op);
4044 g_object_unref (folder);
4046 g_object_unref (header);