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;
122 GtkWidget *remove_attachment_banner;
125 TnyMimePart *other_body;
130 static void modest_msg_view_window_class_init (ModestMsgViewWindowClass *klass);
131 static void modest_msg_view_window_init (ModestMsgViewWindow *obj);
132 static void modest_header_view_observer_init (ModestHeaderViewObserverIface *iface_class);
133 static void modest_msg_view_window_finalize (GObject *obj);
134 static void modest_msg_view_window_show_find_toolbar (GtkWidget *obj, gpointer data);
135 static void modest_msg_view_window_find_toolbar_close (GtkWidget *widget,
136 ModestMsgViewWindow *obj);
137 static void modest_msg_view_window_find_toolbar_search (GtkWidget *widget,
138 ModestMsgViewWindow *obj);
139 static void modest_msg_view_window_toggle_find_toolbar (GtkWidget *obj,
141 static void modest_msg_view_window_disconnect_signals (ModestWindow *self);
143 static gdouble modest_msg_view_window_get_zoom (ModestWindow *window);
144 static void modest_msg_view_window_set_zoom (ModestWindow *window,
146 static gboolean modest_msg_view_window_zoom_minus (ModestWindow *window);
147 static gboolean modest_msg_view_window_zoom_plus (ModestWindow *window);
148 static gboolean modest_msg_view_window_key_event (GtkWidget *window,
151 static void modest_msg_view_window_update_priority (ModestMsgViewWindow *window);
153 static void modest_msg_view_window_show_toolbar (ModestWindow *window,
154 gboolean show_toolbar);
156 static void modest_msg_view_window_clipboard_owner_change (GtkClipboard *clipboard,
158 ModestMsgViewWindow *window);
160 static void modest_msg_view_window_on_row_changed (GtkTreeModel *header_model,
163 ModestMsgViewWindow *window);
165 static void modest_msg_view_window_on_row_deleted (GtkTreeModel *header_model,
167 ModestMsgViewWindow *window);
169 static void modest_msg_view_window_on_row_inserted (GtkTreeModel *header_model,
170 GtkTreePath *tree_path,
171 GtkTreeIter *tree_iter,
172 ModestMsgViewWindow *window);
174 static void modest_msg_view_window_on_row_reordered (GtkTreeModel *header_model,
178 ModestMsgViewWindow *window);
180 static void modest_msg_view_window_update_model_replaced (ModestHeaderViewObserver *window,
182 const gchar *tny_folder_id);
184 static void on_queue_changed (ModestMailOperationQueue *queue,
185 ModestMailOperation *mail_op,
186 ModestMailOperationQueueNotification type,
187 ModestMsgViewWindow *self);
189 static void on_account_removed (TnyAccountStore *account_store,
193 static void on_move_focus (GtkWidget *widget,
194 GtkDirectionType direction,
197 static void view_msg_cb (ModestMailOperation *mail_op,
204 static void set_progress_hint (ModestMsgViewWindow *self,
207 static void update_window_title (ModestMsgViewWindow *window);
209 static void init_window (ModestMsgViewWindow *obj);
211 static gboolean msg_is_visible (TnyHeader *header, gboolean check_outbox);
213 static void check_dimming_rules_after_change (ModestMsgViewWindow *window);
215 static gboolean on_fetch_image (ModestMsgView *msgview,
218 ModestMsgViewWindow *window);
220 static gboolean modest_msg_view_window_scroll_child (ModestMsgViewWindow *self,
221 GtkScrollType scroll_type,
224 static gboolean message_reader (ModestMsgViewWindow *window,
225 ModestMsgViewWindowPrivate *priv,
227 const gchar *msg_uid,
229 GtkTreeRowReference *row_reference);
231 static void setup_menu (ModestMsgViewWindow *self);
232 static gboolean _modest_msg_view_window_map_event (GtkWidget *widget,
235 static void update_branding (ModestMsgViewWindow *self);
236 static void sync_flags (ModestMsgViewWindow *self);
238 /* list my signals */
245 static const GtkToggleActionEntry msg_view_toggle_action_entries [] = {
246 { "FindInMessage", MODEST_TOOLBAR_ICON_FIND, N_("qgn_toolb_gene_find"), "<CTRL>F", NULL, G_CALLBACK (modest_msg_view_window_toggle_find_toolbar), FALSE },
249 #define MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE((o), \
250 MODEST_TYPE_MSG_VIEW_WINDOW, \
251 ModestMsgViewWindowPrivate))
253 static GtkWindowClass *parent_class = NULL;
255 /* uncomment the following if you have defined any signals */
256 static guint signals[LAST_SIGNAL] = {0};
259 modest_msg_view_window_get_type (void)
261 static GType my_type = 0;
263 static const GTypeInfo my_info = {
264 sizeof(ModestMsgViewWindowClass),
265 NULL, /* base init */
266 NULL, /* base finalize */
267 (GClassInitFunc) modest_msg_view_window_class_init,
268 NULL, /* class finalize */
269 NULL, /* class data */
270 sizeof(ModestMsgViewWindow),
272 (GInstanceInitFunc) modest_msg_view_window_init,
275 my_type = g_type_register_static (MODEST_TYPE_HILDON2_WINDOW,
276 "ModestMsgViewWindow",
279 static const GInterfaceInfo modest_header_view_observer_info =
281 (GInterfaceInitFunc) modest_header_view_observer_init,
282 NULL, /* interface_finalize */
283 NULL /* interface_data */
286 g_type_add_interface_static (my_type,
287 MODEST_TYPE_HEADER_VIEW_OBSERVER,
288 &modest_header_view_observer_info);
294 save_state (ModestWindow *self)
296 modest_widget_memory_save (modest_runtime_get_conf (),
298 MODEST_CONF_MSG_VIEW_WINDOW_KEY);
302 modest_msg_view_window_scroll_child (ModestMsgViewWindow *self,
303 GtkScrollType scroll_type,
307 ModestMsgViewWindowPrivate *priv;
310 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(self);
312 switch (scroll_type) {
313 case GTK_SCROLL_STEP_UP:
316 case GTK_SCROLL_STEP_DOWN:
319 case GTK_SCROLL_PAGE_UP:
322 case GTK_SCROLL_PAGE_DOWN:
325 case GTK_SCROLL_START:
336 modest_maemo_utils_scroll_pannable((HildonPannableArea *) priv->main_scroll, 0, step);
338 return (gboolean) step;
342 add_scroll_binding (GtkBindingSet *binding_set,
344 GtkScrollType scroll)
346 guint keypad_keyval = keyval - GDK_Left + GDK_KP_Left;
348 gtk_binding_entry_add_signal (binding_set, keyval, 0,
350 GTK_TYPE_SCROLL_TYPE, scroll,
351 G_TYPE_BOOLEAN, FALSE);
352 gtk_binding_entry_add_signal (binding_set, keypad_keyval, 0,
354 GTK_TYPE_SCROLL_TYPE, scroll,
355 G_TYPE_BOOLEAN, FALSE);
359 modest_msg_view_window_class_init (ModestMsgViewWindowClass *klass)
361 GObjectClass *gobject_class;
362 HildonWindowClass *hildon_window_class;
363 ModestWindowClass *modest_window_class;
364 GtkBindingSet *binding_set;
366 gobject_class = (GObjectClass*) klass;
367 hildon_window_class = (HildonWindowClass *) klass;
368 modest_window_class = (ModestWindowClass *) klass;
370 parent_class = g_type_class_peek_parent (klass);
371 gobject_class->finalize = modest_msg_view_window_finalize;
373 modest_window_class->set_zoom_func = modest_msg_view_window_set_zoom;
374 modest_window_class->get_zoom_func = modest_msg_view_window_get_zoom;
375 modest_window_class->zoom_plus_func = modest_msg_view_window_zoom_plus;
376 modest_window_class->zoom_minus_func = modest_msg_view_window_zoom_minus;
377 modest_window_class->show_toolbar_func = modest_msg_view_window_show_toolbar;
378 modest_window_class->disconnect_signals_func = modest_msg_view_window_disconnect_signals;
380 modest_window_class->save_state_func = save_state;
382 klass->scroll_child = modest_msg_view_window_scroll_child;
384 signals[MSG_CHANGED_SIGNAL] =
385 g_signal_new ("msg-changed",
386 G_TYPE_FROM_CLASS (gobject_class),
388 G_STRUCT_OFFSET (ModestMsgViewWindowClass, msg_changed),
390 modest_marshal_VOID__POINTER_POINTER,
391 G_TYPE_NONE, 2, G_TYPE_POINTER, G_TYPE_POINTER);
393 signals[SCROLL_CHILD_SIGNAL] =
394 g_signal_new ("scroll-child",
395 G_TYPE_FROM_CLASS (gobject_class),
396 G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
397 G_STRUCT_OFFSET (ModestMsgViewWindowClass, scroll_child),
399 modest_marshal_BOOLEAN__ENUM_BOOLEAN,
400 G_TYPE_BOOLEAN, 2, GTK_TYPE_SCROLL_TYPE, G_TYPE_BOOLEAN);
402 binding_set = gtk_binding_set_by_class (klass);
403 add_scroll_binding (binding_set, GDK_Up, GTK_SCROLL_STEP_UP);
404 add_scroll_binding (binding_set, GDK_Down, GTK_SCROLL_STEP_DOWN);
405 add_scroll_binding (binding_set, GDK_Page_Up, GTK_SCROLL_PAGE_UP);
406 add_scroll_binding (binding_set, GDK_Page_Down, GTK_SCROLL_PAGE_DOWN);
407 add_scroll_binding (binding_set, GDK_Home, GTK_SCROLL_START);
408 add_scroll_binding (binding_set, GDK_End, GTK_SCROLL_END);
410 g_type_class_add_private (gobject_class, sizeof(ModestMsgViewWindowPrivate));
414 static void modest_header_view_observer_init(
415 ModestHeaderViewObserverIface *iface_class)
417 iface_class->update_func = modest_msg_view_window_update_model_replaced;
421 modest_msg_view_window_init (ModestMsgViewWindow *obj)
423 ModestMsgViewWindowPrivate *priv;
424 ModestWindowPrivate *parent_priv = NULL;
425 GtkActionGroup *action_group = NULL;
426 GError *error = NULL;
428 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(obj);
429 parent_priv = MODEST_WINDOW_GET_PRIVATE(obj);
430 parent_priv->ui_manager = gtk_ui_manager_new();
432 action_group = gtk_action_group_new ("ModestMsgViewWindowActions");
433 gtk_action_group_set_translation_domain (action_group, GETTEXT_PACKAGE);
435 /* Add common actions */
436 gtk_action_group_add_actions (action_group,
437 modest_action_entries,
438 G_N_ELEMENTS (modest_action_entries),
440 gtk_action_group_add_toggle_actions (action_group,
441 msg_view_toggle_action_entries,
442 G_N_ELEMENTS (msg_view_toggle_action_entries),
445 gtk_ui_manager_insert_action_group (parent_priv->ui_manager, action_group, 0);
446 g_object_unref (action_group);
448 /* Load the UI definition */
449 gtk_ui_manager_add_ui_from_file (parent_priv->ui_manager, MODEST_UIDIR "modest-msg-view-window-ui.xml",
452 g_printerr ("modest: could not merge modest-msg-view-window-ui.xml: %s\n", error->message);
453 g_error_free (error);
458 /* Add accelerators */
459 gtk_window_add_accel_group (GTK_WINDOW (obj),
460 gtk_ui_manager_get_accel_group (parent_priv->ui_manager));
462 priv->is_search_result = FALSE;
463 priv->is_outbox = FALSE;
465 priv->msg_view = NULL;
466 priv->header_model = NULL;
467 priv->header_folder_id = NULL;
468 priv->clipboard_change_handler = 0;
469 priv->queue_change_handler = 0;
470 priv->account_removed_handler = 0;
471 priv->row_changed_handler = 0;
472 priv->row_deleted_handler = 0;
473 priv->row_inserted_handler = 0;
474 priv->rows_reordered_handler = 0;
475 priv->progress_hint = FALSE;
476 priv->fetching_images = 0;
478 priv->optimized_view = FALSE;
479 priv->purge_timeout = 0;
480 priv->remove_attachment_banner = NULL;
481 priv->msg_uid = NULL;
482 priv->other_body = NULL;
484 priv->sighandlers = NULL;
487 init_window (MODEST_MSG_VIEW_WINDOW(obj));
489 hildon_program_add_window (hildon_program_get_instance(),
495 update_progress_hint (ModestMsgViewWindow *self)
497 ModestMsgViewWindowPrivate *priv;
498 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(self);
500 if (GTK_WIDGET_VISIBLE (self)) {
501 hildon_gtk_window_set_progress_indicator (GTK_WINDOW (self),
502 (priv->progress_hint || (priv->fetching_images > 0))?1:0);
507 set_progress_hint (ModestMsgViewWindow *self,
510 ModestWindowPrivate *parent_priv;
511 ModestMsgViewWindowPrivate *priv;
513 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self));
515 parent_priv = MODEST_WINDOW_GET_PRIVATE(self);
516 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(self);
518 /* Sets current progress hint */
519 priv->progress_hint = enabled;
521 update_progress_hint (self);
527 init_window (ModestMsgViewWindow *obj)
529 GtkWidget *main_vbox;
530 ModestMsgViewWindowPrivate *priv;
532 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(obj);
534 priv->msg_view = GTK_WIDGET (tny_platform_factory_new_msg_view (modest_tny_platform_factory_get_instance ()));
535 modest_msg_view_set_shadow_type (MODEST_MSG_VIEW (priv->msg_view), GTK_SHADOW_NONE);
536 main_vbox = gtk_vbox_new (FALSE, 6);
537 priv->main_scroll = hildon_pannable_area_new ();
538 g_object_set (G_OBJECT (priv->main_scroll),
539 "mov-mode", HILDON_MOVEMENT_MODE_BOTH,
542 gtk_container_add (GTK_CONTAINER (priv->main_scroll), priv->msg_view);
543 gtk_box_pack_start (GTK_BOX(main_vbox), priv->main_scroll, TRUE, TRUE, 0);
544 gtk_container_add (GTK_CONTAINER(obj), main_vbox);
546 /* NULL-ize fields if the window is destroyed */
547 g_signal_connect (priv->msg_view, "destroy", G_CALLBACK (gtk_widget_destroyed), &(priv->msg_view));
549 gtk_widget_show_all (GTK_WIDGET(main_vbox));
553 modest_msg_view_window_disconnect_signals (ModestWindow *self)
555 ModestMsgViewWindowPrivate *priv;
556 GtkWidget *header_view = NULL;
557 GtkWindow *parent_window = NULL;
559 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
561 if (gtk_clipboard_get (GDK_SELECTION_PRIMARY) &&
562 g_signal_handler_is_connected (gtk_clipboard_get (GDK_SELECTION_PRIMARY),
563 priv->clipboard_change_handler))
564 g_signal_handler_disconnect (gtk_clipboard_get (GDK_SELECTION_PRIMARY),
565 priv->clipboard_change_handler);
567 if (g_signal_handler_is_connected (G_OBJECT (modest_runtime_get_mail_operation_queue ()),
568 priv->queue_change_handler))
569 g_signal_handler_disconnect (G_OBJECT (modest_runtime_get_mail_operation_queue ()),
570 priv->queue_change_handler);
572 if (g_signal_handler_is_connected (G_OBJECT (modest_runtime_get_account_store ()),
573 priv->account_removed_handler))
574 g_signal_handler_disconnect (G_OBJECT (modest_runtime_get_account_store ()),
575 priv->account_removed_handler);
577 if (priv->header_model) {
578 if (g_signal_handler_is_connected(G_OBJECT (priv->header_model),
579 priv->row_changed_handler))
580 g_signal_handler_disconnect(G_OBJECT (priv->header_model),
581 priv->row_changed_handler);
583 if (g_signal_handler_is_connected(G_OBJECT (priv->header_model),
584 priv->row_deleted_handler))
585 g_signal_handler_disconnect(G_OBJECT (priv->header_model),
586 priv->row_deleted_handler);
588 if (g_signal_handler_is_connected(G_OBJECT (priv->header_model),
589 priv->row_inserted_handler))
590 g_signal_handler_disconnect(G_OBJECT (priv->header_model),
591 priv->row_inserted_handler);
593 if (g_signal_handler_is_connected(G_OBJECT (priv->header_model),
594 priv->rows_reordered_handler))
595 g_signal_handler_disconnect(G_OBJECT (priv->header_model),
596 priv->rows_reordered_handler);
599 modest_signal_mgr_disconnect_all_and_destroy (priv->sighandlers);
600 priv->sighandlers = NULL;
602 parent_window = gtk_window_get_transient_for (GTK_WINDOW (self));
603 if (parent_window && MODEST_IS_HEADER_WINDOW (parent_window)) {
604 header_view = GTK_WIDGET (modest_header_window_get_header_view (MODEST_HEADER_WINDOW (parent_window)));
606 modest_header_view_remove_observer(MODEST_HEADER_VIEW (header_view),
607 MODEST_HEADER_VIEW_OBSERVER(self));
613 modest_msg_view_window_finalize (GObject *obj)
615 ModestMsgViewWindowPrivate *priv;
617 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (obj);
619 /* Sanity check: shouldn't be needed, the window mgr should
620 call this function before */
621 modest_msg_view_window_disconnect_signals (MODEST_WINDOW (obj));
623 if (priv->other_body != NULL) {
624 g_object_unref (priv->other_body);
625 priv->other_body = NULL;
628 if (priv->header_model != NULL) {
629 g_object_unref (priv->header_model);
630 priv->header_model = NULL;
633 if (priv->remove_attachment_banner) {
634 gtk_widget_destroy (priv->remove_attachment_banner);
635 g_object_unref (priv->remove_attachment_banner);
636 priv->remove_attachment_banner = NULL;
639 if (priv->purge_timeout > 0) {
640 g_source_remove (priv->purge_timeout);
641 priv->purge_timeout = 0;
644 if (priv->row_reference) {
645 gtk_tree_row_reference_free (priv->row_reference);
646 priv->row_reference = NULL;
649 if (priv->next_row_reference) {
650 gtk_tree_row_reference_free (priv->next_row_reference);
651 priv->next_row_reference = NULL;
655 g_free (priv->msg_uid);
656 priv->msg_uid = NULL;
659 G_OBJECT_CLASS(parent_class)->finalize (obj);
663 select_next_valid_row (GtkTreeModel *model,
664 GtkTreeRowReference **row_reference,
668 GtkTreeIter tmp_iter;
670 GtkTreePath *next = NULL;
671 gboolean retval = FALSE, finished;
673 g_return_val_if_fail (gtk_tree_row_reference_valid (*row_reference), FALSE);
675 path = gtk_tree_row_reference_get_path (*row_reference);
676 gtk_tree_model_get_iter (model, &tmp_iter, path);
677 gtk_tree_row_reference_free (*row_reference);
678 *row_reference = NULL;
682 TnyHeader *header = NULL;
684 if (gtk_tree_model_iter_next (model, &tmp_iter)) {
685 gtk_tree_model_get (model, &tmp_iter,
686 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
690 if (msg_is_visible (header, is_outbox)) {
691 next = gtk_tree_model_get_path (model, &tmp_iter);
692 *row_reference = gtk_tree_row_reference_new (model, next);
693 gtk_tree_path_free (next);
697 g_object_unref (header);
700 } else if (cycle && gtk_tree_model_get_iter_first (model, &tmp_iter)) {
701 next = gtk_tree_model_get_path (model, &tmp_iter);
703 /* Ensure that we are not selecting the same */
704 if (gtk_tree_path_compare (path, next) != 0) {
705 gtk_tree_model_get (model, &tmp_iter,
706 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
709 if (msg_is_visible (header, is_outbox)) {
710 *row_reference = gtk_tree_row_reference_new (model, next);
714 g_object_unref (header);
718 /* If we ended up in the same message
719 then there is no valid next
723 gtk_tree_path_free (next);
725 /* If there are no more messages and we don't
726 want to start again in the first one then
727 there is no valid next message */
733 gtk_tree_path_free (path);
738 /* TODO: This should be in _init(), with the parameters as properties. */
740 modest_msg_view_window_construct (ModestMsgViewWindow *self,
741 const gchar *modest_account_name,
742 const gchar *mailbox,
743 const gchar *msg_uid)
746 ModestMsgViewWindowPrivate *priv = NULL;
747 ModestWindowPrivate *parent_priv = NULL;
748 ModestDimmingRulesGroup *toolbar_rules_group = NULL;
749 ModestDimmingRulesGroup *clipboard_rules_group = NULL;
751 obj = G_OBJECT (self);
752 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(obj);
753 parent_priv = MODEST_WINDOW_GET_PRIVATE(obj);
755 priv->msg_uid = g_strdup (msg_uid);
758 parent_priv->menubar = NULL;
760 toolbar_rules_group = modest_dimming_rules_group_new (MODEST_DIMMING_RULES_TOOLBAR, TRUE);
761 clipboard_rules_group = modest_dimming_rules_group_new (MODEST_DIMMING_RULES_CLIPBOARD, FALSE);
764 /* Add common dimming rules */
765 modest_dimming_rules_group_add_rules (toolbar_rules_group,
766 modest_msg_view_toolbar_dimming_entries,
767 G_N_ELEMENTS (modest_msg_view_toolbar_dimming_entries),
768 MODEST_WINDOW (self));
769 modest_dimming_rules_group_add_rules (clipboard_rules_group,
770 modest_msg_view_clipboard_dimming_entries,
771 G_N_ELEMENTS (modest_msg_view_clipboard_dimming_entries),
772 MODEST_WINDOW (self));
774 /* Insert dimming rules group for this window */
775 modest_ui_dimming_manager_insert_rules_group (parent_priv->ui_dimming_manager, toolbar_rules_group);
776 modest_ui_dimming_manager_insert_rules_group (parent_priv->ui_dimming_manager, clipboard_rules_group);
777 g_object_unref (toolbar_rules_group);
778 g_object_unref (clipboard_rules_group);
780 /* g_signal_connect (G_OBJECT(obj), "delete-event", G_CALLBACK(on_delete_event), obj); */
782 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);
783 g_signal_connect (G_OBJECT(priv->msg_view), "activate_link",
784 G_CALLBACK (modest_ui_actions_on_msg_link_clicked), obj);
785 g_signal_connect (G_OBJECT(priv->msg_view), "link_hover",
786 G_CALLBACK (modest_ui_actions_on_msg_link_hover), obj);
787 g_signal_connect (G_OBJECT(priv->msg_view), "attachment_clicked",
788 G_CALLBACK (modest_ui_actions_on_msg_attachment_clicked), obj);
789 g_signal_connect (G_OBJECT(priv->msg_view), "recpt_activated",
790 G_CALLBACK (modest_ui_actions_on_msg_recpt_activated), obj);
791 g_signal_connect (G_OBJECT(priv->msg_view), "show_details",
792 G_CALLBACK (modest_ui_actions_on_details), obj);
793 g_signal_connect (G_OBJECT(priv->msg_view), "link_contextual",
794 G_CALLBACK (modest_ui_actions_on_msg_link_contextual), obj);
795 g_signal_connect (G_OBJECT(priv->msg_view), "limit_error",
796 G_CALLBACK (modest_ui_actions_on_limit_error), obj);
797 g_signal_connect (G_OBJECT (priv->msg_view), "fetch_image",
798 G_CALLBACK (on_fetch_image), obj);
800 g_signal_connect (G_OBJECT (obj), "key-release-event",
801 G_CALLBACK (modest_msg_view_window_key_event),
804 g_signal_connect (G_OBJECT (obj), "key-press-event",
805 G_CALLBACK (modest_msg_view_window_key_event),
808 g_signal_connect (G_OBJECT (obj), "move-focus",
809 G_CALLBACK (on_move_focus), obj);
811 g_signal_connect (G_OBJECT (obj), "map-event",
812 G_CALLBACK (_modest_msg_view_window_map_event),
815 /* Mail Operation Queue */
816 priv->queue_change_handler = g_signal_connect (G_OBJECT (modest_runtime_get_mail_operation_queue ()),
818 G_CALLBACK (on_queue_changed),
821 /* Account manager */
822 priv->account_removed_handler = g_signal_connect (G_OBJECT (modest_runtime_get_account_store ()),
824 G_CALLBACK(on_account_removed),
827 modest_window_set_active_account (MODEST_WINDOW(obj), modest_account_name);
828 modest_window_set_active_mailbox (MODEST_WINDOW(obj), mailbox);
830 /* First add out toolbar ... */
831 modest_msg_view_window_show_toolbar (MODEST_WINDOW (obj), TRUE);
833 /* ... and later the find toolbar. This way find toolbar will
834 be shown over the other */
835 priv->find_toolbar = hildon_find_toolbar_new (NULL);
836 hildon_window_add_toolbar (HILDON_WINDOW (obj), GTK_TOOLBAR (priv->find_toolbar));
837 gtk_widget_set_no_show_all (priv->find_toolbar, TRUE);
838 g_signal_connect (G_OBJECT (priv->find_toolbar), "close",
839 G_CALLBACK (modest_msg_view_window_find_toolbar_close), obj);
840 g_signal_connect (G_OBJECT (priv->find_toolbar), "search",
841 G_CALLBACK (modest_msg_view_window_find_toolbar_search), obj);
842 priv->last_search = NULL;
844 /* Init the clipboard actions dim status */
845 modest_msg_view_grab_focus(MODEST_MSG_VIEW (priv->msg_view));
847 update_window_title (MODEST_MSG_VIEW_WINDOW (obj));
852 /* FIXME: parameter checks */
854 modest_msg_view_window_new_with_header_model (TnyMsg *msg,
855 const gchar *modest_account_name,
856 const gchar *mailbox,
857 const gchar *msg_uid,
859 GtkTreeRowReference *row_reference)
861 ModestMsgViewWindow *window = NULL;
862 ModestMsgViewWindowPrivate *priv = NULL;
863 TnyFolder *header_folder = NULL;
864 ModestHeaderView *header_view = NULL;
865 ModestWindowMgr *mgr = NULL;
868 modest_tny_mime_part_to_string (TNY_MIME_PART (msg), 0);
871 mgr = modest_runtime_get_window_mgr ();
872 window = MODEST_MSG_VIEW_WINDOW (modest_window_mgr_get_msg_view_window (mgr));
873 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), NULL);
875 modest_msg_view_window_construct (window, modest_account_name, mailbox, msg_uid);
877 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
879 /* Remember the message list's TreeModel so we can detect changes
880 * and change the list selection when necessary: */
881 header_folder = modest_header_view_get_folder (header_view);
883 priv->is_outbox = (modest_tny_folder_guess_folder_type (header_folder) ==
884 TNY_FOLDER_TYPE_OUTBOX);
885 priv->header_folder_id = tny_folder_get_id (header_folder);
886 g_object_unref(header_folder);
889 /* Setup row references and connect signals */
890 priv->header_model = g_object_ref (model);
892 if (row_reference && gtk_tree_row_reference_valid (row_reference)) {
893 priv->row_reference = gtk_tree_row_reference_copy (row_reference);
894 priv->next_row_reference = gtk_tree_row_reference_copy (row_reference);
895 select_next_valid_row (model, &(priv->next_row_reference), TRUE, priv->is_outbox);
897 priv->row_reference = NULL;
898 priv->next_row_reference = NULL;
901 /* Connect signals */
902 priv->row_changed_handler =
903 g_signal_connect (GTK_TREE_MODEL(model), "row-changed",
904 G_CALLBACK(modest_msg_view_window_on_row_changed),
906 priv->row_deleted_handler =
907 g_signal_connect (GTK_TREE_MODEL(model), "row-deleted",
908 G_CALLBACK(modest_msg_view_window_on_row_deleted),
910 priv->row_inserted_handler =
911 g_signal_connect (GTK_TREE_MODEL(model), "row-inserted",
912 G_CALLBACK(modest_msg_view_window_on_row_inserted),
914 priv->rows_reordered_handler =
915 g_signal_connect(GTK_TREE_MODEL(model), "rows-reordered",
916 G_CALLBACK(modest_msg_view_window_on_row_reordered),
919 if (header_view != NULL){
920 modest_header_view_add_observer(header_view,
921 MODEST_HEADER_VIEW_OBSERVER(window));
924 tny_msg_view_set_msg (TNY_MSG_VIEW (priv->msg_view), msg);
925 update_window_title (MODEST_MSG_VIEW_WINDOW (window));
926 update_branding (MODEST_MSG_VIEW_WINDOW (window));
928 /* gtk_widget_show_all (GTK_WIDGET (window)); */
929 modest_msg_view_window_update_priority (window);
930 /* Check dimming rules */
931 modest_ui_actions_check_toolbar_dimming_rules (MODEST_WINDOW (window));
932 modest_ui_actions_check_menu_dimming_rules (MODEST_WINDOW (window));
933 modest_window_check_dimming_rules_group (MODEST_WINDOW (window), MODEST_DIMMING_RULES_CLIPBOARD);
935 return MODEST_WINDOW(window);
939 modest_msg_view_window_new_from_uid (const gchar *modest_account_name,
940 const gchar *mailbox,
941 const gchar *msg_uid)
943 ModestMsgViewWindow *window = NULL;
944 ModestMsgViewWindowPrivate *priv = NULL;
945 ModestWindowMgr *mgr = NULL;
947 TnyAccount *account = NULL;
949 mgr = modest_runtime_get_window_mgr ();
950 window = MODEST_MSG_VIEW_WINDOW (modest_window_mgr_get_msg_view_window (mgr));
951 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), NULL);
953 modest_msg_view_window_construct (window, modest_account_name, mailbox, msg_uid);
955 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
957 is_merge = g_str_has_prefix (msg_uid, "merge:");
959 /* Get the account */
961 account = tny_account_store_find_account (TNY_ACCOUNT_STORE (modest_runtime_get_account_store ()),
964 if (is_merge || account) {
965 TnyFolder *folder = NULL;
967 /* Try to get the message, if it's already downloaded
968 we don't need to connect */
970 folder = modest_tny_folder_store_find_folder_from_uri (TNY_FOLDER_STORE (account), msg_uid);
972 ModestTnyAccountStore *account_store;
973 ModestTnyLocalFoldersAccount *local_folders_account;
975 account_store = modest_runtime_get_account_store ();
976 local_folders_account = MODEST_TNY_LOCAL_FOLDERS_ACCOUNT (
977 modest_tny_account_store_get_local_folders_account (account_store));
978 folder = modest_tny_local_folders_account_get_merged_outbox (local_folders_account);
979 g_object_unref (local_folders_account);
983 gboolean device_online;
985 device = modest_runtime_get_device();
986 device_online = tny_device_is_online (device);
988 message_reader (window, priv, NULL, msg_uid, folder, NULL);
990 TnyMsg *msg = tny_folder_find_msg (folder, msg_uid, NULL);
992 tny_msg_view_set_msg (TNY_MSG_VIEW (priv->msg_view), msg);
993 update_window_title (MODEST_MSG_VIEW_WINDOW (window));
994 update_branding (MODEST_MSG_VIEW_WINDOW (window));
995 g_object_unref (msg);
996 /* Sync flags to server */
997 sync_flags (MODEST_MSG_VIEW_WINDOW (window));
999 message_reader (window, priv, NULL, msg_uid, folder, NULL);
1002 g_object_unref (folder);
1007 /* Check dimming rules */
1008 modest_ui_actions_check_toolbar_dimming_rules (MODEST_WINDOW (window));
1009 modest_ui_actions_check_menu_dimming_rules (MODEST_WINDOW (window));
1010 modest_window_check_dimming_rules_group (MODEST_WINDOW (window), MODEST_DIMMING_RULES_CLIPBOARD);
1012 return MODEST_WINDOW(window);
1016 modest_msg_view_window_new_from_header_view (ModestHeaderView *header_view,
1017 const gchar *modest_account_name,
1018 const gchar *mailbox,
1019 const gchar *msg_uid,
1020 GtkTreeRowReference *row_reference)
1022 ModestMsgViewWindow *window = NULL;
1023 ModestMsgViewWindowPrivate *priv = NULL;
1024 TnyFolder *header_folder = NULL;
1025 ModestWindowMgr *mgr = NULL;
1029 mgr = modest_runtime_get_window_mgr ();
1030 window = MODEST_MSG_VIEW_WINDOW (modest_window_mgr_get_msg_view_window (mgr));
1031 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), NULL);
1033 modest_msg_view_window_construct (window, modest_account_name, mailbox, msg_uid);
1035 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1037 /* Remember the message list's TreeModel so we can detect changes
1038 * and change the list selection when necessary: */
1040 if (header_view != NULL){
1041 header_folder = modest_header_view_get_folder(header_view);
1042 /* This could happen if the header folder was
1043 unseleted before opening this msg window (for
1044 example if the user selects an account in the
1045 folder view of the main window */
1046 if (header_folder) {
1047 priv->is_outbox = (modest_tny_folder_guess_folder_type (header_folder) ==
1048 TNY_FOLDER_TYPE_OUTBOX);
1049 priv->header_folder_id = tny_folder_get_id(header_folder);
1050 g_object_unref(header_folder);
1054 /* Setup row references and connect signals */
1055 priv->header_model = gtk_tree_view_get_model (GTK_TREE_VIEW (header_view));
1056 g_object_ref (priv->header_model);
1058 if (row_reference && gtk_tree_row_reference_valid (row_reference)) {
1059 priv->row_reference = gtk_tree_row_reference_copy (row_reference);
1060 priv->next_row_reference = gtk_tree_row_reference_copy (row_reference);
1061 select_next_valid_row (priv->header_model, &(priv->next_row_reference), TRUE, priv->is_outbox);
1063 priv->row_reference = NULL;
1064 priv->next_row_reference = NULL;
1067 /* Connect signals */
1068 priv->row_changed_handler =
1069 g_signal_connect (GTK_TREE_MODEL(priv->header_model), "row-changed",
1070 G_CALLBACK(modest_msg_view_window_on_row_changed),
1072 priv->row_deleted_handler =
1073 g_signal_connect (GTK_TREE_MODEL(priv->header_model), "row-deleted",
1074 G_CALLBACK(modest_msg_view_window_on_row_deleted),
1076 priv->row_inserted_handler =
1077 g_signal_connect (GTK_TREE_MODEL(priv->header_model), "row-inserted",
1078 G_CALLBACK(modest_msg_view_window_on_row_inserted),
1080 priv->rows_reordered_handler =
1081 g_signal_connect(GTK_TREE_MODEL(priv->header_model), "rows-reordered",
1082 G_CALLBACK(modest_msg_view_window_on_row_reordered),
1085 if (header_view != NULL){
1086 modest_header_view_add_observer(header_view,
1087 MODEST_HEADER_VIEW_OBSERVER(window));
1090 tny_msg_view_set_msg (TNY_MSG_VIEW (priv->msg_view), NULL);
1091 update_branding (MODEST_MSG_VIEW_WINDOW (window));
1093 if (priv->row_reference) {
1094 path = gtk_tree_row_reference_get_path (priv->row_reference);
1095 if (gtk_tree_model_get_iter (priv->header_model, &iter, path)) {
1097 gtk_tree_model_get (priv->header_model, &iter,
1098 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
1100 message_reader (window, priv, header, NULL, NULL, priv->row_reference);
1101 g_object_unref (header);
1103 gtk_tree_path_free (path);
1105 /* Check dimming rules */
1106 modest_ui_actions_check_toolbar_dimming_rules (MODEST_WINDOW (window));
1107 modest_ui_actions_check_menu_dimming_rules (MODEST_WINDOW (window));
1108 modest_window_check_dimming_rules_group (MODEST_WINDOW (window), MODEST_DIMMING_RULES_CLIPBOARD);
1110 return MODEST_WINDOW(window);
1114 modest_msg_view_window_new_for_search_result (TnyMsg *msg,
1115 const gchar *modest_account_name,
1116 const gchar *mailbox,
1117 const gchar *msg_uid)
1119 ModestMsgViewWindow *window = NULL;
1120 ModestMsgViewWindowPrivate *priv = NULL;
1121 ModestWindowMgr *mgr = NULL;
1123 mgr = modest_runtime_get_window_mgr ();
1124 window = MODEST_MSG_VIEW_WINDOW (modest_window_mgr_get_msg_view_window (mgr));
1125 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), NULL);
1126 modest_msg_view_window_construct (window, modest_account_name, mailbox, msg_uid);
1128 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1130 /* Remember that this is a search result,
1131 * so we can disable some UI appropriately: */
1132 priv->is_search_result = TRUE;
1134 tny_msg_view_set_msg (TNY_MSG_VIEW (priv->msg_view), msg);
1135 update_branding (MODEST_MSG_VIEW_WINDOW (window));
1137 update_window_title (window);
1138 /* gtk_widget_show_all (GTK_WIDGET (window));*/
1139 modest_msg_view_window_update_priority (window);
1141 /* Check dimming rules */
1142 modest_ui_actions_check_toolbar_dimming_rules (MODEST_WINDOW (window));
1143 modest_ui_actions_check_menu_dimming_rules (MODEST_WINDOW (window));
1144 modest_window_check_dimming_rules_group (MODEST_WINDOW (window), MODEST_DIMMING_RULES_CLIPBOARD);
1146 return MODEST_WINDOW(window);
1150 modest_msg_view_window_is_other_body (ModestMsgViewWindow *self)
1152 ModestMsgViewWindowPrivate *priv = NULL;
1154 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self), FALSE);
1155 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
1157 return (priv->other_body != NULL);
1161 modest_msg_view_window_new_with_other_body (TnyMsg *msg,
1162 TnyMimePart *other_body,
1163 const gchar *modest_account_name,
1164 const gchar *mailbox,
1165 const gchar *msg_uid)
1167 GObject *obj = NULL;
1168 ModestMsgViewWindowPrivate *priv;
1169 ModestWindowMgr *mgr = NULL;
1171 g_return_val_if_fail (msg, NULL);
1172 mgr = modest_runtime_get_window_mgr ();
1173 obj = G_OBJECT (modest_window_mgr_get_msg_view_window (mgr));
1174 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (obj);
1175 modest_msg_view_window_construct (MODEST_MSG_VIEW_WINDOW (obj),
1176 modest_account_name, mailbox, msg_uid);
1179 priv->other_body = g_object_ref (other_body);
1180 modest_msg_view_set_msg_with_other_body (MODEST_MSG_VIEW (priv->msg_view), msg, other_body);
1182 tny_msg_view_set_msg (TNY_MSG_VIEW (priv->msg_view), msg);
1184 update_window_title (MODEST_MSG_VIEW_WINDOW (obj));
1185 update_branding (MODEST_MSG_VIEW_WINDOW (obj));
1187 /* gtk_widget_show_all (GTK_WIDGET (obj)); */
1189 /* Check dimming rules */
1190 modest_ui_actions_check_toolbar_dimming_rules (MODEST_WINDOW (obj));
1191 modest_ui_actions_check_menu_dimming_rules (MODEST_WINDOW (obj));
1192 modest_window_check_dimming_rules_group (MODEST_WINDOW (obj), MODEST_DIMMING_RULES_CLIPBOARD);
1194 return MODEST_WINDOW(obj);
1198 modest_msg_view_window_new_for_attachment (TnyMsg *msg,
1199 const gchar *modest_account_name,
1200 const gchar *mailbox,
1201 const gchar *msg_uid)
1203 return modest_msg_view_window_new_with_other_body (msg, NULL, modest_account_name, mailbox, msg_uid);
1207 modest_msg_view_window_on_row_changed (GtkTreeModel *header_model,
1210 ModestMsgViewWindow *window)
1212 check_dimming_rules_after_change (window);
1216 modest_msg_view_window_on_row_deleted(GtkTreeModel *header_model,
1218 ModestMsgViewWindow *window)
1220 check_dimming_rules_after_change (window);
1222 /* The window could have dissapeared */
1225 check_dimming_rules_after_change (ModestMsgViewWindow *window)
1227 modest_ui_actions_check_menu_dimming_rules (MODEST_WINDOW (window));
1228 modest_ui_actions_check_toolbar_dimming_rules (MODEST_WINDOW (window));
1232 /* On insertions we check if the folder still has the message we are
1233 * showing or do not. If do not, we do nothing. Which means we are still
1234 * not attached to any header folder and thus next/prev buttons are
1235 * still dimmed. Once the message that is shown by msg-view is found, the
1236 * new model of header-view will be attached and the references will be set.
1237 * On each further insertions dimming rules will be checked. However
1238 * this requires extra CPU time at least works.
1239 * (An message might be deleted from TnyFolder and thus will not be
1240 * inserted into the model again for example if it is removed by the
1241 * imap server and the header view is refreshed.)
1244 modest_msg_view_window_on_row_inserted (GtkTreeModel *model,
1245 GtkTreePath *tree_path,
1246 GtkTreeIter *tree_iter,
1247 ModestMsgViewWindow *window)
1249 ModestMsgViewWindowPrivate *priv = NULL;
1250 TnyHeader *header = NULL;
1252 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window));
1253 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1255 g_assert (model == priv->header_model);
1257 /* Check if the newly inserted message is the same we are actually
1258 * showing. IF not, we should remain detached from the header model
1259 * and thus prev and next toolbar buttons should remain dimmed. */
1260 gtk_tree_model_get (model, tree_iter,
1261 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
1264 if (TNY_IS_HEADER (header)) {
1267 uid = modest_tny_folder_get_header_unique_id (header);
1268 if (!g_str_equal(priv->msg_uid, uid)) {
1269 check_dimming_rules_after_change (window);
1271 g_object_unref (G_OBJECT(header));
1275 g_object_unref(G_OBJECT(header));
1278 if (priv->row_reference) {
1279 gtk_tree_row_reference_free (priv->row_reference);
1282 /* Setup row_reference for the actual msg. */
1283 priv->row_reference = gtk_tree_row_reference_new (priv->header_model, tree_path);
1284 if (priv->row_reference == NULL) {
1285 g_warning("%s: No reference for msg header item.", __FUNCTION__);
1289 /* Now set up next_row_reference. */
1290 if (priv->next_row_reference) {
1291 gtk_tree_row_reference_free (priv->next_row_reference);
1294 priv->next_row_reference = gtk_tree_row_reference_copy (priv->row_reference);
1295 select_next_valid_row (priv->header_model,
1296 &(priv->next_row_reference), FALSE, priv->is_outbox);
1298 /* Connect the remaining callbacks to become able to detect
1299 * changes in header-view. */
1300 priv->row_changed_handler =
1301 g_signal_connect (priv->header_model, "row-changed",
1302 G_CALLBACK (modest_msg_view_window_on_row_changed),
1304 priv->row_deleted_handler =
1305 g_signal_connect (priv->header_model, "row-deleted",
1306 G_CALLBACK (modest_msg_view_window_on_row_deleted),
1308 priv->rows_reordered_handler =
1309 g_signal_connect (priv->header_model, "rows-reordered",
1310 G_CALLBACK (modest_msg_view_window_on_row_reordered),
1313 check_dimming_rules_after_change (window);
1317 modest_msg_view_window_on_row_reordered (GtkTreeModel *header_model,
1321 ModestMsgViewWindow *window)
1323 ModestMsgViewWindowPrivate *priv = NULL;
1324 gboolean already_changed = FALSE;
1326 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(window);
1328 /* If the current row was reordered select the proper next
1329 valid row. The same if the next row reference changes */
1330 if (!priv->row_reference ||
1331 !gtk_tree_row_reference_valid (priv->row_reference))
1334 if (priv->next_row_reference &&
1335 gtk_tree_row_reference_valid (priv->next_row_reference)) {
1336 GtkTreePath *cur, *next;
1337 /* Check that the order is still the correct one */
1338 cur = gtk_tree_row_reference_get_path (priv->row_reference);
1339 next = gtk_tree_row_reference_get_path (priv->next_row_reference);
1340 gtk_tree_path_next (cur);
1341 if (gtk_tree_path_compare (cur, next) != 0) {
1342 gtk_tree_row_reference_free (priv->next_row_reference);
1343 priv->next_row_reference = gtk_tree_row_reference_copy (priv->row_reference);
1344 select_next_valid_row (header_model, &(priv->next_row_reference), FALSE, priv->is_outbox);
1345 already_changed = TRUE;
1347 gtk_tree_path_free (cur);
1348 gtk_tree_path_free (next);
1350 if (priv->next_row_reference)
1351 gtk_tree_row_reference_free (priv->next_row_reference);
1352 /* Update next row reference */
1353 priv->next_row_reference = gtk_tree_row_reference_copy (priv->row_reference);
1354 select_next_valid_row (header_model, &(priv->next_row_reference), FALSE, priv->is_outbox);
1355 already_changed = TRUE;
1358 check_dimming_rules_after_change (window);
1361 /* The modest_msg_view_window_update_model_replaced implements update
1362 * function for ModestHeaderViewObserver. Checks whether the TnyFolder
1363 * actually belongs to the header-view is the same as the TnyFolder of
1364 * the message of msg-view or not. If they are different, there is
1365 * nothing to do. If they are the same, then the model has replaced and
1366 * the reference in msg-view shall be replaced from the old model to
1367 * the new model. In this case the view will be detached from it's
1368 * header folder. From this point the next/prev buttons are dimmed.
1371 modest_msg_view_window_update_model_replaced (ModestHeaderViewObserver *observer,
1372 GtkTreeModel *model,
1373 const gchar *tny_folder_id)
1375 ModestMsgViewWindowPrivate *priv = NULL;
1376 ModestMsgViewWindow *window = NULL;
1378 g_assert(MODEST_IS_HEADER_VIEW_OBSERVER(observer));
1379 g_assert(MODEST_IS_MSG_VIEW_WINDOW(observer));
1381 window = MODEST_MSG_VIEW_WINDOW(observer);
1382 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(window);
1384 /* If there is an other folder in the header-view then we do
1385 * not care about it's model (msg list). Else if the
1386 * header-view shows the folder the msg shown by us is in, we
1387 * shall replace our model reference and make some check. */
1388 if(model == NULL || tny_folder_id == NULL ||
1389 (priv->header_folder_id && !g_str_equal(tny_folder_id, priv->header_folder_id)))
1392 /* Model is changed(replaced), so we should forget the old
1393 * one. Because there might be other references and there
1394 * might be some change on the model even if we unreferenced
1395 * it, we need to disconnect our signals here. */
1396 if (priv->header_model) {
1397 if (g_signal_handler_is_connected(G_OBJECT (priv->header_model),
1398 priv->row_changed_handler))
1399 g_signal_handler_disconnect(G_OBJECT (priv->header_model),
1400 priv->row_changed_handler);
1401 if (g_signal_handler_is_connected(G_OBJECT (priv->header_model),
1402 priv->row_deleted_handler))
1403 g_signal_handler_disconnect(G_OBJECT (priv->header_model),
1404 priv->row_deleted_handler);
1405 if (g_signal_handler_is_connected(G_OBJECT (priv->header_model),
1406 priv->row_inserted_handler))
1407 g_signal_handler_disconnect(G_OBJECT (priv->header_model),
1408 priv->row_inserted_handler);
1409 if (g_signal_handler_is_connected(G_OBJECT (priv->header_model),
1410 priv->rows_reordered_handler))
1411 g_signal_handler_disconnect(G_OBJECT (priv->header_model),
1412 priv->rows_reordered_handler);
1415 if (priv->row_reference)
1416 gtk_tree_row_reference_free (priv->row_reference);
1417 if (priv->next_row_reference)
1418 gtk_tree_row_reference_free (priv->next_row_reference);
1419 g_object_unref(priv->header_model);
1422 priv->row_changed_handler = 0;
1423 priv->row_deleted_handler = 0;
1424 priv->row_inserted_handler = 0;
1425 priv->rows_reordered_handler = 0;
1426 priv->next_row_reference = NULL;
1427 priv->row_reference = NULL;
1428 priv->header_model = NULL;
1431 priv->header_model = g_object_ref (model);
1433 /* Also we must connect to the new model for row insertions.
1434 * Only for insertions now. We will need other ones only after
1435 * the msg is show by msg-view is added to the new model. */
1436 priv->row_inserted_handler =
1437 g_signal_connect (priv->header_model, "row-inserted",
1438 G_CALLBACK(modest_msg_view_window_on_row_inserted),
1441 modest_ui_actions_check_menu_dimming_rules(MODEST_WINDOW(window));
1442 modest_ui_actions_check_toolbar_dimming_rules(MODEST_WINDOW(window));
1446 modest_msg_view_window_toolbar_on_transfer_mode (ModestMsgViewWindow *self)
1448 ModestMsgViewWindowPrivate *priv= NULL;
1450 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self), FALSE);
1451 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
1453 return priv->progress_hint;
1457 modest_msg_view_window_get_header (ModestMsgViewWindow *self)
1459 ModestMsgViewWindowPrivate *priv= NULL;
1461 TnyHeader *header = NULL;
1462 GtkTreePath *path = NULL;
1465 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self), NULL);
1466 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
1468 /* If the message was not obtained from a treemodel,
1469 * for instance if it was opened directly by the search UI:
1471 if (priv->header_model == NULL ||
1472 priv->row_reference == NULL ||
1473 !gtk_tree_row_reference_valid (priv->row_reference)) {
1474 msg = modest_msg_view_window_get_message (self);
1476 header = tny_msg_get_header (msg);
1477 g_object_unref (msg);
1482 /* Get iter of the currently selected message in the header view: */
1483 path = gtk_tree_row_reference_get_path (priv->row_reference);
1484 g_return_val_if_fail (path != NULL, NULL);
1485 gtk_tree_model_get_iter (priv->header_model,
1489 /* Get current message header */
1490 gtk_tree_model_get (priv->header_model, &iter,
1491 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
1494 gtk_tree_path_free (path);
1499 modest_msg_view_window_get_message (ModestMsgViewWindow *self)
1501 ModestMsgViewWindowPrivate *priv;
1503 g_return_val_if_fail (self, NULL);
1505 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(self);
1507 return tny_msg_view_get_msg (TNY_MSG_VIEW (priv->msg_view));
1511 modest_msg_view_window_get_message_uid (ModestMsgViewWindow *self)
1513 ModestMsgViewWindowPrivate *priv;
1515 g_return_val_if_fail (self, NULL);
1517 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
1519 return (const gchar*) priv->msg_uid;
1522 /* Used for the Ctrl+F accelerator */
1524 modest_msg_view_window_toggle_find_toolbar (GtkWidget *obj,
1527 ModestMsgViewWindow *window = MODEST_MSG_VIEW_WINDOW (data);
1528 ModestMsgViewWindowPrivate *priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1530 if (GTK_WIDGET_VISIBLE (priv->find_toolbar)) {
1531 modest_msg_view_window_find_toolbar_close (obj, data);
1533 modest_msg_view_window_show_find_toolbar (obj, data);
1537 /* Handler for menu option */
1539 modest_msg_view_window_show_find_toolbar (GtkWidget *obj,
1542 ModestMsgViewWindow *window = MODEST_MSG_VIEW_WINDOW (data);
1543 ModestMsgViewWindowPrivate *priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1545 gtk_widget_show (priv->find_toolbar);
1546 hildon_find_toolbar_highlight_entry (HILDON_FIND_TOOLBAR (priv->find_toolbar), TRUE);
1549 /* Handler for click on the "X" close button in find toolbar */
1551 modest_msg_view_window_find_toolbar_close (GtkWidget *widget,
1552 ModestMsgViewWindow *obj)
1554 ModestMsgViewWindowPrivate *priv;
1556 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (obj);
1559 gtk_widget_hide (priv->find_toolbar);
1560 modest_msg_view_grab_focus (MODEST_MSG_VIEW (priv->msg_view));
1564 modest_msg_view_window_find_toolbar_search (GtkWidget *widget,
1565 ModestMsgViewWindow *obj)
1567 gchar *current_search;
1568 ModestMsgViewWindowPrivate *priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (obj);
1570 if (modest_mime_part_view_is_empty (MODEST_MIME_PART_VIEW (priv->msg_view))) {
1571 hildon_banner_show_information (NULL, NULL, _("mail_ib_nothing_to_find"));
1575 g_object_get (G_OBJECT (widget), "prefix", ¤t_search, NULL);
1577 if ((current_search == NULL) || (strcmp (current_search, "") == 0)) {
1578 g_free (current_search);
1579 hildon_banner_show_information (NULL, NULL, _CS("ecdg_ib_find_rep_enter_text"));
1583 if ((priv->last_search == NULL) || (strcmp (priv->last_search, current_search) != 0)) {
1585 g_free (priv->last_search);
1586 priv->last_search = g_strdup (current_search);
1587 result = modest_isearch_view_search (MODEST_ISEARCH_VIEW (priv->msg_view),
1590 hildon_banner_show_information (NULL, NULL,
1591 _HL("ckct_ib_find_no_matches"));
1592 g_free (priv->last_search);
1593 priv->last_search = NULL;
1595 hildon_find_toolbar_highlight_entry (HILDON_FIND_TOOLBAR (priv->find_toolbar), TRUE);
1598 if (!modest_isearch_view_search_next (MODEST_ISEARCH_VIEW (priv->msg_view))) {
1599 hildon_banner_show_information (NULL, NULL,
1600 _HL("ckct_ib_find_search_complete"));
1601 g_free (priv->last_search);
1602 priv->last_search = NULL;
1604 hildon_find_toolbar_highlight_entry (HILDON_FIND_TOOLBAR (priv->find_toolbar), TRUE);
1608 g_free (current_search);
1613 modest_msg_view_window_set_zoom (ModestWindow *window,
1616 ModestMsgViewWindowPrivate *priv;
1617 ModestWindowPrivate *parent_priv;
1619 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window));
1621 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1622 parent_priv = MODEST_WINDOW_GET_PRIVATE (window);
1623 modest_zoomable_set_zoom (MODEST_ZOOMABLE (priv->msg_view), zoom);
1628 modest_msg_view_window_get_zoom (ModestWindow *window)
1630 ModestMsgViewWindowPrivate *priv;
1632 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), 1.0);
1634 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1635 return modest_zoomable_get_zoom (MODEST_ZOOMABLE (priv->msg_view));
1639 modest_msg_view_window_zoom_plus (ModestWindow *window)
1642 ModestMsgViewWindowPrivate *priv;
1646 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), 1.0);
1647 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1649 zoom_level = modest_zoomable_get_zoom (MODEST_ZOOMABLE (priv->msg_view));
1651 if (zoom_level >= 2.0) {
1652 hildon_banner_show_information (NULL, NULL,
1653 _CS("ckct_ib_max_zoom_level_reached"));
1655 } else if (zoom_level >= 1.5) {
1657 } else if (zoom_level >= 1.2) {
1659 } else if (zoom_level >= 1.0) {
1661 } else if (zoom_level >= 0.8) {
1663 } else if (zoom_level >= 0.5) {
1669 /* set zoom level */
1670 int_zoom = (gint) rint (zoom_level*100.0+0.1);
1671 banner_text = g_strdup_printf (_HL("wdgt_ib_zoom"), int_zoom);
1672 modest_platform_information_banner (GTK_WIDGET (window), NULL, banner_text);
1673 g_free (banner_text);
1674 modest_zoomable_set_zoom (MODEST_ZOOMABLE (priv->msg_view), zoom_level);
1680 modest_msg_view_window_zoom_minus (ModestWindow *window)
1683 ModestMsgViewWindowPrivate *priv;
1687 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), 1.0);
1688 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1690 zoom_level = modest_zoomable_get_zoom (MODEST_ZOOMABLE (priv->msg_view));
1692 if (zoom_level <= 0.5) {
1693 hildon_banner_show_information (NULL, NULL,
1694 _CS("ckct_ib_min_zoom_level_reached"));
1696 } else if (zoom_level <= 0.8) {
1698 } else if (zoom_level <= 1.0) {
1700 } else if (zoom_level <= 1.2) {
1702 } else if (zoom_level <= 1.5) {
1704 } else if (zoom_level <= 2.0) {
1710 /* set zoom level */
1711 int_zoom = (gint) rint (zoom_level*100.0+0.1);
1712 banner_text = g_strdup_printf (_HL("wdgt_ib_zoom"), int_zoom);
1713 modest_platform_information_banner (GTK_WIDGET (window), NULL, banner_text);
1714 g_free (banner_text);
1715 modest_zoomable_set_zoom (MODEST_ZOOMABLE (priv->msg_view), zoom_level);
1721 modest_msg_view_window_key_event (GtkWidget *window,
1727 focus = gtk_window_get_focus (GTK_WINDOW (window));
1729 /* for the find toolbar case */
1730 if (focus && GTK_IS_ENTRY (focus)) {
1731 if (event->keyval == GDK_BackSpace) {
1733 copy = gdk_event_copy ((GdkEvent *) event);
1734 gtk_widget_event (focus, copy);
1735 gdk_event_free (copy);
1745 modest_msg_view_window_last_message_selected (ModestMsgViewWindow *window)
1748 ModestMsgViewWindowPrivate *priv;
1749 GtkTreeIter tmp_iter;
1750 gboolean is_last_selected;
1752 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), TRUE);
1753 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1755 /*if no model (so no rows at all), then virtually we are the last*/
1756 if (!priv->header_model || !priv->row_reference)
1759 if (!gtk_tree_row_reference_valid (priv->row_reference))
1762 path = gtk_tree_row_reference_get_path (priv->row_reference);
1766 is_last_selected = TRUE;
1767 while (is_last_selected) {
1769 gtk_tree_path_next (path);
1770 if (!gtk_tree_model_get_iter (priv->header_model, &tmp_iter, path))
1772 gtk_tree_model_get (priv->header_model, &tmp_iter,
1773 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
1776 if (msg_is_visible (header, priv->is_outbox))
1777 is_last_selected = FALSE;
1778 g_object_unref(G_OBJECT(header));
1781 gtk_tree_path_free (path);
1782 return is_last_selected;
1786 modest_msg_view_window_has_headers_model (ModestMsgViewWindow *window)
1788 ModestMsgViewWindowPrivate *priv;
1790 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), TRUE);
1791 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1793 return priv->header_model != NULL;
1797 modest_msg_view_window_is_search_result (ModestMsgViewWindow *window)
1799 ModestMsgViewWindowPrivate *priv;
1801 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), TRUE);
1802 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1804 return priv->is_search_result;
1808 msg_is_visible (TnyHeader *header, gboolean check_outbox)
1810 if ((tny_header_get_flags(header) & TNY_HEADER_FLAG_DELETED))
1812 if (!check_outbox) {
1815 ModestTnySendQueueStatus status;
1816 status = modest_tny_all_send_queues_get_msg_status (header);
1817 return ((status != MODEST_TNY_SEND_QUEUE_FAILED) &&
1818 (status != MODEST_TNY_SEND_QUEUE_SENDING));
1823 modest_msg_view_window_first_message_selected (ModestMsgViewWindow *window)
1826 ModestMsgViewWindowPrivate *priv;
1827 gboolean is_first_selected;
1828 GtkTreeIter tmp_iter;
1830 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), TRUE);
1831 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1833 /*if no model (so no rows at all), then virtually we are the first*/
1834 if (!priv->header_model || !priv->row_reference)
1837 if (!gtk_tree_row_reference_valid (priv->row_reference))
1840 path = gtk_tree_row_reference_get_path (priv->row_reference);
1844 is_first_selected = TRUE;
1845 while (is_first_selected) {
1847 if(!gtk_tree_path_prev (path))
1849 /* Here the 'if' is needless for logic, but let make sure
1850 * iter is valid for gtk_tree_model_get. */
1851 if (!gtk_tree_model_get_iter (priv->header_model, &tmp_iter, path))
1853 gtk_tree_model_get (priv->header_model, &tmp_iter,
1854 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
1857 if (msg_is_visible (header, priv->is_outbox))
1858 is_first_selected = FALSE;
1859 g_object_unref(G_OBJECT(header));
1862 gtk_tree_path_free (path);
1863 return is_first_selected;
1870 GtkTreeRowReference *row_reference;
1874 message_reader_performer (gboolean canceled,
1876 GtkWindow *parent_window,
1877 TnyAccount *account,
1880 ModestMailOperation *mail_op = NULL;
1881 MsgReaderInfo *info;
1883 info = (MsgReaderInfo *) user_data;
1884 if (canceled || err) {
1885 update_window_title (MODEST_MSG_VIEW_WINDOW (parent_window));
1886 modest_ui_actions_on_close_window (NULL, MODEST_WINDOW (parent_window));
1890 /* Register the header - it'll be unregistered in the callback */
1892 modest_window_mgr_register_header (modest_runtime_get_window_mgr (), info->header, NULL);
1894 /* New mail operation */
1895 mail_op = modest_mail_operation_new_with_error_handling (G_OBJECT(parent_window),
1896 modest_ui_actions_disk_operations_error_handler,
1899 modest_mail_operation_queue_add (modest_runtime_get_mail_operation_queue (), mail_op);
1901 modest_mail_operation_get_msg (mail_op, info->header, TRUE, view_msg_cb, info->row_reference);
1903 modest_mail_operation_find_msg (mail_op, info->folder, info->msg_uid, TRUE, view_msg_cb, NULL);
1904 g_object_unref (mail_op);
1906 /* Update dimming rules */
1907 modest_ui_actions_check_toolbar_dimming_rules (MODEST_WINDOW (parent_window));
1908 modest_ui_actions_check_menu_dimming_rules (MODEST_WINDOW (parent_window));
1911 /* Frees. The row_reference will be freed by the view_msg_cb callback */
1912 g_free (info->msg_uid);
1914 g_object_unref (info->folder);
1916 g_object_unref (info->header);
1917 g_slice_free (MsgReaderInfo, info);
1922 * Reads the message whose summary item is @header. It takes care of
1923 * several things, among others:
1925 * If the message was not previously downloaded then ask the user
1926 * before downloading. If there is no connection launch the connection
1927 * dialog. Update toolbar dimming rules.
1929 * Returns: TRUE if the mail operation was started, otherwise if the
1930 * user do not want to download the message, or if the user do not
1931 * want to connect, then the operation is not issued
1934 message_reader (ModestMsgViewWindow *window,
1935 ModestMsgViewWindowPrivate *priv,
1937 const gchar *msg_uid,
1939 GtkTreeRowReference *row_reference)
1941 ModestWindowMgr *mgr;
1942 TnyAccount *account = NULL;
1943 MsgReaderInfo *info;
1945 /* We set the header from model while we're loading */
1946 tny_header_view_set_header (TNY_HEADER_VIEW (priv->msg_view), header);
1947 gtk_window_set_title (GTK_WINDOW (window), _CS("ckdg_pb_updating"));
1953 g_object_ref (folder);
1955 mgr = modest_runtime_get_window_mgr ();
1956 /* Msg download completed */
1957 if (!header || !(tny_header_get_flags (header) & TNY_HEADER_FLAG_CACHED)) {
1959 /* Ask the user if he wants to download the message if
1961 if (!tny_device_is_online (modest_runtime_get_device())) {
1962 GtkResponseType response;
1964 response = modest_platform_run_confirmation_dialog (GTK_WINDOW (window),
1965 _("mcen_nc_get_msg"));
1966 if (response == GTK_RESPONSE_CANCEL) {
1967 update_window_title (window);
1972 folder = tny_header_get_folder (header);
1974 info = g_slice_new (MsgReaderInfo);
1975 info->msg_uid = g_strdup (msg_uid);
1977 info->header = g_object_ref (header);
1979 info->header = NULL;
1981 info->folder = g_object_ref (folder);
1983 info->folder = NULL;
1984 if (row_reference) {
1985 info->row_reference = gtk_tree_row_reference_copy (row_reference);
1987 info->row_reference = NULL;
1990 /* Offer the connection dialog if necessary */
1991 modest_platform_connect_if_remote_and_perform ((GtkWindow *) window,
1993 TNY_FOLDER_STORE (folder),
1994 message_reader_performer,
1997 g_object_unref (folder);
2003 folder = tny_header_get_folder (header);
2006 account = tny_folder_get_account (folder);
2008 info = g_slice_new (MsgReaderInfo);
2009 info->msg_uid = g_strdup (msg_uid);
2011 info->folder = g_object_ref (folder);
2013 info->folder = NULL;
2015 info->header = g_object_ref (header);
2017 info->header = NULL;
2019 info->row_reference = gtk_tree_row_reference_copy (row_reference);
2021 info->row_reference = NULL;
2023 message_reader_performer (FALSE, NULL, (GtkWindow *) window, account, info);
2025 g_object_unref (account);
2027 g_object_unref (folder);
2033 modest_msg_view_window_select_next_message (ModestMsgViewWindow *window)
2035 ModestMsgViewWindowPrivate *priv;
2036 GtkTreePath *path= NULL;
2037 GtkTreeIter tmp_iter;
2039 gboolean retval = TRUE;
2040 GtkTreeRowReference *row_reference = NULL;
2042 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), FALSE);
2043 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
2045 if (!priv->row_reference)
2048 /* Update the next row reference if it's not valid. This could
2049 happen if for example the header which it was pointing to,
2050 was deleted. The best place to do it is in the row-deleted
2051 handler but the tinymail model do not work like the glib
2052 tree models and reports the deletion when the row is still
2054 if (!gtk_tree_row_reference_valid (priv->next_row_reference)) {
2055 if (priv->next_row_reference) {
2056 gtk_tree_row_reference_free (priv->next_row_reference);
2058 if (gtk_tree_row_reference_valid (priv->row_reference)) {
2059 priv->next_row_reference = gtk_tree_row_reference_copy (priv->row_reference);
2060 select_next_valid_row (priv->header_model, &(priv->next_row_reference), FALSE, priv->is_outbox);
2062 priv->next_row_reference = NULL;
2065 if (priv->next_row_reference)
2066 path = gtk_tree_row_reference_get_path (priv->next_row_reference);
2070 row_reference = gtk_tree_row_reference_copy (priv->next_row_reference);
2072 gtk_tree_model_get_iter (priv->header_model,
2075 gtk_tree_path_free (path);
2077 gtk_tree_model_get (priv->header_model, &tmp_iter,
2078 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
2081 /* Read the message & show it */
2082 if (!message_reader (window, priv, header, NULL, NULL, row_reference)) {
2085 gtk_tree_row_reference_free (row_reference);
2088 g_object_unref (header);
2094 modest_msg_view_window_select_previous_message (ModestMsgViewWindow *window)
2096 ModestMsgViewWindowPrivate *priv = NULL;
2098 gboolean finished = FALSE;
2099 gboolean retval = FALSE;
2101 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), FALSE);
2102 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
2104 if (priv->row_reference && !gtk_tree_row_reference_valid (priv->row_reference)) {
2105 gtk_tree_row_reference_free (priv->row_reference);
2106 priv->row_reference = NULL;
2109 /* Return inmediatly if there is no header model */
2110 if (!priv->header_model || !priv->row_reference)
2113 path = gtk_tree_row_reference_get_path (priv->row_reference);
2114 while (!finished && gtk_tree_path_prev (path)) {
2118 gtk_tree_model_get_iter (priv->header_model, &iter, path);
2119 gtk_tree_model_get (priv->header_model, &iter,
2120 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
2124 if (msg_is_visible (header, priv->is_outbox)) {
2125 GtkTreeRowReference *row_reference;
2126 row_reference = gtk_tree_row_reference_new (priv->header_model, path);
2127 /* Read the message & show it */
2128 retval = message_reader (window, priv, header, NULL, NULL, row_reference);
2129 gtk_tree_row_reference_free (row_reference);
2133 g_object_unref (header);
2137 gtk_tree_path_free (path);
2142 view_msg_cb (ModestMailOperation *mail_op,
2149 ModestMsgViewWindow *self = NULL;
2150 ModestMsgViewWindowPrivate *priv = NULL;
2151 GtkTreeRowReference *row_reference = NULL;
2153 /* Unregister the header (it was registered before creating the mail operation) */
2154 modest_window_mgr_unregister_header (modest_runtime_get_window_mgr (), header);
2156 row_reference = (GtkTreeRowReference *) user_data;
2159 gtk_tree_row_reference_free (row_reference);
2160 self = (ModestMsgViewWindow *) modest_mail_operation_get_source (mail_op);
2162 /* Restore window title */
2163 update_window_title (self);
2164 modest_ui_actions_on_close_window (NULL, MODEST_WINDOW (self));
2165 g_object_unref (self);
2170 /* If there was any error */
2171 if (!modest_ui_actions_msg_retrieval_check (mail_op, header, msg)) {
2173 gtk_tree_row_reference_free (row_reference);
2174 self = (ModestMsgViewWindow *) modest_mail_operation_get_source (mail_op);
2176 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
2177 /* First we check if the parent is a folder window */
2178 if (priv->msg_uid && !modest_hildon2_window_mgr_get_folder_window (MODEST_HILDON2_WINDOW_MGR (modest_runtime_get_window_mgr ()))) {
2180 TnyAccount *account = NULL;
2181 GtkWidget *header_window = NULL;
2183 is_merge = g_str_has_prefix (priv->msg_uid, "merge:");
2185 /* Get the account */
2187 account = tny_account_store_find_account (TNY_ACCOUNT_STORE (modest_runtime_get_account_store ()),
2190 if (is_merge || account) {
2191 TnyFolder *folder = NULL;
2193 /* Try to get the message, if it's already downloaded
2194 we don't need to connect */
2196 folder = modest_tny_folder_store_find_folder_from_uri (TNY_FOLDER_STORE (account),
2199 ModestTnyAccountStore *account_store;
2200 ModestTnyLocalFoldersAccount *local_folders_account;
2202 account_store = modest_runtime_get_account_store ();
2203 local_folders_account = MODEST_TNY_LOCAL_FOLDERS_ACCOUNT (
2204 modest_tny_account_store_get_local_folders_account (account_store));
2205 folder = modest_tny_local_folders_account_get_merged_outbox (local_folders_account);
2206 g_object_unref (local_folders_account);
2208 if (account) g_object_unref (account);
2211 header_window = (GtkWidget *)
2212 modest_header_window_new (
2214 modest_window_get_active_account (MODEST_WINDOW (self)),
2215 modest_window_get_active_mailbox (MODEST_WINDOW (self)));
2216 if (!modest_window_mgr_register_window (modest_runtime_get_window_mgr (),
2217 MODEST_WINDOW (header_window),
2219 gtk_widget_destroy (GTK_WIDGET (header_window));
2221 gtk_widget_show_all (GTK_WIDGET (header_window));
2223 g_object_unref (folder);
2229 /* Restore window title */
2230 update_window_title (self);
2231 modest_ui_actions_on_close_window (NULL, MODEST_WINDOW (self));
2232 g_object_unref (self);
2237 if (msg && TNY_IS_CAMEL_BS_MSG (msg)) {
2239 body = modest_tny_msg_find_body_part (msg, TRUE);
2241 if (body && !tny_camel_bs_mime_part_is_fetched (TNY_CAMEL_BS_MIME_PART (body))) {
2242 /* We have body structure but not the body mime part. What we do
2243 * is restarting load of message */
2244 self = (ModestMsgViewWindow *) modest_mail_operation_get_source (mail_op);
2245 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
2247 tny_header_unset_flag (TNY_HEADER (header), TNY_HEADER_FLAG_CACHED);
2249 modest_msg_view_window_reload (self);
2252 gtk_tree_row_reference_free (row_reference);
2253 g_object_unref (body);
2258 g_object_unref (body);
2261 /* Get the window */
2262 self = (ModestMsgViewWindow *) modest_mail_operation_get_source (mail_op);
2263 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self));
2264 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
2266 /* Update the row reference */
2267 if (priv->row_reference != NULL) {
2268 gtk_tree_row_reference_free (priv->row_reference);
2269 priv->row_reference = (row_reference && gtk_tree_row_reference_valid (row_reference))?gtk_tree_row_reference_copy (row_reference):NULL;
2270 if (priv->next_row_reference != NULL) {
2271 gtk_tree_row_reference_free (priv->next_row_reference);
2273 if (priv->row_reference) {
2274 priv->next_row_reference = gtk_tree_row_reference_copy (priv->row_reference);
2275 select_next_valid_row (priv->header_model, &(priv->next_row_reference), TRUE, priv->is_outbox);
2277 priv->next_row_reference = NULL;
2281 /* Mark header as read */
2282 if (!(tny_header_get_flags (header) & TNY_HEADER_FLAG_SEEN))
2283 tny_header_set_flag (header, TNY_HEADER_FLAG_SEEN);
2285 /* Set new message */
2286 if (priv->msg_view != NULL && TNY_IS_MSG_VIEW (priv->msg_view)) {
2287 tny_msg_view_set_msg (TNY_MSG_VIEW (priv->msg_view), msg);
2288 modest_msg_view_window_update_priority (self);
2289 update_window_title (MODEST_MSG_VIEW_WINDOW (self));
2290 update_branding (MODEST_MSG_VIEW_WINDOW (self));
2291 modest_msg_view_grab_focus (MODEST_MSG_VIEW (priv->msg_view));
2294 /* Set the new message uid of the window */
2295 if (priv->msg_uid) {
2296 g_free (priv->msg_uid);
2297 priv->msg_uid = modest_tny_folder_get_header_unique_id (header);
2300 /* Notify the observers */
2301 g_signal_emit (G_OBJECT (self), signals[MSG_CHANGED_SIGNAL],
2302 0, priv->header_model, priv->row_reference);
2304 /* Sync the flags if the message is not opened from a header
2305 model, i.e, if it's opened from a notification */
2306 if (!priv->header_model)
2310 g_object_unref (self);
2312 gtk_tree_row_reference_free (row_reference);
2316 modest_msg_view_window_get_folder_type (ModestMsgViewWindow *window)
2318 ModestMsgViewWindowPrivate *priv;
2320 TnyFolderType folder_type;
2322 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
2324 folder_type = TNY_FOLDER_TYPE_UNKNOWN;
2326 msg = tny_msg_view_get_msg (TNY_MSG_VIEW (priv->msg_view));
2330 folder = tny_msg_get_folder (msg);
2332 folder_type = modest_tny_folder_guess_folder_type (folder);
2333 g_object_unref (folder);
2335 g_object_unref (msg);
2343 modest_msg_view_window_update_priority (ModestMsgViewWindow *window)
2345 ModestMsgViewWindowPrivate *priv;
2346 TnyHeader *header = NULL;
2347 TnyHeaderFlags flags = 0;
2349 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
2351 if (priv->header_model && priv->row_reference && gtk_tree_row_reference_valid (priv->row_reference)) {
2353 GtkTreePath *path = NULL;
2355 path = gtk_tree_row_reference_get_path (priv->row_reference);
2356 g_return_if_fail (path != NULL);
2357 gtk_tree_model_get_iter (priv->header_model,
2359 gtk_tree_row_reference_get_path (priv->row_reference));
2361 gtk_tree_model_get (priv->header_model, &iter, TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
2363 gtk_tree_path_free (path);
2366 msg = tny_msg_view_get_msg (TNY_MSG_VIEW (priv->msg_view));
2368 header = tny_msg_get_header (msg);
2369 g_object_unref (msg);
2374 flags = tny_header_get_flags (header);
2375 g_object_unref(G_OBJECT(header));
2378 modest_msg_view_set_priority (MODEST_MSG_VIEW(priv->msg_view), flags);
2383 toolbar_resize (ModestMsgViewWindow *self)
2385 ModestMsgViewWindowPrivate *priv = NULL;
2386 ModestWindowPrivate *parent_priv = NULL;
2388 gint static_button_size;
2389 ModestWindowMgr *mgr;
2391 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self));
2392 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
2393 parent_priv = MODEST_WINDOW_GET_PRIVATE(self);
2395 mgr = modest_runtime_get_window_mgr ();
2396 static_button_size = modest_window_mgr_get_fullscreen_mode (mgr)?120:120;
2398 if (parent_priv->toolbar) {
2399 /* Set expandable and homogeneous tool buttons */
2400 widget = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/ToolbarMessageReply");
2401 gtk_tool_item_set_expand (GTK_TOOL_ITEM (widget), TRUE);
2402 gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (widget), TRUE);
2403 widget = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/ToolbarMessageReplyAll");
2404 gtk_tool_item_set_expand (GTK_TOOL_ITEM (widget), TRUE);
2405 gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (widget), TRUE);
2406 widget = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/ToolbarMessageForward");
2407 gtk_tool_item_set_expand (GTK_TOOL_ITEM (widget), TRUE);
2408 gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (widget), TRUE);
2409 widget = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/ToolbarDeleteMessage");
2410 gtk_tool_item_set_expand (GTK_TOOL_ITEM (widget), TRUE);
2411 gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (widget), TRUE);
2412 widget = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/ToolbarDownloadExternalImages");
2413 gtk_tool_item_set_expand (GTK_TOOL_ITEM (widget), TRUE);
2414 gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (widget), TRUE);
2415 gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (priv->next_toolitem), TRUE);
2416 gtk_tool_item_set_expand (GTK_TOOL_ITEM (priv->next_toolitem), TRUE);
2417 gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (priv->prev_toolitem), TRUE);
2418 gtk_tool_item_set_expand (GTK_TOOL_ITEM (priv->prev_toolitem), TRUE);
2423 modest_msg_view_window_show_toolbar (ModestWindow *self,
2424 gboolean show_toolbar)
2426 ModestMsgViewWindowPrivate *priv = NULL;
2427 ModestWindowPrivate *parent_priv;
2429 parent_priv = MODEST_WINDOW_GET_PRIVATE(self);
2430 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(self);
2432 /* Set optimized view status */
2433 priv->optimized_view = !show_toolbar;
2435 if (!parent_priv->toolbar) {
2436 parent_priv->toolbar = gtk_ui_manager_get_widget (parent_priv->ui_manager,
2438 gtk_toolbar_set_icon_size (GTK_TOOLBAR (parent_priv->toolbar), HILDON_ICON_SIZE_FINGER);
2439 gtk_widget_set_no_show_all (parent_priv->toolbar, TRUE);
2441 priv->next_toolitem = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/ToolbarMessageNext");
2442 priv->prev_toolitem = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/ToolbarMessageBack");
2443 toolbar_resize (MODEST_MSG_VIEW_WINDOW (self));
2446 hildon_window_add_toolbar (HILDON_WINDOW (self),
2447 GTK_TOOLBAR (parent_priv->toolbar));
2452 /* Quick hack: this prevents toolbar icons "dance" when progress bar show status is changed */
2453 /* TODO: resize mode migth be GTK_RESIZE_QUEUE, in order to avoid unneccesary shows */
2454 gtk_container_set_resize_mode (GTK_CONTAINER(parent_priv->toolbar), GTK_RESIZE_IMMEDIATE);
2456 gtk_widget_show (GTK_WIDGET (parent_priv->toolbar));
2457 if (modest_msg_view_window_transfer_mode_enabled (MODEST_MSG_VIEW_WINDOW (self)))
2458 set_progress_hint (MODEST_MSG_VIEW_WINDOW (self), TRUE);
2460 set_progress_hint (MODEST_MSG_VIEW_WINDOW (self), FALSE);
2463 gtk_widget_set_no_show_all (parent_priv->toolbar, TRUE);
2464 gtk_widget_hide (GTK_WIDGET (parent_priv->toolbar));
2469 modest_msg_view_window_clipboard_owner_change (GtkClipboard *clipboard,
2471 ModestMsgViewWindow *window)
2473 if (!GTK_WIDGET_VISIBLE (window))
2476 modest_window_check_dimming_rules_group (MODEST_WINDOW (window), MODEST_DIMMING_RULES_CLIPBOARD);
2480 modest_msg_view_window_transfer_mode_enabled (ModestMsgViewWindow *self)
2482 ModestMsgViewWindowPrivate *priv;
2484 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self), FALSE);
2485 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(self);
2487 return priv->progress_hint;
2491 observers_empty (ModestMsgViewWindow *self)
2494 ModestMsgViewWindowPrivate *priv;
2495 gboolean is_empty = TRUE;
2496 guint pending_ops = 0;
2498 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(self);
2499 tmp = priv->progress_widgets;
2501 /* Check all observers */
2502 while (tmp && is_empty) {
2503 pending_ops = modest_progress_object_num_pending_operations (MODEST_PROGRESS_OBJECT(tmp->data));
2504 is_empty = pending_ops == 0;
2506 tmp = g_slist_next(tmp);
2513 on_account_removed (TnyAccountStore *account_store,
2514 TnyAccount *account,
2517 /* Do nothing if it's a transport account, because we only
2518 show the messages of a store account */
2519 if (tny_account_get_account_type(account) == TNY_ACCOUNT_TYPE_STORE) {
2520 const gchar *parent_acc = NULL;
2521 const gchar *our_acc = NULL;
2523 our_acc = modest_window_get_active_account (MODEST_WINDOW (user_data));
2524 parent_acc = modest_tny_account_get_parent_modest_account_name_for_server_account (account);
2526 /* Close this window if I'm showing a message of the removed account */
2527 if (our_acc && parent_acc && strcmp (parent_acc, our_acc) == 0)
2528 modest_ui_actions_on_close_window (NULL, MODEST_WINDOW (user_data));
2533 on_mail_operation_started (ModestMailOperation *mail_op,
2536 ModestMsgViewWindow *self;
2537 ModestMailOperationTypeOperation op_type;
2539 ModestMsgViewWindowPrivate *priv;
2540 GObject *source = NULL;
2542 self = MODEST_MSG_VIEW_WINDOW (user_data);
2543 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
2544 op_type = modest_mail_operation_get_type_operation (mail_op);
2545 tmp = priv->progress_widgets;
2546 source = modest_mail_operation_get_source(mail_op);
2547 if (G_OBJECT (self) == source) {
2548 if (op_type == MODEST_MAIL_OPERATION_TYPE_RECEIVE ||
2549 op_type == MODEST_MAIL_OPERATION_TYPE_OPEN ||
2550 op_type == MODEST_MAIL_OPERATION_TYPE_DELETE) {
2551 set_progress_hint (self, TRUE);
2553 modest_progress_object_add_operation (
2554 MODEST_PROGRESS_OBJECT (tmp->data),
2556 tmp = g_slist_next (tmp);
2560 g_object_unref (source);
2562 /* Update dimming rules */
2563 check_dimming_rules_after_change (self);
2567 on_mail_operation_finished (ModestMailOperation *mail_op,
2570 ModestMsgViewWindow *self;
2571 ModestMailOperationTypeOperation op_type;
2573 ModestMsgViewWindowPrivate *priv;
2575 self = MODEST_MSG_VIEW_WINDOW (user_data);
2576 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
2577 op_type = modest_mail_operation_get_type_operation (mail_op);
2578 tmp = priv->progress_widgets;
2580 if (op_type == MODEST_MAIL_OPERATION_TYPE_RECEIVE ||
2581 op_type == MODEST_MAIL_OPERATION_TYPE_OPEN ||
2582 op_type == MODEST_MAIL_OPERATION_TYPE_DELETE) {
2584 modest_progress_object_remove_operation (MODEST_PROGRESS_OBJECT (tmp->data),
2586 tmp = g_slist_next (tmp);
2589 /* If no more operations are being observed, NORMAL mode is enabled again */
2590 if (observers_empty (self)) {
2591 set_progress_hint (self, FALSE);
2595 /* Update dimming rules. We have to do this right here
2596 and not in view_msg_cb because at that point the
2597 transfer mode is still enabled so the dimming rule
2598 won't let the user delete the message that has been
2599 readed for example */
2600 check_dimming_rules_after_change (self);
2604 on_queue_changed (ModestMailOperationQueue *queue,
2605 ModestMailOperation *mail_op,
2606 ModestMailOperationQueueNotification type,
2607 ModestMsgViewWindow *self)
2609 ModestMsgViewWindowPrivate *priv;
2611 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
2613 /* If this operations was created by another window, do nothing */
2614 if (!modest_mail_operation_is_mine (mail_op, G_OBJECT(self)))
2617 if (type == MODEST_MAIL_OPERATION_QUEUE_OPERATION_ADDED) {
2618 priv->sighandlers = modest_signal_mgr_connect (priv->sighandlers,
2620 "operation-started",
2621 G_CALLBACK (on_mail_operation_started),
2623 priv->sighandlers = modest_signal_mgr_connect (priv->sighandlers,
2625 "operation-finished",
2626 G_CALLBACK (on_mail_operation_finished),
2628 } else if (type == MODEST_MAIL_OPERATION_QUEUE_OPERATION_REMOVED) {
2629 priv->sighandlers = modest_signal_mgr_disconnect (priv->sighandlers,
2631 "operation-started");
2632 priv->sighandlers = modest_signal_mgr_disconnect (priv->sighandlers,
2634 "operation-finished");
2639 modest_msg_view_window_get_attachments (ModestMsgViewWindow *win)
2641 ModestMsgViewWindowPrivate *priv;
2642 TnyList *selected_attachments = NULL;
2644 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (win), NULL);
2645 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (win);
2647 /* In Hildon 2.2 as there's no selection we assume we have all attachments selected */
2648 selected_attachments = modest_msg_view_get_attachments (MODEST_MSG_VIEW (priv->msg_view));
2650 return selected_attachments;
2654 ModestMsgViewWindow *self;
2656 gchar *attachment_uid;
2657 } DecodeAsyncHelper;
2660 on_decode_to_stream_async_handler (TnyMimePart *mime_part,
2666 DecodeAsyncHelper *helper = (DecodeAsyncHelper *) user_data;
2667 const gchar *content_type;
2669 if (cancelled || err) {
2672 if ((err->domain == TNY_ERROR_DOMAIN) &&
2673 (err->code == TNY_IO_ERROR_WRITE) &&
2674 (errno == ENOSPC)) {
2675 msg = g_strdup_printf (_KR("cerm_device_memory_full"), "");
2677 msg = g_strdup (_("mail_ib_file_operation_failed"));
2679 modest_platform_information_banner (NULL, NULL, msg);
2685 /* It could happen that the window was closed. So we
2686 assume it is a cancelation */
2687 if (!GTK_WIDGET_VISIBLE (helper->self))
2690 /* Remove the progress hint */
2691 set_progress_hint (helper->self, FALSE);
2693 content_type = tny_mime_part_get_content_type (mime_part);
2694 if (g_str_has_prefix (content_type, "message/rfc822")) {
2695 ModestWindowMgr *mgr;
2696 ModestWindow *msg_win = NULL;
2699 const gchar *mailbox;
2700 TnyStream *file_stream;
2703 fd = g_open (helper->file_path, O_RDONLY, 0644);
2705 file_stream = tny_fs_stream_new (fd);
2707 mgr = modest_runtime_get_window_mgr ();
2709 account = g_strdup (modest_window_get_active_account (MODEST_WINDOW (helper->self)));
2710 mailbox = modest_window_get_active_mailbox (MODEST_WINDOW (helper->self));
2713 account = modest_account_mgr_get_default_account (modest_runtime_get_account_mgr ());
2715 msg = tny_camel_msg_new ();
2716 tny_camel_msg_parse (msg, file_stream);
2717 msg_win = modest_msg_view_window_new_for_attachment (TNY_MSG (msg), account, mailbox, helper->attachment_uid);
2718 modest_window_set_zoom (MODEST_WINDOW (msg_win),
2719 modest_window_get_zoom (MODEST_WINDOW (helper->self)));
2720 if (modest_window_mgr_register_window (mgr, msg_win, MODEST_WINDOW (helper->self)))
2721 gtk_widget_show_all (GTK_WIDGET (msg_win));
2723 gtk_widget_destroy (GTK_WIDGET (msg_win));
2724 g_object_unref (msg);
2725 g_object_unref (file_stream);
2727 modest_platform_information_banner (NULL, NULL, _("mail_ib_file_operation_failed"));
2732 /* make the file read-only */
2733 g_chmod(helper->file_path, 0444);
2735 /* Activate the file */
2736 modest_platform_activate_file (helper->file_path, tny_mime_part_get_content_type (mime_part));
2741 g_object_unref (helper->self);
2742 g_free (helper->file_path);
2743 g_free (helper->attachment_uid);
2744 g_slice_free (DecodeAsyncHelper, helper);
2748 view_attachment_connect_handler (gboolean canceled,
2750 GtkWindow *parent_window,
2751 TnyAccount *account,
2755 if (canceled || err) {
2756 g_object_unref (part);
2760 modest_msg_view_window_view_attachment (MODEST_MSG_VIEW_WINDOW (parent_window),
2762 g_object_unref (part);
2766 modest_msg_view_window_view_attachment (ModestMsgViewWindow *window,
2767 TnyMimePart *mime_part)
2769 ModestMsgViewWindowPrivate *priv;
2770 const gchar *msg_uid;
2771 gchar *attachment_uid = NULL;
2772 gint attachment_index = 0;
2773 TnyList *attachments;
2775 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window));
2776 g_return_if_fail (TNY_IS_MIME_PART (mime_part) || (mime_part == NULL));
2777 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
2779 msg_uid = modest_msg_view_window_get_message_uid (MODEST_MSG_VIEW_WINDOW (window));
2780 attachments = modest_msg_view_get_attachments (MODEST_MSG_VIEW (priv->msg_view));
2781 attachment_index = modest_list_index (attachments, (GObject *) mime_part);
2782 g_object_unref (attachments);
2784 if (msg_uid && attachment_index >= 0) {
2785 attachment_uid = g_strdup_printf ("%s/%d", msg_uid, attachment_index);
2788 if (mime_part == NULL) {
2789 gboolean error = FALSE;
2790 TnyList *selected_attachments = modest_msg_view_get_selected_attachments (MODEST_MSG_VIEW (priv->msg_view));
2791 if (selected_attachments == NULL || tny_list_get_length (selected_attachments) == 0) {
2793 } else if (tny_list_get_length (selected_attachments) > 1) {
2794 hildon_banner_show_information (NULL, NULL, _("mcen_ib_unable_to_display_more"));
2798 iter = tny_list_create_iterator (selected_attachments);
2799 mime_part = (TnyMimePart *) tny_iterator_get_current (iter);
2800 g_object_unref (iter);
2802 if (selected_attachments)
2803 g_object_unref (selected_attachments);
2808 g_object_ref (mime_part);
2811 if (tny_mime_part_is_purged (mime_part))
2814 if (TNY_IS_CAMEL_BS_MIME_PART (mime_part) &&
2815 !tny_camel_bs_mime_part_is_fetched (TNY_CAMEL_BS_MIME_PART (mime_part))) {
2817 TnyAccount *account;
2819 is_merge = g_str_has_prefix (priv->msg_uid, "merge:");
2821 /* Get the account */
2823 account = tny_account_store_find_account (TNY_ACCOUNT_STORE (modest_runtime_get_account_store ()),
2826 if (!tny_device_is_online (modest_runtime_get_device())) {
2827 modest_platform_connect_and_perform (GTK_WINDOW (window),
2829 TNY_ACCOUNT (account),
2830 (ModestConnectedPerformer) view_attachment_connect_handler,
2831 g_object_ref (mime_part));
2836 if (!modest_tny_mime_part_is_msg (mime_part) && tny_mime_part_get_filename (mime_part)) {
2837 gchar *filepath = NULL;
2838 const gchar *att_filename = tny_mime_part_get_filename (mime_part);
2839 gboolean show_error_banner = FALSE;
2840 TnyFsStream *temp_stream = NULL;
2841 temp_stream = modest_utils_create_temp_stream (att_filename, attachment_uid,
2844 if (temp_stream != NULL) {
2845 ModestAccountMgr *mgr;
2846 DecodeAsyncHelper *helper;
2847 gboolean decode_in_provider;
2848 ModestProtocol *protocol;
2849 const gchar *account;
2851 /* Activate progress hint */
2852 set_progress_hint (window, TRUE);
2854 helper = g_slice_new0 (DecodeAsyncHelper);
2855 helper->self = g_object_ref (window);
2856 helper->file_path = g_strdup (filepath);
2857 helper->attachment_uid = g_strdup (attachment_uid);
2859 decode_in_provider = FALSE;
2860 mgr = modest_runtime_get_account_mgr ();
2861 account = modest_window_get_active_account (MODEST_WINDOW (window));
2862 if (modest_account_mgr_account_is_multimailbox (mgr, account, &protocol)) {
2863 if (MODEST_IS_ACCOUNT_PROTOCOL (protocol)) {
2865 uri = g_strconcat ("file://", filepath, NULL);
2866 decode_in_provider =
2867 modest_account_protocol_decode_part_to_stream_async (
2868 MODEST_ACCOUNT_PROTOCOL (protocol),
2871 TNY_STREAM (temp_stream),
2872 on_decode_to_stream_async_handler,
2879 if (!decode_in_provider)
2880 tny_mime_part_decode_to_stream_async (mime_part, TNY_STREAM (temp_stream),
2881 on_decode_to_stream_async_handler,
2884 g_object_unref (temp_stream);
2885 /* NOTE: files in the temporary area will be automatically
2886 * cleaned after some time if they are no longer in use */
2889 const gchar *content_type;
2890 /* the file may already exist but it isn't writable,
2891 * let's try to open it anyway */
2892 content_type = tny_mime_part_get_content_type (mime_part);
2893 modest_platform_activate_file (filepath, content_type);
2895 g_warning ("%s: modest_utils_create_temp_stream failed", __FUNCTION__);
2896 show_error_banner = TRUE;
2901 if (show_error_banner)
2902 modest_platform_information_banner (NULL, NULL, _("mail_ib_file_operation_failed"));
2903 } else if (!modest_tny_mime_part_is_msg (mime_part)) {
2904 ModestWindowMgr *mgr;
2905 ModestWindow *msg_win = NULL;
2906 TnyMsg *current_msg;
2910 current_msg = modest_msg_view_window_get_message (MODEST_MSG_VIEW_WINDOW (window));
2911 mgr = modest_runtime_get_window_mgr ();
2912 header = tny_msg_get_header (TNY_MSG (current_msg));
2913 found = modest_window_mgr_find_registered_message_uid (mgr,
2918 g_debug ("window for this body is already being created");
2921 /* it's not found, so create a new window for it */
2922 modest_window_mgr_register_header (mgr, header, attachment_uid); /* register the uid before building the window */
2923 gchar *account = g_strdup (modest_window_get_active_account (MODEST_WINDOW (window)));
2924 const gchar *mailbox = modest_window_get_active_mailbox (MODEST_WINDOW (window));
2926 account = modest_account_mgr_get_default_account (modest_runtime_get_account_mgr ());
2928 msg_win = modest_msg_view_window_new_with_other_body (TNY_MSG (current_msg), TNY_MIME_PART (mime_part),
2929 account, mailbox, attachment_uid);
2931 modest_window_set_zoom (MODEST_WINDOW (msg_win),
2932 modest_window_get_zoom (MODEST_WINDOW (window)));
2933 if (modest_window_mgr_register_window (mgr, msg_win, MODEST_WINDOW (window)))
2934 gtk_widget_show_all (GTK_WIDGET (msg_win));
2936 gtk_widget_destroy (GTK_WIDGET (msg_win));
2938 g_object_unref (current_msg);
2940 /* message attachment */
2941 TnyHeader *header = NULL;
2942 ModestWindowMgr *mgr;
2943 ModestWindow *msg_win = NULL;
2946 header = tny_msg_get_header (TNY_MSG (mime_part));
2947 mgr = modest_runtime_get_window_mgr ();
2948 found = modest_window_mgr_find_registered_header (mgr, header, &msg_win);
2951 /* if it's found, but there is no msg_win, it's probably in the process of being created;
2952 * thus, we don't do anything */
2953 g_debug ("window for is already being created");
2955 /* it's not found, so create a new window for it */
2956 modest_window_mgr_register_header (mgr, header, attachment_uid); /* register the uid before building the window */
2957 gchar *account = g_strdup (modest_window_get_active_account (MODEST_WINDOW (window)));
2958 const gchar *mailbox = modest_window_get_active_mailbox (MODEST_WINDOW (window));
2960 account = modest_account_mgr_get_default_account (modest_runtime_get_account_mgr ());
2961 msg_win = modest_msg_view_window_new_for_attachment (TNY_MSG (mime_part), account,
2962 mailbox, attachment_uid);
2963 modest_window_set_zoom (MODEST_WINDOW (msg_win),
2964 modest_window_get_zoom (MODEST_WINDOW (window)));
2965 if (modest_window_mgr_register_window (mgr, msg_win, MODEST_WINDOW (window)))
2966 gtk_widget_show_all (GTK_WIDGET (msg_win));
2968 gtk_widget_destroy (GTK_WIDGET (msg_win));
2974 g_free (attachment_uid);
2976 g_object_unref (mime_part);
2988 GnomeVFSResult result;
2990 ModestMsgViewWindow *window;
2993 static void save_mime_part_info_free (SaveMimePartInfo *info, gboolean with_struct);
2994 static gboolean idle_save_mime_part_show_result (SaveMimePartInfo *info);
2995 static gpointer save_mime_part_to_file (SaveMimePartInfo *info);
2996 static void save_mime_parts_to_file_with_checks (GtkWindow *parent, SaveMimePartInfo *info);
2999 save_mime_part_info_free (SaveMimePartInfo *info, gboolean with_struct)
3003 for (node = info->pairs; node != NULL; node = g_list_next (node)) {
3004 SaveMimePartPair *pair = (SaveMimePartPair *) node->data;
3005 g_free (pair->filename);
3006 g_object_unref (pair->part);
3007 g_slice_free (SaveMimePartPair, pair);
3009 g_list_free (info->pairs);
3012 g_object_unref (info->window);
3013 info->window = NULL;
3015 g_slice_free (SaveMimePartInfo, info);
3020 idle_save_mime_part_show_result (SaveMimePartInfo *info)
3022 /* This is a GDK lock because we are an idle callback and
3023 * hildon_banner_show_information is or does Gtk+ code */
3025 gdk_threads_enter (); /* CHECKED */
3026 if (info->result == GNOME_VFS_ERROR_CANCELLED) {
3028 } else if (info->result == GNOME_VFS_OK) {
3029 hildon_banner_show_information (NULL, NULL, _CS("sfil_ib_saved"));
3030 } else if (info->result == GNOME_VFS_ERROR_NO_SPACE) {
3033 /* Check if the uri belongs to the external mmc */
3034 if (g_str_has_prefix (info->uri, g_getenv (MODEST_MMC1_VOLUMEPATH_ENV)))
3035 msg = g_strdup_printf (_KR("cerm_device_memory_full"), "");
3037 msg = g_strdup (_KR("cerm_memory_card_full"));
3038 modest_platform_information_banner (NULL, NULL, msg);
3041 hildon_banner_show_information (NULL, NULL, _("mail_ib_file_operation_failed"));
3043 save_mime_part_info_free (info, FALSE);
3044 gdk_threads_leave (); /* CHECKED */
3050 save_mime_part_to_file_connect_handler (gboolean canceled,
3052 GtkWindow *parent_window,
3053 TnyAccount *account,
3054 SaveMimePartInfo *info)
3056 if (canceled || err) {
3057 if (canceled && !err) {
3058 info->result = GNOME_VFS_ERROR_CANCELLED;
3060 g_idle_add ((GSourceFunc) idle_save_mime_part_show_result, info);
3062 g_thread_create ((GThreadFunc)save_mime_part_to_file, info, FALSE, NULL);
3067 save_mime_part_to_file_connect_idle (SaveMimePartInfo *info)
3070 TnyAccount *account;
3071 ModestMsgViewWindowPrivate *priv;
3073 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (info->window);
3075 is_merge = g_str_has_prefix (priv->msg_uid, "merge:");
3078 /* Get the account */
3080 account = tny_account_store_find_account (TNY_ACCOUNT_STORE (modest_runtime_get_account_store ()),
3083 modest_platform_connect_and_perform (GTK_WINDOW (info->window),
3085 TNY_ACCOUNT (account),
3086 (ModestConnectedPerformer) save_mime_part_to_file_connect_handler,
3092 save_mime_part_to_file (SaveMimePartInfo *info)
3094 GnomeVFSHandle *handle;
3096 SaveMimePartPair *pair = (SaveMimePartPair *) info->pairs->data;
3098 if (TNY_IS_CAMEL_BS_MIME_PART (pair->part) &&
3099 !tny_camel_bs_mime_part_is_fetched (TNY_CAMEL_BS_MIME_PART (pair->part))) {
3100 g_idle_add ((GSourceFunc) save_mime_part_to_file_connect_idle, info);
3104 info->result = gnome_vfs_create (&handle, pair->filename, GNOME_VFS_OPEN_WRITE, FALSE, 0644);
3105 if (info->result == GNOME_VFS_OK) {
3106 GError *error = NULL;
3107 gboolean decode_in_provider;
3109 ModestAccountMgr *mgr;
3110 const gchar *account;
3111 ModestProtocol *protocol = NULL;
3113 stream = tny_vfs_stream_new (handle);
3115 decode_in_provider = FALSE;
3116 mgr = modest_runtime_get_account_mgr ();
3117 account = modest_window_get_active_account (MODEST_WINDOW (info->window));
3118 if (modest_account_mgr_account_is_multimailbox (mgr, account, &protocol)) {
3119 if (MODEST_IS_ACCOUNT_PROTOCOL (protocol)) {
3120 decode_in_provider =
3121 modest_account_protocol_decode_part_to_stream (
3122 MODEST_ACCOUNT_PROTOCOL (protocol),
3130 if (!decode_in_provider)
3131 written = tny_mime_part_decode_to_stream (pair->part, stream, &error);
3134 g_warning ("modest: could not save attachment %s: %d (%s)\n", pair->filename, error?error->code:-1, error?error->message:"Unknown error");
3136 if ((error->domain == TNY_ERROR_DOMAIN) &&
3137 (error->code == TNY_IO_ERROR_WRITE) &&
3138 (errno == ENOSPC)) {
3139 info->result = GNOME_VFS_ERROR_NO_SPACE;
3141 info->result = GNOME_VFS_ERROR_IO;
3144 g_object_unref (G_OBJECT (stream));
3146 g_warning ("Could not create save attachment %s: %s\n",
3147 pair->filename, gnome_vfs_result_to_string (info->result));
3150 /* Go on saving remaining files */
3151 info->pairs = g_list_remove_link (info->pairs, info->pairs);
3152 if (info->pairs != NULL) {
3153 save_mime_part_to_file (info);
3155 g_idle_add ((GSourceFunc) idle_save_mime_part_show_result, info);
3162 save_mime_parts_to_file_with_checks (GtkWindow *parent,
3163 SaveMimePartInfo *info)
3165 gboolean is_ok = TRUE;
3166 gint replaced_files = 0;
3167 const GList *files = info->pairs;
3168 const GList *iter, *to_replace = NULL;
3170 for (iter = files; (iter != NULL) && (replaced_files < 2); iter = g_list_next(iter)) {
3171 SaveMimePartPair *pair = iter->data;
3172 gchar *unescaped = g_uri_unescape_string (pair->filename, NULL);
3174 if (modest_utils_file_exists (unescaped)) {
3176 if (replaced_files == 1)
3181 if (replaced_files) {
3184 if (replaced_files == 1) {
3185 SaveMimePartPair *pair = to_replace->data;
3186 const gchar *basename = strrchr (pair->filename, G_DIR_SEPARATOR) + 1;
3187 gchar *escaped_basename, *message;
3189 escaped_basename = g_uri_unescape_string (basename, NULL);
3190 message = g_strdup_printf ("%s\n%s",
3191 _FM("docm_nc_replace_file"),
3192 (escaped_basename) ? escaped_basename : "");
3193 response = modest_platform_run_confirmation_dialog (parent, message);
3195 g_free (escaped_basename);
3197 response = modest_platform_run_confirmation_dialog (parent,
3198 _FM("docm_nc_replace_multiple"));
3200 if (response != GTK_RESPONSE_OK)
3205 save_mime_part_info_free (info, TRUE);
3207 g_thread_create ((GThreadFunc)save_mime_part_to_file, info, FALSE, NULL);
3212 typedef struct _SaveAttachmentsInfo {
3213 TnyList *attachments_list;
3214 ModestMsgViewWindow *window;
3215 } SaveAttachmentsInfo;
3218 save_attachments_response (GtkDialog *dialog,
3222 TnyList *mime_parts;
3224 GList *files_to_save = NULL;
3225 gchar *current_folder;
3226 SaveAttachmentsInfo *sa_info = (SaveAttachmentsInfo *) user_data;
3228 mime_parts = TNY_LIST (sa_info->attachments_list);
3230 if (arg1 != GTK_RESPONSE_OK)
3233 chooser_uri = gtk_file_chooser_get_uri (GTK_FILE_CHOOSER (dialog));
3234 current_folder = gtk_file_chooser_get_current_folder_uri (GTK_FILE_CHOOSER (dialog));
3235 if (current_folder && *current_folder != '\0') {
3237 modest_conf_set_string (modest_runtime_get_conf (), MODEST_CONF_LATEST_SAVE_ATTACHMENT_PATH,
3238 current_folder,&err);
3240 g_debug ("Error storing latest used folder: %s", err->message);
3244 g_free (current_folder);
3246 if (!modest_utils_folder_writable (chooser_uri)) {
3247 const gchar *err_msg;
3249 #ifdef MODEST_PLATFORM_MAEMO
3250 if (modest_maemo_utils_in_usb_mode ()) {
3251 err_msg = dgettext ("hildon-status-bar-usb", "usbh_ib_mmc_usb_connected");
3253 err_msg = _FM("sfil_ib_readonly_location");
3256 err_msg = _FM("sfil_ib_readonly_location");
3258 hildon_banner_show_information (NULL, NULL, err_msg);
3262 iter = tny_list_create_iterator (mime_parts);
3263 while (!tny_iterator_is_done (iter)) {
3264 TnyMimePart *mime_part = (TnyMimePart *) tny_iterator_get_current (iter);
3266 if ((modest_tny_mime_part_is_attachment_for_modest (mime_part)) &&
3267 !tny_mime_part_is_purged (mime_part) &&
3268 (tny_mime_part_get_filename (mime_part) != NULL)) {
3269 SaveMimePartPair *pair;
3271 pair = g_slice_new0 (SaveMimePartPair);
3273 if (tny_list_get_length (mime_parts) > 1) {
3275 gnome_vfs_escape_slashes (tny_mime_part_get_filename (mime_part));
3276 pair->filename = g_build_filename (chooser_uri, escaped, NULL);
3279 pair->filename = g_strdup (chooser_uri);
3281 pair->part = mime_part;
3282 files_to_save = g_list_prepend (files_to_save, pair);
3284 tny_iterator_next (iter);
3286 g_object_unref (iter);
3289 if (files_to_save != NULL) {
3290 SaveMimePartInfo *info = g_slice_new0 (SaveMimePartInfo);
3291 info->pairs = files_to_save;
3292 info->result = TRUE;
3293 info->uri = g_strdup (chooser_uri);
3294 info->window = g_object_ref (sa_info->window);
3295 save_mime_parts_to_file_with_checks ((GtkWindow *) dialog, info);
3297 g_free (chooser_uri);
3300 /* Free and close the dialog */
3301 g_object_unref (mime_parts);
3302 g_object_unref (sa_info->window);
3303 g_slice_free (SaveAttachmentsInfo, sa_info);
3304 gtk_widget_destroy (GTK_WIDGET (dialog));
3308 modest_msg_view_window_save_attachments (ModestMsgViewWindow *window,
3309 TnyList *mime_parts)
3311 ModestMsgViewWindowPrivate *priv;
3312 GtkWidget *save_dialog = NULL;
3313 gchar *conf_folder = NULL;
3314 gchar *filename = NULL;
3315 gchar *save_multiple_str = NULL;
3316 const gchar *root_folder = "file:///";
3318 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window));
3319 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
3321 if (mime_parts == NULL) {
3322 /* In Hildon 2.2 save and delete operate over all the attachments as there's no
3323 * selection available */
3324 mime_parts = modest_msg_view_get_attachments (MODEST_MSG_VIEW (priv->msg_view));
3325 if (mime_parts && !modest_maemo_utils_select_attachments (GTK_WINDOW (window), mime_parts, FALSE)) {
3326 g_object_unref (mime_parts);
3329 if (mime_parts == NULL || tny_list_get_length (mime_parts) == 0) {
3331 g_object_unref (mime_parts);
3337 g_object_ref (mime_parts);
3340 /* prepare dialog */
3341 if (tny_list_get_length (mime_parts) == 1) {
3343 /* only one attachment selected */
3344 iter = tny_list_create_iterator (mime_parts);
3345 TnyMimePart *mime_part = (TnyMimePart *) tny_iterator_get_current (iter);
3346 g_object_unref (iter);
3347 if (!modest_tny_mime_part_is_msg (mime_part) &&
3348 modest_tny_mime_part_is_attachment_for_modest (mime_part) &&
3349 !tny_mime_part_is_purged (mime_part)) {
3350 filename = g_strdup (tny_mime_part_get_filename (mime_part));
3352 /* TODO: show any error? */
3353 g_warning ("%s: Tried to save a non-file attachment", __FUNCTION__);
3354 g_object_unref (mime_parts);
3357 g_object_unref (mime_part);
3359 gint num = tny_list_get_length (mime_parts);
3360 save_multiple_str = g_strdup_printf (dngettext("hildon-fm",
3361 "sfil_va_number_of_objects_attachment",
3362 "sfil_va_number_of_objects_attachments",
3366 save_dialog = hildon_file_chooser_dialog_new (GTK_WINDOW (window),
3367 GTK_FILE_CHOOSER_ACTION_SAVE);
3369 /* Get last used folder */
3370 conf_folder = modest_conf_get_string (modest_runtime_get_conf (),
3371 MODEST_CONF_LATEST_SAVE_ATTACHMENT_PATH, NULL);
3373 /* File chooser stops working if we select "file:///" as current folder */
3374 if (conf_folder && g_ascii_strcasecmp (root_folder, conf_folder) != 0) {
3375 g_free (conf_folder);
3379 if (conf_folder && conf_folder[0] != '\0') {
3380 gtk_file_chooser_set_current_folder_uri (GTK_FILE_CHOOSER (save_dialog), conf_folder);
3383 /* Set the default folder to documents folder */
3384 docs_folder = (gchar *) g_strdup(g_get_user_special_dir (G_USER_DIRECTORY_DOCUMENTS));
3387 docs_folder = g_build_filename (g_getenv (MYDOCS_ENV), DOCS_FOLDER, NULL);
3389 gtk_file_chooser_set_current_folder (GTK_FILE_CHOOSER (save_dialog), docs_folder);
3390 g_free (docs_folder);
3392 g_free (conf_folder);
3396 gtk_file_chooser_set_current_name (GTK_FILE_CHOOSER (save_dialog),
3401 /* if multiple, set multiple string */
3402 if (save_multiple_str) {
3403 g_object_set (G_OBJECT (save_dialog), "save-multiple", save_multiple_str, NULL);
3404 gtk_window_set_title (GTK_WINDOW (save_dialog), _FM("sfil_ti_save_objects_files"));
3405 g_free (save_multiple_str);
3408 /* We must run this asynchronously, because the hildon dialog
3409 performs a gtk_dialog_run by itself which leads to gdk
3411 SaveAttachmentsInfo *sa_info;
3412 sa_info = g_slice_new (SaveAttachmentsInfo);
3413 sa_info->attachments_list = mime_parts;
3414 sa_info->window = g_object_ref (window);
3415 g_signal_connect (save_dialog, "response",
3416 G_CALLBACK (save_attachments_response), sa_info);
3418 gtk_widget_show_all (save_dialog);
3422 show_remove_attachment_information (gpointer userdata)
3424 ModestMsgViewWindow *window = (ModestMsgViewWindow *) userdata;
3425 ModestMsgViewWindowPrivate *priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
3427 /* We're outside the main lock */
3428 gdk_threads_enter ();
3430 if (priv->remove_attachment_banner != NULL) {
3431 gtk_widget_destroy (priv->remove_attachment_banner);
3432 g_object_unref (priv->remove_attachment_banner);
3435 priv->remove_attachment_banner = g_object_ref (
3436 hildon_banner_show_animation (NULL, NULL, _("mcen_me_inbox_remove_attachments")));
3438 gdk_threads_leave ();
3444 modest_msg_view_window_remove_attachments (ModestMsgViewWindow *window, gboolean get_all)
3446 ModestMsgViewWindowPrivate *priv;
3447 TnyList *mime_parts = NULL, *tmp;
3448 gchar *confirmation_message;
3454 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window));
3455 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
3457 /* In hildon 2.2 we ignore the get_all flag as we always get all attachments. This is
3458 * because we don't have selection
3460 mime_parts = modest_msg_view_get_attachments (MODEST_MSG_VIEW (priv->msg_view));
3462 /* Remove already purged messages from mime parts list. We use
3463 a copy of the list to remove items in the original one */
3464 tmp = tny_list_copy (mime_parts);
3465 iter = tny_list_create_iterator (tmp);
3466 while (!tny_iterator_is_done (iter)) {
3467 TnyMimePart *part = TNY_MIME_PART (tny_iterator_get_current (iter));
3468 if (tny_mime_part_is_purged (part))
3469 tny_list_remove (mime_parts, (GObject *) part);
3471 g_object_unref (part);
3472 tny_iterator_next (iter);
3474 g_object_unref (tmp);
3475 g_object_unref (iter);
3477 if (!modest_maemo_utils_select_attachments (GTK_WINDOW (window), mime_parts, TRUE) ||
3478 tny_list_get_length (mime_parts) == 0) {
3479 g_object_unref (mime_parts);
3483 n_attachments = tny_list_get_length (mime_parts);
3484 if (n_attachments == 1) {
3488 iter = tny_list_create_iterator (mime_parts);
3489 part = (TnyMimePart *) tny_iterator_get_current (iter);
3490 g_object_unref (iter);
3491 if (modest_tny_mime_part_is_msg (part)) {
3493 header = tny_msg_get_header (TNY_MSG (part));
3494 filename = tny_header_dup_subject (header);
3495 g_object_unref (header);
3496 if (filename == NULL)
3497 filename = g_strdup (_("mail_va_no_subject"));
3499 filename = g_strdup (tny_mime_part_get_filename (TNY_MIME_PART (part)));
3501 confirmation_message = g_strdup_printf (_("mcen_nc_purge_file_text"), filename);
3503 g_object_unref (part);
3505 confirmation_message = g_strdup_printf (ngettext("mcen_nc_purge_file_text",
3506 "mcen_nc_purge_files_text",
3507 n_attachments), n_attachments);
3509 response = modest_platform_run_confirmation_dialog (GTK_WINDOW (window),
3510 confirmation_message);
3511 g_free (confirmation_message);
3513 if (response != GTK_RESPONSE_OK) {
3514 g_object_unref (mime_parts);
3518 priv->purge_timeout = g_timeout_add (2000, show_remove_attachment_information, window);
3520 iter = tny_list_create_iterator (mime_parts);
3521 while (!tny_iterator_is_done (iter)) {
3524 part = (TnyMimePart *) tny_iterator_get_current (iter);
3525 tny_mime_part_set_purged (TNY_MIME_PART (part));
3526 g_object_unref (part);
3527 tny_iterator_next (iter);
3529 g_object_unref (iter);
3531 msg = tny_msg_view_get_msg (TNY_MSG_VIEW (priv->msg_view));
3532 tny_msg_view_clear (TNY_MSG_VIEW (priv->msg_view));
3533 tny_msg_rewrite_cache (msg);
3534 tny_msg_view_set_msg (TNY_MSG_VIEW (priv->msg_view), msg);
3535 g_object_unref (msg);
3536 update_branding (MODEST_MSG_VIEW_WINDOW (window));
3538 g_object_unref (mime_parts);
3540 if (priv->purge_timeout > 0) {
3541 g_source_remove (priv->purge_timeout);
3542 priv->purge_timeout = 0;
3545 if (priv->remove_attachment_banner) {
3546 gtk_widget_destroy (priv->remove_attachment_banner);
3547 g_object_unref (priv->remove_attachment_banner);
3548 priv->remove_attachment_banner = NULL;
3554 update_window_title (ModestMsgViewWindow *window)
3556 ModestMsgViewWindowPrivate *priv;
3558 TnyHeader *header = NULL;
3559 gchar *subject = NULL;
3561 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
3563 /* Note that if the window is closed while we're retrieving
3564 the message, this widget could de deleted */
3565 if (!priv->msg_view)
3568 msg = tny_msg_view_get_msg (TNY_MSG_VIEW (priv->msg_view));
3570 if (priv->other_body) {
3573 description = modest_tny_mime_part_get_header_value (priv->other_body, "Content-Description");
3575 g_strstrip (description);
3576 subject = description;
3578 } else if (msg != NULL) {
3579 header = tny_msg_get_header (msg);
3580 subject = tny_header_dup_subject (header);
3581 g_object_unref (header);
3582 g_object_unref (msg);
3585 if ((subject == NULL)||(subject[0] == '\0')) {
3587 subject = g_strdup (_("mail_va_no_subject"));
3590 gtk_window_set_title (GTK_WINDOW (window), subject);
3595 on_move_focus (GtkWidget *widget,
3596 GtkDirectionType direction,
3599 g_signal_stop_emission_by_name (G_OBJECT (widget), "move-focus");
3603 fetch_image_open_stream (TnyStreamCache *self, gint64 *expected_size, gchar *uri)
3605 GnomeVFSResult result;
3606 GnomeVFSHandle *handle = NULL;
3607 GnomeVFSFileInfo *info = NULL;
3610 result = gnome_vfs_open (&handle, uri, GNOME_VFS_OPEN_READ);
3611 if (result != GNOME_VFS_OK) {
3616 info = gnome_vfs_file_info_new ();
3617 result = gnome_vfs_get_file_info_from_handle (handle, info, GNOME_VFS_FILE_INFO_DEFAULT);
3618 if (result != GNOME_VFS_OK || ! (info->valid_fields & GNOME_VFS_FILE_INFO_FIELDS_SIZE)) {
3619 /* We put a "safe" default size for going to cache */
3620 *expected_size = (300*1024);
3622 *expected_size = info->size;
3624 gnome_vfs_file_info_unref (info);
3626 stream = tny_vfs_stream_new (handle);
3635 TnyStream *output_stream;
3636 GtkWidget *msg_view;
3641 on_fetch_image_idle_refresh_view (gpointer userdata)
3644 FetchImageData *fidata = (FetchImageData *) userdata;
3646 gdk_threads_enter ();
3647 if (GTK_WIDGET_DRAWABLE (fidata->msg_view)) {
3648 ModestMsgViewWindowPrivate *priv;
3650 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (fidata->window);
3651 priv->fetching_images--;
3652 gtk_widget_queue_draw (fidata->msg_view);
3653 update_progress_hint (MODEST_MSG_VIEW_WINDOW (fidata->window));
3655 gdk_threads_leave ();
3657 g_object_unref (fidata->msg_view);
3658 g_object_unref (fidata->window);
3659 g_slice_free (FetchImageData, fidata);
3664 on_fetch_image_thread (gpointer userdata)
3666 FetchImageData *fidata = (FetchImageData *) userdata;
3667 TnyStreamCache *cache;
3668 TnyStream *cache_stream;
3670 cache = modest_runtime_get_images_cache ();
3672 tny_stream_cache_get_stream (cache,
3674 (TnyStreamCacheOpenStreamFetcher) fetch_image_open_stream,
3675 (gpointer) fidata->uri);
3676 g_free (fidata->cache_id);
3677 g_free (fidata->uri);
3679 if (cache_stream != NULL) {
3682 while (G_LIKELY (!tny_stream_is_eos (cache_stream))) {
3685 nb_read = tny_stream_read (cache_stream, buffer, sizeof (buffer));
3686 if (G_UNLIKELY (nb_read < 0)) {
3688 } else if (G_LIKELY (nb_read > 0)) {
3689 gssize nb_written = 0;
3691 while (G_UNLIKELY (nb_written < nb_read)) {
3694 len = tny_stream_write (fidata->output_stream, buffer + nb_written,
3695 nb_read - nb_written);
3696 if (G_UNLIKELY (len < 0))
3702 tny_stream_close (cache_stream);
3703 g_object_unref (cache_stream);
3706 tny_stream_close (fidata->output_stream);
3707 g_object_unref (fidata->output_stream);
3709 g_idle_add (on_fetch_image_idle_refresh_view, fidata);
3715 on_fetch_image (ModestMsgView *msgview,
3718 ModestMsgViewWindow *window)
3720 const gchar *current_account;
3721 ModestMsgViewWindowPrivate *priv;
3722 FetchImageData *fidata;
3724 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
3726 current_account = modest_window_get_active_account (MODEST_WINDOW (window));
3728 fidata = g_slice_new0 (FetchImageData);
3729 fidata->msg_view = g_object_ref (msgview);
3730 fidata->window = g_object_ref (window);
3731 fidata->uri = g_strdup (uri);
3732 fidata->cache_id = modest_images_cache_get_id (current_account, uri);
3733 fidata->output_stream = g_object_ref (stream);
3735 priv->fetching_images++;
3736 if (g_thread_create (on_fetch_image_thread, fidata, FALSE, NULL) == NULL) {
3737 g_object_unref (fidata->output_stream);
3738 g_free (fidata->cache_id);
3739 g_free (fidata->uri);
3740 g_object_unref (fidata->msg_view);
3741 g_slice_free (FetchImageData, fidata);
3742 tny_stream_close (stream);
3743 priv->fetching_images--;
3744 update_progress_hint (window);
3747 update_progress_hint (window);
3753 setup_menu (ModestMsgViewWindow *self)
3755 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW(self));
3757 /* Settings menu buttons */
3758 modest_hildon2_window_add_to_menu (MODEST_HILDON2_WINDOW (self), _("mcen_me_viewer_find"), NULL,
3759 APP_MENU_CALLBACK (modest_msg_view_window_show_find_toolbar),
3760 MODEST_DIMMING_CALLBACK (modest_ui_dimming_rules_on_find_in_msg));
3762 modest_hildon2_window_add_to_menu (MODEST_HILDON2_WINDOW (self),
3763 dngettext(GETTEXT_PACKAGE,
3764 "mcen_me_move_message",
3765 "mcen_me_move_messages",
3768 APP_MENU_CALLBACK (modest_ui_actions_on_move_to),
3769 MODEST_DIMMING_CALLBACK (modest_ui_dimming_rules_on_move_to));
3771 modest_hildon2_window_add_to_menu (MODEST_HILDON2_WINDOW (self), _("mcen_me_inbox_mark_as_read"), NULL,
3772 APP_MENU_CALLBACK (modest_ui_actions_on_mark_as_read),
3773 MODEST_DIMMING_CALLBACK (modest_ui_dimming_rules_on_mark_as_read_msg_in_view));
3775 modest_hildon2_window_add_to_menu (MODEST_HILDON2_WINDOW (self), _("mcen_me_inbox_mark_as_unread"), NULL,
3776 APP_MENU_CALLBACK (modest_ui_actions_on_mark_as_unread),
3777 MODEST_DIMMING_CALLBACK (modest_ui_dimming_rules_on_mark_as_unread_msg_in_view));
3779 modest_hildon2_window_add_to_menu (MODEST_HILDON2_WINDOW (self), _("mcen_me_viewer_save_attachments"), NULL,
3780 APP_MENU_CALLBACK (modest_ui_actions_save_attachments),
3781 MODEST_DIMMING_CALLBACK (modest_ui_dimming_rules_on_save_attachments));
3782 modest_hildon2_window_add_to_menu (MODEST_HILDON2_WINDOW (self), _("mcen_me_inbox_remove_attachments"), NULL,
3783 APP_MENU_CALLBACK (modest_ui_actions_remove_attachments),
3784 MODEST_DIMMING_CALLBACK (modest_ui_dimming_rules_on_remove_attachments));
3786 modest_hildon2_window_add_to_menu (MODEST_HILDON2_WINDOW (self), _("mcen_me_new_message"), "<Control>n",
3787 APP_MENU_CALLBACK (modest_ui_actions_on_new_msg),
3788 MODEST_DIMMING_CALLBACK (modest_ui_dimming_rules_on_new_msg));
3789 modest_hildon2_window_add_to_menu (MODEST_HILDON2_WINDOW (self), _("mcen_me_viewer_addtocontacts"), NULL,
3790 APP_MENU_CALLBACK (modest_ui_actions_add_to_contacts),
3791 MODEST_DIMMING_CALLBACK (modest_ui_dimming_rules_on_add_to_contacts));
3793 modest_hildon2_window_add_to_menu (MODEST_HILDON2_WINDOW (self), _("mcen_ti_message_properties"), NULL,
3794 APP_MENU_CALLBACK (modest_ui_actions_on_details),
3795 MODEST_DIMMING_CALLBACK (modest_ui_dimming_rules_on_details));
3799 modest_msg_view_window_add_to_contacts (ModestMsgViewWindow *self)
3801 ModestMsgViewWindowPrivate *priv;
3802 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
3803 GSList *recipients = NULL;
3806 msg = tny_msg_view_get_msg (TNY_MSG_VIEW (priv->msg_view));
3810 header = modest_msg_view_window_get_header (self);
3813 recipients = modest_tny_msg_header_get_all_recipients_list (header);
3814 g_object_unref (header);
3816 recipients = modest_tny_msg_get_all_recipients_list (msg);
3817 g_object_unref (msg);
3821 /* Offer the user to add recipients to the address book */
3822 modest_address_book_add_address_list_with_selector (recipients, (GtkWindow *) self);
3823 g_slist_foreach (recipients, (GFunc) g_free, NULL); g_slist_free (recipients);
3828 _modest_msg_view_window_map_event (GtkWidget *widget,
3832 ModestMsgViewWindow *self = (ModestMsgViewWindow *) userdata;
3834 update_progress_hint (self);
3840 modest_msg_view_window_fetch_images (ModestMsgViewWindow *self)
3842 ModestMsgViewWindowPrivate *priv;
3843 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
3845 modest_msg_view_request_fetch_images (MODEST_MSG_VIEW (priv->msg_view));
3849 modest_msg_view_window_has_blocked_external_images (ModestMsgViewWindow *self)
3851 ModestMsgViewWindowPrivate *priv;
3852 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
3854 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self), FALSE);
3856 return modest_msg_view_has_blocked_external_images (MODEST_MSG_VIEW (priv->msg_view));
3860 modest_msg_view_window_reload (ModestMsgViewWindow *self)
3862 ModestMsgViewWindowPrivate *priv;
3863 const gchar *msg_uid;
3864 TnyHeader *header = NULL;
3865 TnyFolder *folder = NULL;
3867 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self));
3869 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
3871 header = modest_msg_view_window_get_header (MODEST_MSG_VIEW_WINDOW (self));
3875 folder = tny_header_get_folder (header);
3876 g_object_unref (header);
3881 msg_uid = modest_msg_view_window_get_message_uid (self);
3883 GtkTreeRowReference *row_reference;
3885 if (priv->row_reference && gtk_tree_row_reference_valid (priv->row_reference)) {
3886 row_reference = priv->row_reference;
3888 row_reference = NULL;
3890 if (!message_reader (self, priv, NULL, msg_uid, folder, row_reference))
3891 g_warning ("Shouldn't happen, trying to reload a message failed");
3894 g_object_unref (folder);
3898 update_branding (ModestMsgViewWindow *self)
3900 const gchar *account;
3901 const gchar *mailbox;
3902 ModestAccountMgr *mgr;
3903 ModestProtocol *protocol = NULL;
3904 gchar *service_name = NULL;
3905 const GdkPixbuf *service_icon = NULL;
3906 ModestMsgViewWindowPrivate *priv;
3908 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
3910 account = modest_window_get_active_account (MODEST_WINDOW (self));
3911 mailbox = modest_window_get_active_mailbox (MODEST_WINDOW (self));
3913 mgr = modest_runtime_get_account_mgr ();
3915 if (modest_account_mgr_account_is_multimailbox (mgr, account, &protocol)) {
3916 if (MODEST_IS_ACCOUNT_PROTOCOL (protocol)) {
3917 service_name = modest_account_protocol_get_service_name (MODEST_ACCOUNT_PROTOCOL (protocol),
3919 service_icon = modest_account_protocol_get_service_icon (MODEST_ACCOUNT_PROTOCOL (protocol),
3920 account, mailbox, MODEST_ICON_SIZE_SMALL);
3924 modest_msg_view_set_branding (MODEST_MSG_VIEW (priv->msg_view), service_name, service_icon);
3925 g_free (service_name);
3929 sync_flags (ModestMsgViewWindow *self)
3931 TnyHeader *header = NULL;
3933 header = modest_msg_view_window_get_header (self);
3935 TnyMsg *msg = modest_msg_view_window_get_message (self);
3937 header = tny_msg_get_header (msg);
3938 g_object_unref (msg);
3943 TnyFolder *folder = tny_header_get_folder (header);
3946 ModestMailOperation *mail_op;
3948 /* Sync folder, we need this to save the seen flag */
3949 mail_op = modest_mail_operation_new (NULL);
3950 modest_mail_operation_queue_add (modest_runtime_get_mail_operation_queue (),
3952 modest_mail_operation_sync_folder (mail_op, folder, FALSE, NULL, NULL);
3953 g_object_unref (mail_op);
3954 g_object_unref (folder);
3956 g_object_unref (header);