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 g_idle_add ((GSourceFunc) idle_save_mime_part_show_result, info);
3059 g_thread_create ((GThreadFunc)save_mime_part_to_file, info, FALSE, NULL);
3064 save_mime_part_to_file_connect_idle (SaveMimePartInfo *info)
3067 TnyAccount *account;
3068 ModestMsgViewWindowPrivate *priv;
3070 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (info->window);
3072 is_merge = g_str_has_prefix (priv->msg_uid, "merge:");
3075 /* Get the account */
3077 account = tny_account_store_find_account (TNY_ACCOUNT_STORE (modest_runtime_get_account_store ()),
3080 modest_platform_connect_and_perform (GTK_WINDOW (info->window),
3082 TNY_ACCOUNT (account),
3083 (ModestConnectedPerformer) save_mime_part_to_file_connect_handler,
3089 save_mime_part_to_file (SaveMimePartInfo *info)
3091 GnomeVFSHandle *handle;
3093 SaveMimePartPair *pair = (SaveMimePartPair *) info->pairs->data;
3095 if (TNY_IS_CAMEL_BS_MIME_PART (pair->part) &&
3096 !tny_camel_bs_mime_part_is_fetched (TNY_CAMEL_BS_MIME_PART (pair->part))) {
3097 g_idle_add ((GSourceFunc) save_mime_part_to_file_connect_idle, info);
3101 info->result = gnome_vfs_create (&handle, pair->filename, GNOME_VFS_OPEN_WRITE, FALSE, 0644);
3102 if (info->result == GNOME_VFS_OK) {
3103 GError *error = NULL;
3104 gboolean decode_in_provider;
3106 ModestAccountMgr *mgr;
3107 const gchar *account;
3108 ModestProtocol *protocol = NULL;
3110 stream = tny_vfs_stream_new (handle);
3112 decode_in_provider = FALSE;
3113 mgr = modest_runtime_get_account_mgr ();
3114 account = modest_window_get_active_account (MODEST_WINDOW (info->window));
3115 if (modest_account_mgr_account_is_multimailbox (mgr, account, &protocol)) {
3116 if (MODEST_IS_ACCOUNT_PROTOCOL (protocol)) {
3117 decode_in_provider =
3118 modest_account_protocol_decode_part_to_stream (
3119 MODEST_ACCOUNT_PROTOCOL (protocol),
3127 if (!decode_in_provider)
3128 written = tny_mime_part_decode_to_stream (pair->part, stream, &error);
3131 g_warning ("modest: could not save attachment %s: %d (%s)\n", pair->filename, error?error->code:-1, error?error->message:"Unknown error");
3133 if ((error->domain == TNY_ERROR_DOMAIN) &&
3134 (error->code == TNY_IO_ERROR_WRITE) &&
3135 (errno == ENOSPC)) {
3136 info->result = GNOME_VFS_ERROR_NO_SPACE;
3138 info->result = GNOME_VFS_ERROR_IO;
3141 g_object_unref (G_OBJECT (stream));
3143 g_warning ("Could not create save attachment %s: %s\n",
3144 pair->filename, gnome_vfs_result_to_string (info->result));
3147 /* Go on saving remaining files */
3148 info->pairs = g_list_remove_link (info->pairs, info->pairs);
3149 if (info->pairs != NULL) {
3150 save_mime_part_to_file (info);
3152 g_idle_add ((GSourceFunc) idle_save_mime_part_show_result, info);
3159 save_mime_parts_to_file_with_checks (GtkWindow *parent,
3160 SaveMimePartInfo *info)
3162 gboolean is_ok = TRUE;
3163 gint replaced_files = 0;
3164 const GList *files = info->pairs;
3165 const GList *iter, *to_replace = NULL;
3167 for (iter = files; (iter != NULL) && (replaced_files < 2); iter = g_list_next(iter)) {
3168 SaveMimePartPair *pair = iter->data;
3169 gchar *unescaped = g_uri_unescape_string (pair->filename, NULL);
3171 if (modest_utils_file_exists (unescaped)) {
3173 if (replaced_files == 1)
3178 if (replaced_files) {
3181 if (replaced_files == 1) {
3182 SaveMimePartPair *pair = to_replace->data;
3183 const gchar *basename = strrchr (pair->filename, G_DIR_SEPARATOR) + 1;
3184 gchar *escaped_basename, *message;
3186 escaped_basename = g_uri_unescape_string (basename, NULL);
3187 message = g_strdup_printf ("%s\n%s",
3188 _FM("docm_nc_replace_file"),
3189 (escaped_basename) ? escaped_basename : "");
3190 response = modest_platform_run_confirmation_dialog (parent, message);
3192 g_free (escaped_basename);
3194 response = modest_platform_run_confirmation_dialog (parent,
3195 _FM("docm_nc_replace_multiple"));
3197 if (response != GTK_RESPONSE_OK)
3202 save_mime_part_info_free (info, TRUE);
3204 g_thread_create ((GThreadFunc)save_mime_part_to_file, info, FALSE, NULL);
3209 typedef struct _SaveAttachmentsInfo {
3210 TnyList *attachments_list;
3211 ModestMsgViewWindow *window;
3212 } SaveAttachmentsInfo;
3215 save_attachments_response (GtkDialog *dialog,
3219 TnyList *mime_parts;
3221 GList *files_to_save = NULL;
3222 gchar *current_folder;
3223 SaveAttachmentsInfo *sa_info = (SaveAttachmentsInfo *) user_data;
3225 mime_parts = TNY_LIST (sa_info->attachments_list);
3227 if (arg1 != GTK_RESPONSE_OK)
3230 chooser_uri = gtk_file_chooser_get_uri (GTK_FILE_CHOOSER (dialog));
3231 current_folder = gtk_file_chooser_get_current_folder_uri (GTK_FILE_CHOOSER (dialog));
3232 if (current_folder && *current_folder != '\0') {
3234 modest_conf_set_string (modest_runtime_get_conf (), MODEST_CONF_LATEST_SAVE_ATTACHMENT_PATH,
3235 current_folder,&err);
3237 g_debug ("Error storing latest used folder: %s", err->message);
3241 g_free (current_folder);
3243 if (!modest_utils_folder_writable (chooser_uri)) {
3244 const gchar *err_msg;
3246 #ifdef MODEST_PLATFORM_MAEMO
3247 if (modest_maemo_utils_in_usb_mode ()) {
3248 err_msg = dgettext ("hildon-status-bar-usb", "usbh_ib_mmc_usb_connected");
3250 err_msg = _FM("sfil_ib_readonly_location");
3253 err_msg = _FM("sfil_ib_readonly_location");
3255 hildon_banner_show_information (NULL, NULL, err_msg);
3259 iter = tny_list_create_iterator (mime_parts);
3260 while (!tny_iterator_is_done (iter)) {
3261 TnyMimePart *mime_part = (TnyMimePart *) tny_iterator_get_current (iter);
3263 if ((modest_tny_mime_part_is_attachment_for_modest (mime_part)) &&
3264 !tny_mime_part_is_purged (mime_part) &&
3265 (tny_mime_part_get_filename (mime_part) != NULL)) {
3266 SaveMimePartPair *pair;
3268 pair = g_slice_new0 (SaveMimePartPair);
3270 if (tny_list_get_length (mime_parts) > 1) {
3272 gnome_vfs_escape_slashes (tny_mime_part_get_filename (mime_part));
3273 pair->filename = g_build_filename (chooser_uri, escaped, NULL);
3276 pair->filename = g_strdup (chooser_uri);
3278 pair->part = mime_part;
3279 files_to_save = g_list_prepend (files_to_save, pair);
3281 tny_iterator_next (iter);
3283 g_object_unref (iter);
3286 if (files_to_save != NULL) {
3287 SaveMimePartInfo *info = g_slice_new0 (SaveMimePartInfo);
3288 info->pairs = files_to_save;
3289 info->result = TRUE;
3290 info->uri = g_strdup (chooser_uri);
3291 info->window = g_object_ref (sa_info->window);
3292 save_mime_parts_to_file_with_checks ((GtkWindow *) dialog, info);
3294 g_free (chooser_uri);
3297 /* Free and close the dialog */
3298 g_object_unref (mime_parts);
3299 g_object_unref (sa_info->window);
3300 g_slice_free (SaveAttachmentsInfo, sa_info);
3301 gtk_widget_destroy (GTK_WIDGET (dialog));
3305 modest_msg_view_window_save_attachments (ModestMsgViewWindow *window,
3306 TnyList *mime_parts)
3308 ModestMsgViewWindowPrivate *priv;
3309 GtkWidget *save_dialog = NULL;
3310 gchar *conf_folder = NULL;
3311 gchar *filename = NULL;
3312 gchar *save_multiple_str = NULL;
3313 const gchar *root_folder = "file:///";
3315 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window));
3316 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
3318 if (mime_parts == NULL) {
3319 /* In Hildon 2.2 save and delete operate over all the attachments as there's no
3320 * selection available */
3321 mime_parts = modest_msg_view_get_attachments (MODEST_MSG_VIEW (priv->msg_view));
3322 if (mime_parts && !modest_maemo_utils_select_attachments (GTK_WINDOW (window), mime_parts, FALSE)) {
3323 g_object_unref (mime_parts);
3326 if (mime_parts == NULL || tny_list_get_length (mime_parts) == 0) {
3328 g_object_unref (mime_parts);
3334 g_object_ref (mime_parts);
3337 /* prepare dialog */
3338 if (tny_list_get_length (mime_parts) == 1) {
3340 /* only one attachment selected */
3341 iter = tny_list_create_iterator (mime_parts);
3342 TnyMimePart *mime_part = (TnyMimePart *) tny_iterator_get_current (iter);
3343 g_object_unref (iter);
3344 if (!modest_tny_mime_part_is_msg (mime_part) &&
3345 modest_tny_mime_part_is_attachment_for_modest (mime_part) &&
3346 !tny_mime_part_is_purged (mime_part)) {
3347 filename = g_strdup (tny_mime_part_get_filename (mime_part));
3349 /* TODO: show any error? */
3350 g_warning ("%s: Tried to save a non-file attachment", __FUNCTION__);
3351 g_object_unref (mime_parts);
3354 g_object_unref (mime_part);
3356 gint num = tny_list_get_length (mime_parts);
3357 save_multiple_str = g_strdup_printf (dngettext("hildon-fm",
3358 "sfil_va_number_of_objects_attachment",
3359 "sfil_va_number_of_objects_attachments",
3363 save_dialog = hildon_file_chooser_dialog_new (GTK_WINDOW (window),
3364 GTK_FILE_CHOOSER_ACTION_SAVE);
3366 /* Get last used folder */
3367 conf_folder = modest_conf_get_string (modest_runtime_get_conf (),
3368 MODEST_CONF_LATEST_SAVE_ATTACHMENT_PATH, NULL);
3370 /* File chooser stops working if we select "file:///" as current folder */
3371 if (conf_folder && g_ascii_strcasecmp (root_folder, conf_folder) != 0) {
3372 g_free (conf_folder);
3376 if (conf_folder && conf_folder[0] != '\0') {
3377 gtk_file_chooser_set_current_folder_uri (GTK_FILE_CHOOSER (save_dialog), conf_folder);
3380 /* Set the default folder to documents folder */
3381 docs_folder = (gchar *) g_strdup(g_get_user_special_dir (G_USER_DIRECTORY_DOCUMENTS));
3384 docs_folder = g_build_filename (g_getenv (MYDOCS_ENV), DOCS_FOLDER, NULL);
3386 gtk_file_chooser_set_current_folder (GTK_FILE_CHOOSER (save_dialog), docs_folder);
3387 g_free (docs_folder);
3389 g_free (conf_folder);
3393 gtk_file_chooser_set_current_name (GTK_FILE_CHOOSER (save_dialog),
3398 /* if multiple, set multiple string */
3399 if (save_multiple_str) {
3400 g_object_set (G_OBJECT (save_dialog), "save-multiple", save_multiple_str, NULL);
3401 gtk_window_set_title (GTK_WINDOW (save_dialog), _FM("sfil_ti_save_objects_files"));
3402 g_free (save_multiple_str);
3405 /* We must run this asynchronously, because the hildon dialog
3406 performs a gtk_dialog_run by itself which leads to gdk
3408 SaveAttachmentsInfo *sa_info;
3409 sa_info = g_slice_new (SaveAttachmentsInfo);
3410 sa_info->attachments_list = mime_parts;
3411 sa_info->window = g_object_ref (window);
3412 g_signal_connect (save_dialog, "response",
3413 G_CALLBACK (save_attachments_response), sa_info);
3415 gtk_widget_show_all (save_dialog);
3419 show_remove_attachment_information (gpointer userdata)
3421 ModestMsgViewWindow *window = (ModestMsgViewWindow *) userdata;
3422 ModestMsgViewWindowPrivate *priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
3424 /* We're outside the main lock */
3425 gdk_threads_enter ();
3427 if (priv->remove_attachment_banner != NULL) {
3428 gtk_widget_destroy (priv->remove_attachment_banner);
3429 g_object_unref (priv->remove_attachment_banner);
3432 priv->remove_attachment_banner = g_object_ref (
3433 hildon_banner_show_animation (NULL, NULL, _("mcen_me_inbox_remove_attachments")));
3435 gdk_threads_leave ();
3441 modest_msg_view_window_remove_attachments (ModestMsgViewWindow *window, gboolean get_all)
3443 ModestMsgViewWindowPrivate *priv;
3444 TnyList *mime_parts = NULL, *tmp;
3445 gchar *confirmation_message;
3451 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window));
3452 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
3454 /* In hildon 2.2 we ignore the get_all flag as we always get all attachments. This is
3455 * because we don't have selection
3457 mime_parts = modest_msg_view_get_attachments (MODEST_MSG_VIEW (priv->msg_view));
3459 /* Remove already purged messages from mime parts list. We use
3460 a copy of the list to remove items in the original one */
3461 tmp = tny_list_copy (mime_parts);
3462 iter = tny_list_create_iterator (tmp);
3463 while (!tny_iterator_is_done (iter)) {
3464 TnyMimePart *part = TNY_MIME_PART (tny_iterator_get_current (iter));
3465 if (tny_mime_part_is_purged (part))
3466 tny_list_remove (mime_parts, (GObject *) part);
3468 g_object_unref (part);
3469 tny_iterator_next (iter);
3471 g_object_unref (tmp);
3472 g_object_unref (iter);
3474 if (!modest_maemo_utils_select_attachments (GTK_WINDOW (window), mime_parts, TRUE) ||
3475 tny_list_get_length (mime_parts) == 0) {
3476 g_object_unref (mime_parts);
3480 n_attachments = tny_list_get_length (mime_parts);
3481 if (n_attachments == 1) {
3485 iter = tny_list_create_iterator (mime_parts);
3486 part = (TnyMimePart *) tny_iterator_get_current (iter);
3487 g_object_unref (iter);
3488 if (modest_tny_mime_part_is_msg (part)) {
3490 header = tny_msg_get_header (TNY_MSG (part));
3491 filename = tny_header_dup_subject (header);
3492 g_object_unref (header);
3493 if (filename == NULL)
3494 filename = g_strdup (_("mail_va_no_subject"));
3496 filename = g_strdup (tny_mime_part_get_filename (TNY_MIME_PART (part)));
3498 confirmation_message = g_strdup_printf (_("mcen_nc_purge_file_text"), filename);
3500 g_object_unref (part);
3502 confirmation_message = g_strdup_printf (ngettext("mcen_nc_purge_file_text",
3503 "mcen_nc_purge_files_text",
3504 n_attachments), n_attachments);
3506 response = modest_platform_run_confirmation_dialog (GTK_WINDOW (window),
3507 confirmation_message);
3508 g_free (confirmation_message);
3510 if (response != GTK_RESPONSE_OK) {
3511 g_object_unref (mime_parts);
3515 priv->purge_timeout = g_timeout_add (2000, show_remove_attachment_information, window);
3517 iter = tny_list_create_iterator (mime_parts);
3518 while (!tny_iterator_is_done (iter)) {
3521 part = (TnyMimePart *) tny_iterator_get_current (iter);
3522 tny_mime_part_set_purged (TNY_MIME_PART (part));
3523 g_object_unref (part);
3524 tny_iterator_next (iter);
3526 g_object_unref (iter);
3528 msg = tny_msg_view_get_msg (TNY_MSG_VIEW (priv->msg_view));
3529 tny_msg_view_clear (TNY_MSG_VIEW (priv->msg_view));
3530 tny_msg_rewrite_cache (msg);
3531 tny_msg_view_set_msg (TNY_MSG_VIEW (priv->msg_view), msg);
3532 g_object_unref (msg);
3533 update_branding (MODEST_MSG_VIEW_WINDOW (window));
3535 g_object_unref (mime_parts);
3537 if (priv->purge_timeout > 0) {
3538 g_source_remove (priv->purge_timeout);
3539 priv->purge_timeout = 0;
3542 if (priv->remove_attachment_banner) {
3543 gtk_widget_destroy (priv->remove_attachment_banner);
3544 g_object_unref (priv->remove_attachment_banner);
3545 priv->remove_attachment_banner = NULL;
3551 update_window_title (ModestMsgViewWindow *window)
3553 ModestMsgViewWindowPrivate *priv;
3555 TnyHeader *header = NULL;
3556 gchar *subject = NULL;
3558 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
3560 /* Note that if the window is closed while we're retrieving
3561 the message, this widget could de deleted */
3562 if (!priv->msg_view)
3565 msg = tny_msg_view_get_msg (TNY_MSG_VIEW (priv->msg_view));
3567 if (priv->other_body) {
3570 description = modest_tny_mime_part_get_header_value (priv->other_body, "Content-Description");
3572 g_strstrip (description);
3573 subject = description;
3575 } else if (msg != NULL) {
3576 header = tny_msg_get_header (msg);
3577 subject = tny_header_dup_subject (header);
3578 g_object_unref (header);
3579 g_object_unref (msg);
3582 if ((subject == NULL)||(subject[0] == '\0')) {
3584 subject = g_strdup (_("mail_va_no_subject"));
3587 gtk_window_set_title (GTK_WINDOW (window), subject);
3592 on_move_focus (GtkWidget *widget,
3593 GtkDirectionType direction,
3596 g_signal_stop_emission_by_name (G_OBJECT (widget), "move-focus");
3600 fetch_image_open_stream (TnyStreamCache *self, gint64 *expected_size, gchar *uri)
3602 GnomeVFSResult result;
3603 GnomeVFSHandle *handle = NULL;
3604 GnomeVFSFileInfo *info = NULL;
3607 result = gnome_vfs_open (&handle, uri, GNOME_VFS_OPEN_READ);
3608 if (result != GNOME_VFS_OK) {
3613 info = gnome_vfs_file_info_new ();
3614 result = gnome_vfs_get_file_info_from_handle (handle, info, GNOME_VFS_FILE_INFO_DEFAULT);
3615 if (result != GNOME_VFS_OK || ! (info->valid_fields & GNOME_VFS_FILE_INFO_FIELDS_SIZE)) {
3616 /* We put a "safe" default size for going to cache */
3617 *expected_size = (300*1024);
3619 *expected_size = info->size;
3621 gnome_vfs_file_info_unref (info);
3623 stream = tny_vfs_stream_new (handle);
3632 TnyStream *output_stream;
3633 GtkWidget *msg_view;
3638 on_fetch_image_idle_refresh_view (gpointer userdata)
3641 FetchImageData *fidata = (FetchImageData *) userdata;
3643 gdk_threads_enter ();
3644 if (GTK_WIDGET_DRAWABLE (fidata->msg_view)) {
3645 ModestMsgViewWindowPrivate *priv;
3647 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (fidata->window);
3648 priv->fetching_images--;
3649 gtk_widget_queue_draw (fidata->msg_view);
3650 update_progress_hint (MODEST_MSG_VIEW_WINDOW (fidata->window));
3652 gdk_threads_leave ();
3654 g_object_unref (fidata->msg_view);
3655 g_object_unref (fidata->window);
3656 g_slice_free (FetchImageData, fidata);
3661 on_fetch_image_thread (gpointer userdata)
3663 FetchImageData *fidata = (FetchImageData *) userdata;
3664 TnyStreamCache *cache;
3665 TnyStream *cache_stream;
3667 cache = modest_runtime_get_images_cache ();
3669 tny_stream_cache_get_stream (cache,
3671 (TnyStreamCacheOpenStreamFetcher) fetch_image_open_stream,
3672 (gpointer) fidata->uri);
3673 g_free (fidata->cache_id);
3674 g_free (fidata->uri);
3676 if (cache_stream != NULL) {
3679 while (G_LIKELY (!tny_stream_is_eos (cache_stream))) {
3682 nb_read = tny_stream_read (cache_stream, buffer, sizeof (buffer));
3683 if (G_UNLIKELY (nb_read < 0)) {
3685 } else if (G_LIKELY (nb_read > 0)) {
3686 gssize nb_written = 0;
3688 while (G_UNLIKELY (nb_written < nb_read)) {
3691 len = tny_stream_write (fidata->output_stream, buffer + nb_written,
3692 nb_read - nb_written);
3693 if (G_UNLIKELY (len < 0))
3699 tny_stream_close (cache_stream);
3700 g_object_unref (cache_stream);
3703 tny_stream_close (fidata->output_stream);
3704 g_object_unref (fidata->output_stream);
3706 g_idle_add (on_fetch_image_idle_refresh_view, fidata);
3712 on_fetch_image (ModestMsgView *msgview,
3715 ModestMsgViewWindow *window)
3717 const gchar *current_account;
3718 ModestMsgViewWindowPrivate *priv;
3719 FetchImageData *fidata;
3721 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
3723 current_account = modest_window_get_active_account (MODEST_WINDOW (window));
3725 fidata = g_slice_new0 (FetchImageData);
3726 fidata->msg_view = g_object_ref (msgview);
3727 fidata->window = g_object_ref (window);
3728 fidata->uri = g_strdup (uri);
3729 fidata->cache_id = modest_images_cache_get_id (current_account, uri);
3730 fidata->output_stream = g_object_ref (stream);
3732 priv->fetching_images++;
3733 if (g_thread_create (on_fetch_image_thread, fidata, FALSE, NULL) == NULL) {
3734 g_object_unref (fidata->output_stream);
3735 g_free (fidata->cache_id);
3736 g_free (fidata->uri);
3737 g_object_unref (fidata->msg_view);
3738 g_slice_free (FetchImageData, fidata);
3739 tny_stream_close (stream);
3740 priv->fetching_images--;
3741 update_progress_hint (window);
3744 update_progress_hint (window);
3750 setup_menu (ModestMsgViewWindow *self)
3752 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW(self));
3754 /* Settings menu buttons */
3755 modest_hildon2_window_add_to_menu (MODEST_HILDON2_WINDOW (self), _("mcen_me_viewer_find"), NULL,
3756 APP_MENU_CALLBACK (modest_msg_view_window_show_find_toolbar),
3757 MODEST_DIMMING_CALLBACK (modest_ui_dimming_rules_on_find_in_msg));
3759 modest_hildon2_window_add_to_menu (MODEST_HILDON2_WINDOW (self),
3760 dngettext(GETTEXT_PACKAGE,
3761 "mcen_me_move_message",
3762 "mcen_me_move_messages",
3765 APP_MENU_CALLBACK (modest_ui_actions_on_move_to),
3766 MODEST_DIMMING_CALLBACK (modest_ui_dimming_rules_on_move_to));
3768 modest_hildon2_window_add_to_menu (MODEST_HILDON2_WINDOW (self), _("mcen_me_inbox_mark_as_read"), NULL,
3769 APP_MENU_CALLBACK (modest_ui_actions_on_mark_as_read),
3770 MODEST_DIMMING_CALLBACK (modest_ui_dimming_rules_on_mark_as_read_msg_in_view));
3772 modest_hildon2_window_add_to_menu (MODEST_HILDON2_WINDOW (self), _("mcen_me_inbox_mark_as_unread"), NULL,
3773 APP_MENU_CALLBACK (modest_ui_actions_on_mark_as_unread),
3774 MODEST_DIMMING_CALLBACK (modest_ui_dimming_rules_on_mark_as_unread_msg_in_view));
3776 modest_hildon2_window_add_to_menu (MODEST_HILDON2_WINDOW (self), _("mcen_me_viewer_save_attachments"), NULL,
3777 APP_MENU_CALLBACK (modest_ui_actions_save_attachments),
3778 MODEST_DIMMING_CALLBACK (modest_ui_dimming_rules_on_save_attachments));
3779 modest_hildon2_window_add_to_menu (MODEST_HILDON2_WINDOW (self), _("mcen_me_inbox_remove_attachments"), NULL,
3780 APP_MENU_CALLBACK (modest_ui_actions_remove_attachments),
3781 MODEST_DIMMING_CALLBACK (modest_ui_dimming_rules_on_remove_attachments));
3783 modest_hildon2_window_add_to_menu (MODEST_HILDON2_WINDOW (self), _("mcen_me_new_message"), "<Control>n",
3784 APP_MENU_CALLBACK (modest_ui_actions_on_new_msg),
3785 MODEST_DIMMING_CALLBACK (modest_ui_dimming_rules_on_new_msg));
3786 modest_hildon2_window_add_to_menu (MODEST_HILDON2_WINDOW (self), _("mcen_me_viewer_addtocontacts"), NULL,
3787 APP_MENU_CALLBACK (modest_ui_actions_add_to_contacts),
3788 MODEST_DIMMING_CALLBACK (modest_ui_dimming_rules_on_add_to_contacts));
3790 modest_hildon2_window_add_to_menu (MODEST_HILDON2_WINDOW (self), _("mcen_ti_message_properties"), NULL,
3791 APP_MENU_CALLBACK (modest_ui_actions_on_details),
3792 MODEST_DIMMING_CALLBACK (modest_ui_dimming_rules_on_details));
3796 modest_msg_view_window_add_to_contacts (ModestMsgViewWindow *self)
3798 ModestMsgViewWindowPrivate *priv;
3799 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
3800 GSList *recipients = NULL;
3803 msg = tny_msg_view_get_msg (TNY_MSG_VIEW (priv->msg_view));
3807 header = modest_msg_view_window_get_header (self);
3810 recipients = modest_tny_msg_header_get_all_recipients_list (header);
3811 g_object_unref (header);
3813 recipients = modest_tny_msg_get_all_recipients_list (msg);
3814 g_object_unref (msg);
3818 /* Offer the user to add recipients to the address book */
3819 modest_address_book_add_address_list_with_selector (recipients, (GtkWindow *) self);
3820 g_slist_foreach (recipients, (GFunc) g_free, NULL); g_slist_free (recipients);
3825 _modest_msg_view_window_map_event (GtkWidget *widget,
3829 ModestMsgViewWindow *self = (ModestMsgViewWindow *) userdata;
3831 update_progress_hint (self);
3837 modest_msg_view_window_fetch_images (ModestMsgViewWindow *self)
3839 ModestMsgViewWindowPrivate *priv;
3840 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
3842 modest_msg_view_request_fetch_images (MODEST_MSG_VIEW (priv->msg_view));
3846 modest_msg_view_window_has_blocked_external_images (ModestMsgViewWindow *self)
3848 ModestMsgViewWindowPrivate *priv;
3849 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
3851 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self), FALSE);
3853 return modest_msg_view_has_blocked_external_images (MODEST_MSG_VIEW (priv->msg_view));
3857 modest_msg_view_window_reload (ModestMsgViewWindow *self)
3859 ModestMsgViewWindowPrivate *priv;
3860 const gchar *msg_uid;
3861 TnyHeader *header = NULL;
3862 TnyFolder *folder = NULL;
3864 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self));
3866 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
3868 header = modest_msg_view_window_get_header (MODEST_MSG_VIEW_WINDOW (self));
3872 folder = tny_header_get_folder (header);
3873 g_object_unref (header);
3878 msg_uid = modest_msg_view_window_get_message_uid (self);
3880 GtkTreeRowReference *row_reference;
3882 if (priv->row_reference && gtk_tree_row_reference_valid (priv->row_reference)) {
3883 row_reference = priv->row_reference;
3885 row_reference = NULL;
3887 if (!message_reader (self, priv, NULL, msg_uid, folder, row_reference))
3888 g_warning ("Shouldn't happen, trying to reload a message failed");
3891 g_object_unref (folder);
3895 update_branding (ModestMsgViewWindow *self)
3897 const gchar *account;
3898 const gchar *mailbox;
3899 ModestAccountMgr *mgr;
3900 ModestProtocol *protocol = NULL;
3901 gchar *service_name = NULL;
3902 const GdkPixbuf *service_icon = NULL;
3903 ModestMsgViewWindowPrivate *priv;
3905 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
3907 account = modest_window_get_active_account (MODEST_WINDOW (self));
3908 mailbox = modest_window_get_active_mailbox (MODEST_WINDOW (self));
3910 mgr = modest_runtime_get_account_mgr ();
3912 if (modest_account_mgr_account_is_multimailbox (mgr, account, &protocol)) {
3913 if (MODEST_IS_ACCOUNT_PROTOCOL (protocol)) {
3914 service_name = modest_account_protocol_get_service_name (MODEST_ACCOUNT_PROTOCOL (protocol),
3916 service_icon = modest_account_protocol_get_service_icon (MODEST_ACCOUNT_PROTOCOL (protocol),
3917 account, mailbox, MODEST_ICON_SIZE_SMALL);
3921 modest_msg_view_set_branding (MODEST_MSG_VIEW (priv->msg_view), service_name, service_icon);
3922 g_free (service_name);
3926 sync_flags (ModestMsgViewWindow *self)
3928 TnyHeader *header = NULL;
3930 header = modest_msg_view_window_get_header (self);
3932 TnyMsg *msg = modest_msg_view_window_get_message (self);
3934 header = tny_msg_get_header (msg);
3935 g_object_unref (msg);
3940 TnyFolder *folder = tny_header_get_folder (header);
3943 ModestMailOperation *mail_op;
3945 /* Sync folder, we need this to save the seen flag */
3946 mail_op = modest_mail_operation_new (NULL);
3947 modest_mail_operation_queue_add (modest_runtime_get_mail_operation_queue (),
3949 modest_mail_operation_sync_folder (mail_op, folder, FALSE, NULL, NULL);
3950 g_object_unref (mail_op);
3951 g_object_unref (folder);
3953 g_object_unref (header);