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 #include <X11/Xatom.h>
76 #include <X11/XKBlib.h>
77 #include <X11/Xdmcp.h>
79 #define MYDOCS_ENV "MYDOCSDIR"
80 #define DOCS_FOLDER ".documents"
82 typedef struct _ModestMsgViewWindowPrivate ModestMsgViewWindowPrivate;
83 struct _ModestMsgViewWindowPrivate {
86 GtkWidget *main_scroll;
87 GtkWidget *find_toolbar;
90 /* Progress observers */
91 GSList *progress_widgets;
94 GtkWidget *prev_toolitem;
95 GtkWidget *next_toolitem;
96 gboolean progress_hint;
99 /* Optimized view enabled */
100 gboolean optimized_view;
102 /* Whether this was created via the *_new_for_search_result() function. */
103 gboolean is_search_result;
105 /* Whether the message is in outbox */
108 /* A reference to the @model of the header view
109 * to allow selecting previous/next messages,
110 * if the message is currently selected in the header view.
112 const gchar *header_folder_id;
113 GtkTreeModel *header_model;
114 GtkTreeRowReference *row_reference;
115 GtkTreeRowReference *next_row_reference;
117 gulong clipboard_change_handler;
118 gulong queue_change_handler;
119 gulong account_removed_handler;
120 gulong row_changed_handler;
121 gulong row_deleted_handler;
122 gulong row_inserted_handler;
123 gulong rows_reordered_handler;
124 gulong fetch_image_redraw_handler;
127 GtkWidget *remove_attachment_banner;
130 TnyMimePart *other_body;
136 static void modest_msg_view_window_class_init (ModestMsgViewWindowClass *klass);
137 static void modest_msg_view_window_init (ModestMsgViewWindow *obj);
138 static void modest_header_view_observer_init (ModestHeaderViewObserverIface *iface_class);
139 static void modest_msg_view_window_finalize (GObject *obj);
140 static void modest_msg_view_window_show_find_toolbar (GtkWidget *obj, gpointer data);
141 static void modest_msg_view_window_find_toolbar_close (GtkWidget *widget,
142 ModestMsgViewWindow *obj);
143 static void modest_msg_view_window_find_toolbar_search (GtkWidget *widget,
144 ModestMsgViewWindow *obj);
145 static void modest_msg_view_window_toggle_find_toolbar (GtkWidget *obj,
147 static void modest_msg_view_window_disconnect_signals (ModestWindow *self);
149 static gdouble modest_msg_view_window_get_zoom (ModestWindow *window);
150 static void modest_msg_view_window_set_zoom (ModestWindow *window,
152 static gboolean modest_msg_view_window_zoom_minus (ModestWindow *window);
153 static gboolean modest_msg_view_window_zoom_plus (ModestWindow *window);
154 static gboolean modest_msg_view_window_key_event (GtkWidget *window,
157 static void modest_msg_view_window_update_priority (ModestMsgViewWindow *window);
159 static void modest_msg_view_window_show_toolbar (ModestWindow *window,
160 gboolean show_toolbar);
162 static void modest_msg_view_window_clipboard_owner_change (GtkClipboard *clipboard,
164 ModestMsgViewWindow *window);
166 static void modest_msg_view_window_on_row_changed (GtkTreeModel *header_model,
169 ModestMsgViewWindow *window);
171 static void modest_msg_view_window_on_row_deleted (GtkTreeModel *header_model,
173 ModestMsgViewWindow *window);
175 static void modest_msg_view_window_on_row_inserted (GtkTreeModel *header_model,
176 GtkTreePath *tree_path,
177 GtkTreeIter *tree_iter,
178 ModestMsgViewWindow *window);
180 static void modest_msg_view_window_on_row_reordered (GtkTreeModel *header_model,
184 ModestMsgViewWindow *window);
186 static void modest_msg_view_window_update_model_replaced (ModestHeaderViewObserver *window,
188 const gchar *tny_folder_id);
190 static void on_queue_changed (ModestMailOperationQueue *queue,
191 ModestMailOperation *mail_op,
192 ModestMailOperationQueueNotification type,
193 ModestMsgViewWindow *self);
195 static void on_account_removed (TnyAccountStore *account_store,
199 static void on_move_focus (GtkWidget *widget,
200 GtkDirectionType direction,
203 static void view_msg_cb (ModestMailOperation *mail_op,
210 static void set_progress_hint (ModestMsgViewWindow *self,
213 static void update_window_title (ModestMsgViewWindow *window);
215 static void init_window (ModestMsgViewWindow *obj);
217 static gboolean msg_is_visible (TnyHeader *header, gboolean check_outbox);
219 static void check_dimming_rules_after_change (ModestMsgViewWindow *window);
221 static gboolean on_fetch_image (ModestMsgView *msgview,
224 ModestMsgViewWindow *window);
226 static gboolean modest_msg_view_window_scroll_child (ModestMsgViewWindow *self,
227 GtkScrollType scroll_type,
230 static gboolean message_reader (ModestMsgViewWindow *window,
231 ModestMsgViewWindowPrivate *priv,
233 const gchar *msg_uid,
235 GtkTreeRowReference *row_reference);
237 static void setup_menu (ModestMsgViewWindow *self);
238 static gboolean _modest_msg_view_window_map_event (GtkWidget *widget,
241 static void update_branding (ModestMsgViewWindow *self);
242 static void sync_flags (ModestMsgViewWindow *self);
243 static gboolean on_handle_calendar (ModestMsgView *msgview, TnyMimePart *calendar_part,
244 GtkContainer *container, ModestMsgViewWindow *self);
246 static gboolean on_realize (GtkWidget *widget,
249 /* list my signals */
256 static const GtkToggleActionEntry msg_view_toggle_action_entries [] = {
257 { "FindInMessage", MODEST_TOOLBAR_ICON_FIND, N_("qgn_toolb_gene_find"), "<CTRL>F", NULL, G_CALLBACK (modest_msg_view_window_toggle_find_toolbar), FALSE },
260 #define MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE((o), \
261 MODEST_TYPE_MSG_VIEW_WINDOW, \
262 ModestMsgViewWindowPrivate))
264 static GtkWindowClass *parent_class = NULL;
266 /* uncomment the following if you have defined any signals */
267 static guint signals[LAST_SIGNAL] = {0};
270 modest_msg_view_window_get_type (void)
272 static GType my_type = 0;
274 static const GTypeInfo my_info = {
275 sizeof(ModestMsgViewWindowClass),
276 NULL, /* base init */
277 NULL, /* base finalize */
278 (GClassInitFunc) modest_msg_view_window_class_init,
279 NULL, /* class finalize */
280 NULL, /* class data */
281 sizeof(ModestMsgViewWindow),
283 (GInstanceInitFunc) modest_msg_view_window_init,
286 my_type = g_type_register_static (MODEST_TYPE_HILDON2_WINDOW,
287 "ModestMsgViewWindow",
290 static const GInterfaceInfo modest_header_view_observer_info =
292 (GInterfaceInitFunc) modest_header_view_observer_init,
293 NULL, /* interface_finalize */
294 NULL /* interface_data */
297 g_type_add_interface_static (my_type,
298 MODEST_TYPE_HEADER_VIEW_OBSERVER,
299 &modest_header_view_observer_info);
305 save_state (ModestWindow *self)
307 modest_widget_memory_save (modest_runtime_get_conf (),
309 MODEST_CONF_MSG_VIEW_WINDOW_KEY);
313 modest_msg_view_window_scroll_child (ModestMsgViewWindow *self,
314 GtkScrollType scroll_type,
318 ModestMsgViewWindowPrivate *priv;
321 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(self);
323 switch (scroll_type) {
324 case GTK_SCROLL_STEP_UP:
327 case GTK_SCROLL_STEP_DOWN:
330 case GTK_SCROLL_PAGE_UP:
333 case GTK_SCROLL_PAGE_DOWN:
336 case GTK_SCROLL_START:
347 modest_maemo_utils_scroll_pannable((HildonPannableArea *) priv->main_scroll, 0, step);
349 return (gboolean) step;
353 add_scroll_binding (GtkBindingSet *binding_set,
355 GtkScrollType scroll)
357 guint keypad_keyval = keyval - GDK_Left + GDK_KP_Left;
359 gtk_binding_entry_add_signal (binding_set, keyval, 0,
361 GTK_TYPE_SCROLL_TYPE, scroll,
362 G_TYPE_BOOLEAN, FALSE);
363 gtk_binding_entry_add_signal (binding_set, keypad_keyval, 0,
365 GTK_TYPE_SCROLL_TYPE, scroll,
366 G_TYPE_BOOLEAN, FALSE);
370 modest_msg_view_window_class_init (ModestMsgViewWindowClass *klass)
372 GObjectClass *gobject_class;
373 HildonWindowClass *hildon_window_class;
374 ModestWindowClass *modest_window_class;
375 GtkBindingSet *binding_set;
377 gobject_class = (GObjectClass*) klass;
378 hildon_window_class = (HildonWindowClass *) klass;
379 modest_window_class = (ModestWindowClass *) klass;
381 parent_class = g_type_class_peek_parent (klass);
382 gobject_class->finalize = modest_msg_view_window_finalize;
384 modest_window_class->set_zoom_func = modest_msg_view_window_set_zoom;
385 modest_window_class->get_zoom_func = modest_msg_view_window_get_zoom;
386 modest_window_class->zoom_plus_func = modest_msg_view_window_zoom_plus;
387 modest_window_class->zoom_minus_func = modest_msg_view_window_zoom_minus;
388 modest_window_class->show_toolbar_func = modest_msg_view_window_show_toolbar;
389 modest_window_class->disconnect_signals_func = modest_msg_view_window_disconnect_signals;
391 modest_window_class->save_state_func = save_state;
393 klass->scroll_child = modest_msg_view_window_scroll_child;
395 signals[MSG_CHANGED_SIGNAL] =
396 g_signal_new ("msg-changed",
397 G_TYPE_FROM_CLASS (gobject_class),
399 G_STRUCT_OFFSET (ModestMsgViewWindowClass, msg_changed),
401 modest_marshal_VOID__POINTER_POINTER,
402 G_TYPE_NONE, 2, G_TYPE_POINTER, G_TYPE_POINTER);
404 signals[SCROLL_CHILD_SIGNAL] =
405 g_signal_new ("scroll-child",
406 G_TYPE_FROM_CLASS (gobject_class),
407 G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
408 G_STRUCT_OFFSET (ModestMsgViewWindowClass, scroll_child),
410 modest_marshal_BOOLEAN__ENUM_BOOLEAN,
411 G_TYPE_BOOLEAN, 2, GTK_TYPE_SCROLL_TYPE, G_TYPE_BOOLEAN);
413 binding_set = gtk_binding_set_by_class (klass);
414 add_scroll_binding (binding_set, GDK_Up, GTK_SCROLL_STEP_UP);
415 add_scroll_binding (binding_set, GDK_Down, GTK_SCROLL_STEP_DOWN);
416 add_scroll_binding (binding_set, GDK_Page_Up, GTK_SCROLL_PAGE_UP);
417 add_scroll_binding (binding_set, GDK_Page_Down, GTK_SCROLL_PAGE_DOWN);
418 add_scroll_binding (binding_set, GDK_Home, GTK_SCROLL_START);
419 add_scroll_binding (binding_set, GDK_End, GTK_SCROLL_END);
421 g_type_class_add_private (gobject_class, sizeof(ModestMsgViewWindowPrivate));
425 static void modest_header_view_observer_init(
426 ModestHeaderViewObserverIface *iface_class)
428 iface_class->update_func = modest_msg_view_window_update_model_replaced;
432 modest_msg_view_window_init (ModestMsgViewWindow *obj)
434 ModestMsgViewWindowPrivate *priv;
435 ModestWindowPrivate *parent_priv = NULL;
436 GtkActionGroup *action_group = NULL;
437 GError *error = NULL;
439 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(obj);
440 parent_priv = MODEST_WINDOW_GET_PRIVATE(obj);
441 parent_priv->ui_manager = gtk_ui_manager_new();
443 action_group = gtk_action_group_new ("ModestMsgViewWindowActions");
444 gtk_action_group_set_translation_domain (action_group, GETTEXT_PACKAGE);
446 /* Add common actions */
447 gtk_action_group_add_actions (action_group,
448 modest_action_entries,
449 G_N_ELEMENTS (modest_action_entries),
451 gtk_action_group_add_toggle_actions (action_group,
452 msg_view_toggle_action_entries,
453 G_N_ELEMENTS (msg_view_toggle_action_entries),
456 gtk_ui_manager_insert_action_group (parent_priv->ui_manager, action_group, 0);
457 g_object_unref (action_group);
459 /* Load the UI definition */
460 gtk_ui_manager_add_ui_from_file (parent_priv->ui_manager, MODEST_UIDIR "modest-msg-view-window-ui.xml",
463 g_printerr ("modest: could not merge modest-msg-view-window-ui.xml: %s\n", error->message);
464 g_error_free (error);
469 /* Add accelerators */
470 gtk_window_add_accel_group (GTK_WINDOW (obj),
471 gtk_ui_manager_get_accel_group (parent_priv->ui_manager));
473 priv->is_search_result = FALSE;
474 priv->is_outbox = FALSE;
476 priv->msg_view = NULL;
477 priv->header_model = NULL;
478 priv->header_folder_id = NULL;
479 priv->clipboard_change_handler = 0;
480 priv->queue_change_handler = 0;
481 priv->account_removed_handler = 0;
482 priv->row_changed_handler = 0;
483 priv->row_deleted_handler = 0;
484 priv->row_inserted_handler = 0;
485 priv->rows_reordered_handler = 0;
486 priv->fetch_image_redraw_handler = 0;
487 priv->progress_hint = FALSE;
488 priv->fetching_images = 0;
490 priv->optimized_view = FALSE;
491 priv->purge_timeout = 0;
492 priv->remove_attachment_banner = NULL;
493 priv->msg_uid = NULL;
494 priv->other_body = NULL;
496 priv->sighandlers = NULL;
499 init_window (MODEST_MSG_VIEW_WINDOW(obj));
501 hildon_program_add_window (hildon_program_get_instance(),
504 /* Grab the zoom keys, it will be used for Zoom and not for
506 g_signal_connect (G_OBJECT (obj), "realize",
507 G_CALLBACK (on_realize),
512 update_progress_hint (ModestMsgViewWindow *self)
514 ModestMsgViewWindowPrivate *priv;
515 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(self);
517 if (GTK_WIDGET_VISIBLE (self)) {
518 hildon_gtk_window_set_progress_indicator (GTK_WINDOW (self),
519 (priv->progress_hint || (priv->fetching_images > 0))?1:0);
524 set_progress_hint (ModestMsgViewWindow *self,
527 ModestWindowPrivate *parent_priv;
528 ModestMsgViewWindowPrivate *priv;
530 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self));
532 parent_priv = MODEST_WINDOW_GET_PRIVATE(self);
533 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(self);
535 /* Sets current progress hint */
536 priv->progress_hint = enabled;
538 update_progress_hint (self);
544 init_window (ModestMsgViewWindow *obj)
546 GtkWidget *main_vbox;
547 ModestMsgViewWindowPrivate *priv;
549 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(obj);
551 priv->msg_view = GTK_WIDGET (tny_platform_factory_new_msg_view (modest_tny_platform_factory_get_instance ()));
552 modest_msg_view_set_shadow_type (MODEST_MSG_VIEW (priv->msg_view), GTK_SHADOW_NONE);
553 main_vbox = gtk_vbox_new (FALSE, 6);
554 priv->main_scroll = hildon_pannable_area_new ();
555 g_object_set (G_OBJECT (priv->main_scroll),
556 "mov-mode", HILDON_MOVEMENT_MODE_BOTH,
559 gtk_container_add (GTK_CONTAINER (priv->main_scroll), priv->msg_view);
560 gtk_box_pack_start (GTK_BOX(main_vbox), priv->main_scroll, TRUE, TRUE, 0);
561 gtk_container_add (GTK_CONTAINER(obj), main_vbox);
563 /* NULL-ize fields if the window is destroyed */
564 g_signal_connect (priv->msg_view, "destroy", G_CALLBACK (gtk_widget_destroyed), &(priv->msg_view));
566 gtk_widget_show_all (GTK_WIDGET(main_vbox));
570 modest_msg_view_window_disconnect_signals (ModestWindow *self)
572 ModestMsgViewWindowPrivate *priv;
573 GtkWidget *header_view = NULL;
574 GtkWindow *parent_window = NULL;
576 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
578 if (gtk_clipboard_get (GDK_SELECTION_PRIMARY) &&
579 g_signal_handler_is_connected (gtk_clipboard_get (GDK_SELECTION_PRIMARY),
580 priv->clipboard_change_handler))
581 g_signal_handler_disconnect (gtk_clipboard_get (GDK_SELECTION_PRIMARY),
582 priv->clipboard_change_handler);
584 if (g_signal_handler_is_connected (G_OBJECT (modest_runtime_get_mail_operation_queue ()),
585 priv->queue_change_handler))
586 g_signal_handler_disconnect (G_OBJECT (modest_runtime_get_mail_operation_queue ()),
587 priv->queue_change_handler);
589 if (g_signal_handler_is_connected (G_OBJECT (modest_runtime_get_account_store ()),
590 priv->account_removed_handler))
591 g_signal_handler_disconnect (G_OBJECT (modest_runtime_get_account_store ()),
592 priv->account_removed_handler);
594 if (priv->header_model) {
595 if (g_signal_handler_is_connected(G_OBJECT (priv->header_model),
596 priv->row_changed_handler))
597 g_signal_handler_disconnect(G_OBJECT (priv->header_model),
598 priv->row_changed_handler);
600 if (g_signal_handler_is_connected(G_OBJECT (priv->header_model),
601 priv->row_deleted_handler))
602 g_signal_handler_disconnect(G_OBJECT (priv->header_model),
603 priv->row_deleted_handler);
605 if (g_signal_handler_is_connected(G_OBJECT (priv->header_model),
606 priv->row_inserted_handler))
607 g_signal_handler_disconnect(G_OBJECT (priv->header_model),
608 priv->row_inserted_handler);
610 if (g_signal_handler_is_connected(G_OBJECT (priv->header_model),
611 priv->rows_reordered_handler))
612 g_signal_handler_disconnect(G_OBJECT (priv->header_model),
613 priv->rows_reordered_handler);
616 modest_signal_mgr_disconnect_all_and_destroy (priv->sighandlers);
617 priv->sighandlers = NULL;
619 parent_window = gtk_window_get_transient_for (GTK_WINDOW (self));
620 if (parent_window && MODEST_IS_HEADER_WINDOW (parent_window)) {
621 header_view = GTK_WIDGET (modest_header_window_get_header_view (MODEST_HEADER_WINDOW (parent_window)));
623 modest_header_view_remove_observer(MODEST_HEADER_VIEW (header_view),
624 MODEST_HEADER_VIEW_OBSERVER(self));
630 modest_msg_view_window_finalize (GObject *obj)
632 ModestMsgViewWindowPrivate *priv;
634 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (obj);
636 /* Sanity check: shouldn't be needed, the window mgr should
637 call this function before */
638 modest_msg_view_window_disconnect_signals (MODEST_WINDOW (obj));
640 if (priv->fetch_image_redraw_handler > 0) {
641 g_source_remove (priv->fetch_image_redraw_handler);
642 priv->fetch_image_redraw_handler = 0;
645 if (priv->other_body != NULL) {
646 g_object_unref (priv->other_body);
647 priv->other_body = NULL;
650 if (priv->top_msg != NULL) {
651 g_object_unref (priv->top_msg);
652 priv->top_msg = NULL;
655 if (priv->header_model != NULL) {
656 g_object_unref (priv->header_model);
657 priv->header_model = NULL;
660 if (priv->remove_attachment_banner) {
661 gtk_widget_destroy (priv->remove_attachment_banner);
662 g_object_unref (priv->remove_attachment_banner);
663 priv->remove_attachment_banner = NULL;
666 if (priv->purge_timeout > 0) {
667 g_source_remove (priv->purge_timeout);
668 priv->purge_timeout = 0;
671 if (priv->row_reference) {
672 gtk_tree_row_reference_free (priv->row_reference);
673 priv->row_reference = NULL;
676 if (priv->next_row_reference) {
677 gtk_tree_row_reference_free (priv->next_row_reference);
678 priv->next_row_reference = NULL;
682 g_free (priv->msg_uid);
683 priv->msg_uid = NULL;
686 G_OBJECT_CLASS(parent_class)->finalize (obj);
690 select_next_valid_row (GtkTreeModel *model,
691 GtkTreeRowReference **row_reference,
695 GtkTreeIter tmp_iter;
697 GtkTreePath *next = NULL;
698 gboolean retval = FALSE, finished;
700 g_return_val_if_fail (gtk_tree_row_reference_valid (*row_reference), FALSE);
702 path = gtk_tree_row_reference_get_path (*row_reference);
703 gtk_tree_model_get_iter (model, &tmp_iter, path);
704 gtk_tree_row_reference_free (*row_reference);
705 *row_reference = NULL;
709 TnyHeader *header = NULL;
711 if (gtk_tree_model_iter_next (model, &tmp_iter)) {
712 gtk_tree_model_get (model, &tmp_iter,
713 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
717 if (msg_is_visible (header, is_outbox)) {
718 next = gtk_tree_model_get_path (model, &tmp_iter);
719 *row_reference = gtk_tree_row_reference_new (model, next);
720 gtk_tree_path_free (next);
724 g_object_unref (header);
727 } else if (cycle && gtk_tree_model_get_iter_first (model, &tmp_iter)) {
728 next = gtk_tree_model_get_path (model, &tmp_iter);
730 /* Ensure that we are not selecting the same */
731 if (gtk_tree_path_compare (path, next) != 0) {
732 gtk_tree_model_get (model, &tmp_iter,
733 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
736 if (msg_is_visible (header, is_outbox)) {
737 *row_reference = gtk_tree_row_reference_new (model, next);
741 g_object_unref (header);
745 /* If we ended up in the same message
746 then there is no valid next
750 gtk_tree_path_free (next);
752 /* If there are no more messages and we don't
753 want to start again in the first one then
754 there is no valid next message */
760 gtk_tree_path_free (path);
765 /* TODO: This should be in _init(), with the parameters as properties. */
767 modest_msg_view_window_construct (ModestMsgViewWindow *self,
768 const gchar *modest_account_name,
769 const gchar *mailbox,
770 const gchar *msg_uid)
773 ModestMsgViewWindowPrivate *priv = NULL;
774 ModestWindowPrivate *parent_priv = NULL;
775 ModestDimmingRulesGroup *toolbar_rules_group = NULL;
776 ModestDimmingRulesGroup *clipboard_rules_group = NULL;
778 obj = G_OBJECT (self);
779 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(obj);
780 parent_priv = MODEST_WINDOW_GET_PRIVATE(obj);
782 priv->msg_uid = g_strdup (msg_uid);
785 parent_priv->menubar = NULL;
787 toolbar_rules_group = modest_dimming_rules_group_new (MODEST_DIMMING_RULES_TOOLBAR, TRUE);
788 clipboard_rules_group = modest_dimming_rules_group_new (MODEST_DIMMING_RULES_CLIPBOARD, FALSE);
791 /* Add common dimming rules */
792 modest_dimming_rules_group_add_rules (toolbar_rules_group,
793 modest_msg_view_toolbar_dimming_entries,
794 G_N_ELEMENTS (modest_msg_view_toolbar_dimming_entries),
795 MODEST_WINDOW (self));
796 modest_dimming_rules_group_add_rules (clipboard_rules_group,
797 modest_msg_view_clipboard_dimming_entries,
798 G_N_ELEMENTS (modest_msg_view_clipboard_dimming_entries),
799 MODEST_WINDOW (self));
801 /* Insert dimming rules group for this window */
802 modest_ui_dimming_manager_insert_rules_group (parent_priv->ui_dimming_manager, toolbar_rules_group);
803 modest_ui_dimming_manager_insert_rules_group (parent_priv->ui_dimming_manager, clipboard_rules_group);
804 g_object_unref (toolbar_rules_group);
805 g_object_unref (clipboard_rules_group);
807 /* g_signal_connect (G_OBJECT(obj), "delete-event", G_CALLBACK(on_delete_event), obj); */
809 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);
810 g_signal_connect (G_OBJECT(priv->msg_view), "activate_link",
811 G_CALLBACK (modest_ui_actions_on_msg_link_clicked), obj);
812 g_signal_connect (G_OBJECT(priv->msg_view), "link_hover",
813 G_CALLBACK (modest_ui_actions_on_msg_link_hover), obj);
814 g_signal_connect (G_OBJECT(priv->msg_view), "attachment_clicked",
815 G_CALLBACK (modest_ui_actions_on_msg_attachment_clicked), obj);
816 g_signal_connect (G_OBJECT(priv->msg_view), "recpt_activated",
817 G_CALLBACK (modest_ui_actions_on_msg_recpt_activated), obj);
818 g_signal_connect (G_OBJECT(priv->msg_view), "show_details",
819 G_CALLBACK (modest_ui_actions_on_details), obj);
820 g_signal_connect (G_OBJECT(priv->msg_view), "link_contextual",
821 G_CALLBACK (modest_ui_actions_on_msg_link_contextual), obj);
822 g_signal_connect (G_OBJECT(priv->msg_view), "limit_error",
823 G_CALLBACK (modest_ui_actions_on_limit_error), obj);
824 g_signal_connect (G_OBJECT(priv->msg_view), "handle_calendar",
825 G_CALLBACK (on_handle_calendar), obj);
826 g_signal_connect (G_OBJECT (priv->msg_view), "fetch_image",
827 G_CALLBACK (on_fetch_image), obj);
829 g_signal_connect (G_OBJECT (obj), "key-release-event",
830 G_CALLBACK (modest_msg_view_window_key_event),
833 g_signal_connect (G_OBJECT (obj), "key-press-event",
834 G_CALLBACK (modest_msg_view_window_key_event),
837 g_signal_connect (G_OBJECT (obj), "move-focus",
838 G_CALLBACK (on_move_focus), obj);
840 g_signal_connect (G_OBJECT (obj), "map-event",
841 G_CALLBACK (_modest_msg_view_window_map_event),
844 /* Mail Operation Queue */
845 priv->queue_change_handler = g_signal_connect (G_OBJECT (modest_runtime_get_mail_operation_queue ()),
847 G_CALLBACK (on_queue_changed),
850 /* Account manager */
851 priv->account_removed_handler = g_signal_connect (G_OBJECT (modest_runtime_get_account_store ()),
853 G_CALLBACK(on_account_removed),
856 modest_window_set_active_account (MODEST_WINDOW(obj), modest_account_name);
857 modest_window_set_active_mailbox (MODEST_WINDOW(obj), mailbox);
859 /* First add out toolbar ... */
860 modest_msg_view_window_show_toolbar (MODEST_WINDOW (obj), TRUE);
862 /* ... and later the find toolbar. This way find toolbar will
863 be shown over the other */
864 priv->find_toolbar = hildon_find_toolbar_new (NULL);
865 hildon_window_add_toolbar (HILDON_WINDOW (obj), GTK_TOOLBAR (priv->find_toolbar));
866 gtk_widget_set_no_show_all (priv->find_toolbar, TRUE);
867 g_signal_connect (G_OBJECT (priv->find_toolbar), "close",
868 G_CALLBACK (modest_msg_view_window_find_toolbar_close), obj);
869 g_signal_connect (G_OBJECT (priv->find_toolbar), "search",
870 G_CALLBACK (modest_msg_view_window_find_toolbar_search), obj);
871 priv->last_search = NULL;
873 /* Init the clipboard actions dim status */
874 modest_msg_view_grab_focus(MODEST_MSG_VIEW (priv->msg_view));
876 update_window_title (MODEST_MSG_VIEW_WINDOW (obj));
881 /* FIXME: parameter checks */
883 modest_msg_view_window_new_with_header_model (TnyMsg *msg,
884 const gchar *modest_account_name,
885 const gchar *mailbox,
886 const gchar *msg_uid,
888 GtkTreeRowReference *row_reference)
890 ModestMsgViewWindow *window = NULL;
891 ModestMsgViewWindowPrivate *priv = NULL;
892 TnyFolder *header_folder = NULL;
893 ModestHeaderView *header_view = NULL;
894 ModestWindowMgr *mgr = NULL;
897 modest_tny_mime_part_to_string (TNY_MIME_PART (msg), 0);
900 mgr = modest_runtime_get_window_mgr ();
901 window = MODEST_MSG_VIEW_WINDOW (modest_window_mgr_get_msg_view_window (mgr));
902 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), NULL);
904 modest_msg_view_window_construct (window, modest_account_name, mailbox, msg_uid);
906 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
907 priv->top_msg = NULL;
909 /* Remember the message list's TreeModel so we can detect changes
910 * and change the list selection when necessary: */
911 header_folder = modest_header_view_get_folder (header_view);
913 priv->is_outbox = (modest_tny_folder_guess_folder_type (header_folder) ==
914 TNY_FOLDER_TYPE_OUTBOX);
915 priv->header_folder_id = tny_folder_get_id (header_folder);
916 g_object_unref(header_folder);
919 /* Setup row references and connect signals */
920 priv->header_model = g_object_ref (model);
922 if (row_reference && gtk_tree_row_reference_valid (row_reference)) {
923 priv->row_reference = gtk_tree_row_reference_copy (row_reference);
924 priv->next_row_reference = gtk_tree_row_reference_copy (row_reference);
925 select_next_valid_row (model, &(priv->next_row_reference), TRUE, priv->is_outbox);
927 priv->row_reference = NULL;
928 priv->next_row_reference = NULL;
931 /* Connect signals */
932 priv->row_changed_handler =
933 g_signal_connect (GTK_TREE_MODEL(model), "row-changed",
934 G_CALLBACK(modest_msg_view_window_on_row_changed),
936 priv->row_deleted_handler =
937 g_signal_connect (GTK_TREE_MODEL(model), "row-deleted",
938 G_CALLBACK(modest_msg_view_window_on_row_deleted),
940 priv->row_inserted_handler =
941 g_signal_connect (GTK_TREE_MODEL(model), "row-inserted",
942 G_CALLBACK(modest_msg_view_window_on_row_inserted),
944 priv->rows_reordered_handler =
945 g_signal_connect(GTK_TREE_MODEL(model), "rows-reordered",
946 G_CALLBACK(modest_msg_view_window_on_row_reordered),
949 if (header_view != NULL){
950 modest_header_view_add_observer(header_view,
951 MODEST_HEADER_VIEW_OBSERVER(window));
954 tny_msg_view_set_msg (TNY_MSG_VIEW (priv->msg_view), msg);
955 update_window_title (MODEST_MSG_VIEW_WINDOW (window));
956 update_branding (MODEST_MSG_VIEW_WINDOW (window));
958 /* gtk_widget_show_all (GTK_WIDGET (window)); */
959 modest_msg_view_window_update_priority (window);
960 /* Check dimming rules */
961 modest_ui_actions_check_toolbar_dimming_rules (MODEST_WINDOW (window));
962 modest_ui_actions_check_menu_dimming_rules (MODEST_WINDOW (window));
963 modest_window_check_dimming_rules_group (MODEST_WINDOW (window), MODEST_DIMMING_RULES_CLIPBOARD);
965 return MODEST_WINDOW(window);
969 modest_msg_view_window_new_from_uid (const gchar *modest_account_name,
970 const gchar *mailbox,
971 const gchar *msg_uid)
973 ModestMsgViewWindow *window = NULL;
974 ModestMsgViewWindowPrivate *priv = NULL;
975 ModestWindowMgr *mgr = NULL;
977 TnyAccount *account = NULL;
979 mgr = modest_runtime_get_window_mgr ();
980 window = MODEST_MSG_VIEW_WINDOW (modest_window_mgr_get_msg_view_window (mgr));
981 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), NULL);
983 modest_msg_view_window_construct (window, modest_account_name, mailbox, msg_uid);
985 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
986 priv->top_msg = NULL;
988 is_merge = g_str_has_prefix (msg_uid, "merge:");
990 /* Get the account */
992 account = tny_account_store_find_account (TNY_ACCOUNT_STORE (modest_runtime_get_account_store ()),
995 if (is_merge || account) {
996 TnyFolder *folder = NULL;
998 /* Try to get the message, if it's already downloaded
999 we don't need to connect */
1001 folder = modest_tny_folder_store_find_folder_from_uri (TNY_FOLDER_STORE (account), msg_uid);
1003 ModestTnyAccountStore *account_store;
1004 ModestTnyLocalFoldersAccount *local_folders_account;
1006 account_store = modest_runtime_get_account_store ();
1007 local_folders_account = MODEST_TNY_LOCAL_FOLDERS_ACCOUNT (
1008 modest_tny_account_store_get_local_folders_account (account_store));
1009 folder = modest_tny_local_folders_account_get_merged_outbox (local_folders_account);
1010 g_object_unref (local_folders_account);
1014 gboolean device_online;
1016 device = modest_runtime_get_device();
1017 device_online = tny_device_is_online (device);
1018 if (device_online) {
1019 message_reader (window, priv, NULL, msg_uid, folder, NULL);
1021 TnyMsg *msg = tny_folder_find_msg (folder, msg_uid, NULL);
1023 tny_msg_view_set_msg (TNY_MSG_VIEW (priv->msg_view), msg);
1024 update_window_title (MODEST_MSG_VIEW_WINDOW (window));
1025 update_branding (MODEST_MSG_VIEW_WINDOW (window));
1026 g_object_unref (msg);
1027 /* Sync flags to server */
1028 sync_flags (MODEST_MSG_VIEW_WINDOW (window));
1030 message_reader (window, priv, NULL, msg_uid, folder, NULL);
1033 g_object_unref (folder);
1038 /* Check dimming rules */
1039 modest_ui_actions_check_toolbar_dimming_rules (MODEST_WINDOW (window));
1040 modest_ui_actions_check_menu_dimming_rules (MODEST_WINDOW (window));
1041 modest_window_check_dimming_rules_group (MODEST_WINDOW (window), MODEST_DIMMING_RULES_CLIPBOARD);
1043 return MODEST_WINDOW(window);
1047 modest_msg_view_window_new_from_header_view (ModestHeaderView *header_view,
1048 const gchar *modest_account_name,
1049 const gchar *mailbox,
1050 const gchar *msg_uid,
1051 GtkTreeRowReference *row_reference)
1053 ModestMsgViewWindow *window = NULL;
1054 ModestMsgViewWindowPrivate *priv = NULL;
1055 TnyFolder *header_folder = NULL;
1056 ModestWindowMgr *mgr = NULL;
1060 mgr = modest_runtime_get_window_mgr ();
1061 window = MODEST_MSG_VIEW_WINDOW (modest_window_mgr_get_msg_view_window (mgr));
1062 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), NULL);
1064 modest_msg_view_window_construct (window, modest_account_name, mailbox, msg_uid);
1066 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1067 priv->top_msg = NULL;
1069 /* Remember the message list's TreeModel so we can detect changes
1070 * and change the list selection when necessary: */
1072 if (header_view != NULL){
1073 header_folder = modest_header_view_get_folder(header_view);
1074 /* This could happen if the header folder was
1075 unseleted before opening this msg window (for
1076 example if the user selects an account in the
1077 folder view of the main window */
1078 if (header_folder) {
1079 priv->is_outbox = (modest_tny_folder_guess_folder_type (header_folder) ==
1080 TNY_FOLDER_TYPE_OUTBOX);
1081 priv->header_folder_id = tny_folder_get_id(header_folder);
1082 g_object_unref(header_folder);
1086 /* Setup row references and connect signals */
1087 priv->header_model = gtk_tree_view_get_model (GTK_TREE_VIEW (header_view));
1088 g_object_ref (priv->header_model);
1090 if (row_reference && gtk_tree_row_reference_valid (row_reference)) {
1091 priv->row_reference = gtk_tree_row_reference_copy (row_reference);
1092 priv->next_row_reference = gtk_tree_row_reference_copy (row_reference);
1093 select_next_valid_row (priv->header_model, &(priv->next_row_reference), TRUE, priv->is_outbox);
1095 priv->row_reference = NULL;
1096 priv->next_row_reference = NULL;
1099 /* Connect signals */
1100 priv->row_changed_handler =
1101 g_signal_connect (GTK_TREE_MODEL(priv->header_model), "row-changed",
1102 G_CALLBACK(modest_msg_view_window_on_row_changed),
1104 priv->row_deleted_handler =
1105 g_signal_connect (GTK_TREE_MODEL(priv->header_model), "row-deleted",
1106 G_CALLBACK(modest_msg_view_window_on_row_deleted),
1108 priv->row_inserted_handler =
1109 g_signal_connect (GTK_TREE_MODEL(priv->header_model), "row-inserted",
1110 G_CALLBACK(modest_msg_view_window_on_row_inserted),
1112 priv->rows_reordered_handler =
1113 g_signal_connect(GTK_TREE_MODEL(priv->header_model), "rows-reordered",
1114 G_CALLBACK(modest_msg_view_window_on_row_reordered),
1117 if (header_view != NULL){
1118 modest_header_view_add_observer(header_view,
1119 MODEST_HEADER_VIEW_OBSERVER(window));
1122 tny_msg_view_set_msg (TNY_MSG_VIEW (priv->msg_view), NULL);
1123 update_branding (MODEST_MSG_VIEW_WINDOW (window));
1125 if (priv->row_reference) {
1126 path = gtk_tree_row_reference_get_path (priv->row_reference);
1127 if (gtk_tree_model_get_iter (priv->header_model, &iter, path)) {
1129 gtk_tree_model_get (priv->header_model, &iter,
1130 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
1132 message_reader (window, priv, header, NULL, NULL, priv->row_reference);
1133 g_object_unref (header);
1135 gtk_tree_path_free (path);
1137 /* Check dimming rules */
1138 modest_ui_actions_check_toolbar_dimming_rules (MODEST_WINDOW (window));
1139 modest_ui_actions_check_menu_dimming_rules (MODEST_WINDOW (window));
1140 modest_window_check_dimming_rules_group (MODEST_WINDOW (window), MODEST_DIMMING_RULES_CLIPBOARD);
1142 return MODEST_WINDOW(window);
1146 modest_msg_view_window_new_for_search_result (TnyMsg *msg,
1147 const gchar *modest_account_name,
1148 const gchar *mailbox,
1149 const gchar *msg_uid)
1151 ModestMsgViewWindow *window = NULL;
1152 ModestMsgViewWindowPrivate *priv = NULL;
1153 ModestWindowMgr *mgr = NULL;
1155 mgr = modest_runtime_get_window_mgr ();
1156 window = MODEST_MSG_VIEW_WINDOW (modest_window_mgr_get_msg_view_window (mgr));
1157 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), NULL);
1158 modest_msg_view_window_construct (window, modest_account_name, mailbox, msg_uid);
1160 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1161 priv->top_msg = NULL;
1163 /* Remember that this is a search result,
1164 * so we can disable some UI appropriately: */
1165 priv->is_search_result = TRUE;
1167 tny_msg_view_set_msg (TNY_MSG_VIEW (priv->msg_view), msg);
1168 update_branding (MODEST_MSG_VIEW_WINDOW (window));
1170 update_window_title (window);
1171 /* gtk_widget_show_all (GTK_WIDGET (window));*/
1172 modest_msg_view_window_update_priority (window);
1174 /* Check dimming rules */
1175 modest_ui_actions_check_toolbar_dimming_rules (MODEST_WINDOW (window));
1176 modest_ui_actions_check_menu_dimming_rules (MODEST_WINDOW (window));
1177 modest_window_check_dimming_rules_group (MODEST_WINDOW (window), MODEST_DIMMING_RULES_CLIPBOARD);
1179 return MODEST_WINDOW(window);
1183 modest_msg_view_window_is_other_body (ModestMsgViewWindow *self)
1185 ModestMsgViewWindowPrivate *priv = NULL;
1187 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self), FALSE);
1188 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
1190 return (priv->other_body != NULL);
1194 modest_msg_view_window_new_with_other_body (TnyMsg *msg,
1195 TnyMimePart *other_body,
1197 const gchar *modest_account_name,
1198 const gchar *mailbox,
1199 const gchar *msg_uid)
1201 GObject *obj = NULL;
1202 ModestMsgViewWindowPrivate *priv;
1203 ModestWindowMgr *mgr = NULL;
1205 g_return_val_if_fail (msg, NULL);
1206 mgr = modest_runtime_get_window_mgr ();
1207 obj = G_OBJECT (modest_window_mgr_get_msg_view_window (mgr));
1208 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (obj);
1209 modest_msg_view_window_construct (MODEST_MSG_VIEW_WINDOW (obj),
1210 modest_account_name, mailbox, msg_uid);
1213 priv->other_body = g_object_ref (other_body);
1214 modest_msg_view_set_msg_with_other_body (MODEST_MSG_VIEW (priv->msg_view), msg, other_body);
1216 tny_msg_view_set_msg (TNY_MSG_VIEW (priv->msg_view), msg);
1219 priv->top_msg = g_object_ref (top_msg);
1221 priv->top_msg = NULL;
1223 update_window_title (MODEST_MSG_VIEW_WINDOW (obj));
1224 update_branding (MODEST_MSG_VIEW_WINDOW (obj));
1226 /* gtk_widget_show_all (GTK_WIDGET (obj)); */
1228 /* Check dimming rules */
1229 modest_ui_actions_check_toolbar_dimming_rules (MODEST_WINDOW (obj));
1230 modest_ui_actions_check_menu_dimming_rules (MODEST_WINDOW (obj));
1231 modest_window_check_dimming_rules_group (MODEST_WINDOW (obj), MODEST_DIMMING_RULES_CLIPBOARD);
1233 return MODEST_WINDOW(obj);
1237 modest_msg_view_window_new_for_attachment (TnyMsg *msg,
1239 const gchar *modest_account_name,
1240 const gchar *mailbox,
1241 const gchar *msg_uid)
1243 return modest_msg_view_window_new_with_other_body (msg, NULL, top_msg, modest_account_name, mailbox, msg_uid);
1247 modest_msg_view_window_on_row_changed (GtkTreeModel *header_model,
1250 ModestMsgViewWindow *window)
1252 check_dimming_rules_after_change (window);
1256 modest_msg_view_window_on_row_deleted(GtkTreeModel *header_model,
1258 ModestMsgViewWindow *window)
1260 check_dimming_rules_after_change (window);
1262 /* The window could have dissapeared */
1265 check_dimming_rules_after_change (ModestMsgViewWindow *window)
1267 modest_ui_actions_check_menu_dimming_rules (MODEST_WINDOW (window));
1268 modest_ui_actions_check_toolbar_dimming_rules (MODEST_WINDOW (window));
1272 /* On insertions we check if the folder still has the message we are
1273 * showing or do not. If do not, we do nothing. Which means we are still
1274 * not attached to any header folder and thus next/prev buttons are
1275 * still dimmed. Once the message that is shown by msg-view is found, the
1276 * new model of header-view will be attached and the references will be set.
1277 * On each further insertions dimming rules will be checked. However
1278 * this requires extra CPU time at least works.
1279 * (An message might be deleted from TnyFolder and thus will not be
1280 * inserted into the model again for example if it is removed by the
1281 * imap server and the header view is refreshed.)
1284 modest_msg_view_window_on_row_inserted (GtkTreeModel *model,
1285 GtkTreePath *tree_path,
1286 GtkTreeIter *tree_iter,
1287 ModestMsgViewWindow *window)
1289 ModestMsgViewWindowPrivate *priv = NULL;
1290 TnyHeader *header = NULL;
1292 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window));
1293 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1295 g_assert (model == priv->header_model);
1297 /* Check if the newly inserted message is the same we are actually
1298 * showing. IF not, we should remain detached from the header model
1299 * and thus prev and next toolbar buttons should remain dimmed. */
1300 gtk_tree_model_get (model, tree_iter,
1301 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
1304 if (TNY_IS_HEADER (header)) {
1307 uid = modest_tny_folder_get_header_unique_id (header);
1308 if (!g_str_equal(priv->msg_uid, uid)) {
1309 check_dimming_rules_after_change (window);
1311 g_object_unref (G_OBJECT(header));
1315 g_object_unref(G_OBJECT(header));
1318 if (priv->row_reference) {
1319 gtk_tree_row_reference_free (priv->row_reference);
1322 /* Setup row_reference for the actual msg. */
1323 priv->row_reference = gtk_tree_row_reference_new (priv->header_model, tree_path);
1324 if (priv->row_reference == NULL) {
1325 g_warning("%s: No reference for msg header item.", __FUNCTION__);
1329 /* Now set up next_row_reference. */
1330 if (priv->next_row_reference) {
1331 gtk_tree_row_reference_free (priv->next_row_reference);
1334 priv->next_row_reference = gtk_tree_row_reference_copy (priv->row_reference);
1335 select_next_valid_row (priv->header_model,
1336 &(priv->next_row_reference), FALSE, priv->is_outbox);
1338 /* Connect the remaining callbacks to become able to detect
1339 * changes in header-view. */
1340 priv->row_changed_handler =
1341 g_signal_connect (priv->header_model, "row-changed",
1342 G_CALLBACK (modest_msg_view_window_on_row_changed),
1344 priv->row_deleted_handler =
1345 g_signal_connect (priv->header_model, "row-deleted",
1346 G_CALLBACK (modest_msg_view_window_on_row_deleted),
1348 priv->rows_reordered_handler =
1349 g_signal_connect (priv->header_model, "rows-reordered",
1350 G_CALLBACK (modest_msg_view_window_on_row_reordered),
1353 check_dimming_rules_after_change (window);
1357 modest_msg_view_window_on_row_reordered (GtkTreeModel *header_model,
1361 ModestMsgViewWindow *window)
1363 ModestMsgViewWindowPrivate *priv = NULL;
1364 gboolean already_changed = FALSE;
1366 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(window);
1368 /* If the current row was reordered select the proper next
1369 valid row. The same if the next row reference changes */
1370 if (!priv->row_reference ||
1371 !gtk_tree_row_reference_valid (priv->row_reference))
1374 if (priv->next_row_reference &&
1375 gtk_tree_row_reference_valid (priv->next_row_reference)) {
1376 GtkTreePath *cur, *next;
1377 /* Check that the order is still the correct one */
1378 cur = gtk_tree_row_reference_get_path (priv->row_reference);
1379 next = gtk_tree_row_reference_get_path (priv->next_row_reference);
1380 gtk_tree_path_next (cur);
1381 if (gtk_tree_path_compare (cur, next) != 0) {
1382 gtk_tree_row_reference_free (priv->next_row_reference);
1383 priv->next_row_reference = gtk_tree_row_reference_copy (priv->row_reference);
1384 select_next_valid_row (header_model, &(priv->next_row_reference), FALSE, priv->is_outbox);
1385 already_changed = TRUE;
1387 gtk_tree_path_free (cur);
1388 gtk_tree_path_free (next);
1390 if (priv->next_row_reference)
1391 gtk_tree_row_reference_free (priv->next_row_reference);
1392 /* Update next row reference */
1393 priv->next_row_reference = gtk_tree_row_reference_copy (priv->row_reference);
1394 select_next_valid_row (header_model, &(priv->next_row_reference), FALSE, priv->is_outbox);
1395 already_changed = TRUE;
1398 check_dimming_rules_after_change (window);
1401 /* The modest_msg_view_window_update_model_replaced implements update
1402 * function for ModestHeaderViewObserver. Checks whether the TnyFolder
1403 * actually belongs to the header-view is the same as the TnyFolder of
1404 * the message of msg-view or not. If they are different, there is
1405 * nothing to do. If they are the same, then the model has replaced and
1406 * the reference in msg-view shall be replaced from the old model to
1407 * the new model. In this case the view will be detached from it's
1408 * header folder. From this point the next/prev buttons are dimmed.
1411 modest_msg_view_window_update_model_replaced (ModestHeaderViewObserver *observer,
1412 GtkTreeModel *model,
1413 const gchar *tny_folder_id)
1415 ModestMsgViewWindowPrivate *priv = NULL;
1416 ModestMsgViewWindow *window = NULL;
1418 g_assert(MODEST_IS_HEADER_VIEW_OBSERVER(observer));
1419 g_assert(MODEST_IS_MSG_VIEW_WINDOW(observer));
1421 window = MODEST_MSG_VIEW_WINDOW(observer);
1422 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(window);
1424 /* If there is an other folder in the header-view then we do
1425 * not care about it's model (msg list). Else if the
1426 * header-view shows the folder the msg shown by us is in, we
1427 * shall replace our model reference and make some check. */
1428 if(model == NULL || tny_folder_id == NULL ||
1429 (priv->header_folder_id && !g_str_equal(tny_folder_id, priv->header_folder_id)))
1432 /* Model is changed(replaced), so we should forget the old
1433 * one. Because there might be other references and there
1434 * might be some change on the model even if we unreferenced
1435 * it, we need to disconnect our signals here. */
1436 if (priv->header_model) {
1437 if (g_signal_handler_is_connected(G_OBJECT (priv->header_model),
1438 priv->row_changed_handler))
1439 g_signal_handler_disconnect(G_OBJECT (priv->header_model),
1440 priv->row_changed_handler);
1441 if (g_signal_handler_is_connected(G_OBJECT (priv->header_model),
1442 priv->row_deleted_handler))
1443 g_signal_handler_disconnect(G_OBJECT (priv->header_model),
1444 priv->row_deleted_handler);
1445 if (g_signal_handler_is_connected(G_OBJECT (priv->header_model),
1446 priv->row_inserted_handler))
1447 g_signal_handler_disconnect(G_OBJECT (priv->header_model),
1448 priv->row_inserted_handler);
1449 if (g_signal_handler_is_connected(G_OBJECT (priv->header_model),
1450 priv->rows_reordered_handler))
1451 g_signal_handler_disconnect(G_OBJECT (priv->header_model),
1452 priv->rows_reordered_handler);
1455 if (priv->row_reference)
1456 gtk_tree_row_reference_free (priv->row_reference);
1457 if (priv->next_row_reference)
1458 gtk_tree_row_reference_free (priv->next_row_reference);
1459 g_object_unref(priv->header_model);
1462 priv->row_changed_handler = 0;
1463 priv->row_deleted_handler = 0;
1464 priv->row_inserted_handler = 0;
1465 priv->rows_reordered_handler = 0;
1466 priv->next_row_reference = NULL;
1467 priv->row_reference = NULL;
1468 priv->header_model = NULL;
1471 priv->header_model = g_object_ref (model);
1473 /* Also we must connect to the new model for row insertions.
1474 * Only for insertions now. We will need other ones only after
1475 * the msg is show by msg-view is added to the new model. */
1476 priv->row_inserted_handler =
1477 g_signal_connect (priv->header_model, "row-inserted",
1478 G_CALLBACK(modest_msg_view_window_on_row_inserted),
1481 modest_ui_actions_check_menu_dimming_rules(MODEST_WINDOW(window));
1482 modest_ui_actions_check_toolbar_dimming_rules(MODEST_WINDOW(window));
1486 modest_msg_view_window_toolbar_on_transfer_mode (ModestMsgViewWindow *self)
1488 ModestMsgViewWindowPrivate *priv= NULL;
1490 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self), FALSE);
1491 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
1493 return priv->progress_hint;
1497 modest_msg_view_window_get_header (ModestMsgViewWindow *self)
1499 ModestMsgViewWindowPrivate *priv= NULL;
1501 TnyHeader *header = NULL;
1502 GtkTreePath *path = NULL;
1505 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self), NULL);
1506 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
1508 /* If the message was not obtained from a treemodel,
1509 * for instance if it was opened directly by the search UI:
1511 if (priv->header_model == NULL ||
1512 priv->row_reference == NULL ||
1513 !gtk_tree_row_reference_valid (priv->row_reference)) {
1514 msg = modest_msg_view_window_get_message (self);
1516 header = tny_msg_get_header (msg);
1517 g_object_unref (msg);
1522 /* Get iter of the currently selected message in the header view: */
1523 path = gtk_tree_row_reference_get_path (priv->row_reference);
1524 g_return_val_if_fail (path != NULL, NULL);
1525 gtk_tree_model_get_iter (priv->header_model,
1529 /* Get current message header */
1530 gtk_tree_model_get (priv->header_model, &iter,
1531 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
1534 gtk_tree_path_free (path);
1539 modest_msg_view_window_get_message (ModestMsgViewWindow *self)
1541 ModestMsgViewWindowPrivate *priv;
1543 g_return_val_if_fail (self, NULL);
1545 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(self);
1547 return tny_msg_view_get_msg (TNY_MSG_VIEW (priv->msg_view));
1551 modest_msg_view_window_get_top_message (ModestMsgViewWindow *self)
1553 ModestMsgViewWindowPrivate *priv;
1555 g_return_val_if_fail (self, NULL);
1557 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(self);
1560 return g_object_ref (priv->top_msg);
1566 modest_msg_view_window_get_message_uid (ModestMsgViewWindow *self)
1568 ModestMsgViewWindowPrivate *priv;
1570 g_return_val_if_fail (self, NULL);
1572 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
1574 return (const gchar*) priv->msg_uid;
1577 /* Used for the Ctrl+F accelerator */
1579 modest_msg_view_window_toggle_find_toolbar (GtkWidget *obj,
1582 ModestMsgViewWindow *window = MODEST_MSG_VIEW_WINDOW (data);
1583 ModestMsgViewWindowPrivate *priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1585 if (GTK_WIDGET_VISIBLE (priv->find_toolbar)) {
1586 modest_msg_view_window_find_toolbar_close (obj, data);
1588 modest_msg_view_window_show_find_toolbar (obj, data);
1592 /* Handler for menu option */
1594 modest_msg_view_window_show_find_toolbar (GtkWidget *obj,
1597 ModestMsgViewWindow *window = MODEST_MSG_VIEW_WINDOW (data);
1598 ModestMsgViewWindowPrivate *priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1600 gtk_widget_show (priv->find_toolbar);
1601 hildon_find_toolbar_highlight_entry (HILDON_FIND_TOOLBAR (priv->find_toolbar), TRUE);
1604 /* Handler for click on the "X" close button in find toolbar */
1606 modest_msg_view_window_find_toolbar_close (GtkWidget *widget,
1607 ModestMsgViewWindow *obj)
1609 ModestMsgViewWindowPrivate *priv;
1611 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (obj);
1614 gtk_widget_hide (priv->find_toolbar);
1615 modest_msg_view_grab_focus (MODEST_MSG_VIEW (priv->msg_view));
1619 modest_msg_view_window_find_toolbar_search (GtkWidget *widget,
1620 ModestMsgViewWindow *obj)
1622 gchar *current_search;
1623 ModestMsgViewWindowPrivate *priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (obj);
1625 if (modest_mime_part_view_is_empty (MODEST_MIME_PART_VIEW (priv->msg_view))) {
1626 hildon_banner_show_information (NULL, NULL, _("mail_ib_nothing_to_find"));
1630 g_object_get (G_OBJECT (widget), "prefix", ¤t_search, NULL);
1632 if ((current_search == NULL) || (strcmp (current_search, "") == 0)) {
1633 g_free (current_search);
1634 hildon_banner_show_information (NULL, NULL, _CS("ecdg_ib_find_rep_enter_text"));
1638 if ((priv->last_search == NULL) || (strcmp (priv->last_search, current_search) != 0)) {
1640 g_free (priv->last_search);
1641 priv->last_search = g_strdup (current_search);
1642 result = modest_isearch_view_search (MODEST_ISEARCH_VIEW (priv->msg_view),
1645 hildon_banner_show_information (NULL, NULL,
1646 _HL("ckct_ib_find_no_matches"));
1647 g_free (priv->last_search);
1648 priv->last_search = NULL;
1650 hildon_find_toolbar_highlight_entry (HILDON_FIND_TOOLBAR (priv->find_toolbar), TRUE);
1653 if (!modest_isearch_view_search_next (MODEST_ISEARCH_VIEW (priv->msg_view))) {
1654 hildon_banner_show_information (NULL, NULL,
1655 _HL("ckct_ib_find_search_complete"));
1656 g_free (priv->last_search);
1657 priv->last_search = NULL;
1659 hildon_find_toolbar_highlight_entry (HILDON_FIND_TOOLBAR (priv->find_toolbar), TRUE);
1663 g_free (current_search);
1668 modest_msg_view_window_set_zoom (ModestWindow *window,
1671 ModestMsgViewWindowPrivate *priv;
1672 ModestWindowPrivate *parent_priv;
1674 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window));
1676 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1677 parent_priv = MODEST_WINDOW_GET_PRIVATE (window);
1678 modest_zoomable_set_zoom (MODEST_ZOOMABLE (priv->msg_view), zoom);
1683 modest_msg_view_window_get_zoom (ModestWindow *window)
1685 ModestMsgViewWindowPrivate *priv;
1687 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), 1.0);
1689 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1690 return modest_zoomable_get_zoom (MODEST_ZOOMABLE (priv->msg_view));
1694 modest_msg_view_window_zoom_plus (ModestWindow *window)
1697 ModestMsgViewWindowPrivate *priv;
1701 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), 1.0);
1702 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1704 zoom_level = modest_zoomable_get_zoom (MODEST_ZOOMABLE (priv->msg_view));
1706 if (zoom_level >= 2.0) {
1707 hildon_banner_show_information (NULL, NULL,
1708 _CS("ckct_ib_max_zoom_level_reached"));
1710 } else if (zoom_level >= 1.5) {
1712 } else if (zoom_level >= 1.2) {
1714 } else if (zoom_level >= 1.0) {
1716 } else if (zoom_level >= 0.8) {
1718 } else if (zoom_level >= 0.5) {
1724 /* set zoom level */
1725 int_zoom = (gint) rint (zoom_level*100.0+0.1);
1726 banner_text = g_strdup_printf (_HL("wdgt_ib_zoom"), int_zoom);
1727 modest_platform_information_banner (GTK_WIDGET (window), NULL, banner_text);
1728 g_free (banner_text);
1729 modest_zoomable_set_zoom (MODEST_ZOOMABLE (priv->msg_view), zoom_level);
1735 modest_msg_view_window_zoom_minus (ModestWindow *window)
1738 ModestMsgViewWindowPrivate *priv;
1742 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), 1.0);
1743 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1745 zoom_level = modest_zoomable_get_zoom (MODEST_ZOOMABLE (priv->msg_view));
1747 if (zoom_level <= 0.5) {
1748 hildon_banner_show_information (NULL, NULL,
1749 _CS("ckct_ib_min_zoom_level_reached"));
1751 } else if (zoom_level <= 0.8) {
1753 } else if (zoom_level <= 1.0) {
1755 } else if (zoom_level <= 1.2) {
1757 } else if (zoom_level <= 1.5) {
1759 } else if (zoom_level <= 2.0) {
1765 /* set zoom level */
1766 int_zoom = (gint) rint (zoom_level*100.0+0.1);
1767 banner_text = g_strdup_printf (_HL("wdgt_ib_zoom"), int_zoom);
1768 modest_platform_information_banner (GTK_WIDGET (window), NULL, banner_text);
1769 g_free (banner_text);
1770 modest_zoomable_set_zoom (MODEST_ZOOMABLE (priv->msg_view), zoom_level);
1776 modest_msg_view_window_key_event (GtkWidget *window,
1782 focus = gtk_window_get_focus (GTK_WINDOW (window));
1784 /* for the find toolbar case */
1785 if (focus && GTK_IS_ENTRY (focus)) {
1786 if (event->keyval == GDK_BackSpace) {
1788 copy = gdk_event_copy ((GdkEvent *) event);
1789 gtk_widget_event (focus, copy);
1790 gdk_event_free (copy);
1800 modest_msg_view_window_last_message_selected (ModestMsgViewWindow *window)
1803 ModestMsgViewWindowPrivate *priv;
1804 GtkTreeIter tmp_iter;
1805 gboolean is_last_selected;
1807 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), TRUE);
1808 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1810 /*if no model (so no rows at all), then virtually we are the last*/
1811 if (!priv->header_model || !priv->row_reference)
1814 if (!gtk_tree_row_reference_valid (priv->row_reference))
1817 path = gtk_tree_row_reference_get_path (priv->row_reference);
1821 is_last_selected = TRUE;
1822 while (is_last_selected) {
1824 gtk_tree_path_next (path);
1825 if (!gtk_tree_model_get_iter (priv->header_model, &tmp_iter, path))
1827 gtk_tree_model_get (priv->header_model, &tmp_iter,
1828 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
1831 if (msg_is_visible (header, priv->is_outbox))
1832 is_last_selected = FALSE;
1833 g_object_unref(G_OBJECT(header));
1836 gtk_tree_path_free (path);
1837 return is_last_selected;
1841 modest_msg_view_window_has_headers_model (ModestMsgViewWindow *window)
1843 ModestMsgViewWindowPrivate *priv;
1845 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), TRUE);
1846 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1848 return priv->header_model != NULL;
1852 modest_msg_view_window_is_search_result (ModestMsgViewWindow *window)
1854 ModestMsgViewWindowPrivate *priv;
1856 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), TRUE);
1857 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1859 return priv->is_search_result;
1863 msg_is_visible (TnyHeader *header, gboolean check_outbox)
1865 if ((tny_header_get_flags(header) & TNY_HEADER_FLAG_DELETED))
1867 if (!check_outbox) {
1870 ModestTnySendQueueStatus status;
1871 status = modest_tny_all_send_queues_get_msg_status (header);
1872 return ((status != MODEST_TNY_SEND_QUEUE_FAILED) &&
1873 (status != MODEST_TNY_SEND_QUEUE_SENDING));
1878 modest_msg_view_window_first_message_selected (ModestMsgViewWindow *window)
1881 ModestMsgViewWindowPrivate *priv;
1882 gboolean is_first_selected;
1883 GtkTreeIter tmp_iter;
1885 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), TRUE);
1886 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1888 /*if no model (so no rows at all), then virtually we are the first*/
1889 if (!priv->header_model || !priv->row_reference)
1892 if (!gtk_tree_row_reference_valid (priv->row_reference))
1895 path = gtk_tree_row_reference_get_path (priv->row_reference);
1899 is_first_selected = TRUE;
1900 while (is_first_selected) {
1902 if(!gtk_tree_path_prev (path))
1904 /* Here the 'if' is needless for logic, but let make sure
1905 * iter is valid for gtk_tree_model_get. */
1906 if (!gtk_tree_model_get_iter (priv->header_model, &tmp_iter, path))
1908 gtk_tree_model_get (priv->header_model, &tmp_iter,
1909 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
1912 if (msg_is_visible (header, priv->is_outbox))
1913 is_first_selected = FALSE;
1914 g_object_unref(G_OBJECT(header));
1917 gtk_tree_path_free (path);
1918 return is_first_selected;
1925 GtkTreeRowReference *row_reference;
1929 message_reader_performer (gboolean canceled,
1931 GtkWindow *parent_window,
1932 TnyAccount *account,
1935 ModestMailOperation *mail_op = NULL;
1936 MsgReaderInfo *info;
1938 info = (MsgReaderInfo *) user_data;
1939 if (canceled || err) {
1940 update_window_title (MODEST_MSG_VIEW_WINDOW (parent_window));
1941 modest_ui_actions_on_close_window (NULL, MODEST_WINDOW (parent_window));
1945 /* Register the header - it'll be unregistered in the callback */
1947 modest_window_mgr_register_header (modest_runtime_get_window_mgr (), info->header, NULL);
1949 /* New mail operation */
1950 mail_op = modest_mail_operation_new_with_error_handling (G_OBJECT(parent_window),
1951 modest_ui_actions_disk_operations_error_handler,
1954 modest_mail_operation_queue_add (modest_runtime_get_mail_operation_queue (), mail_op);
1956 modest_mail_operation_get_msg (mail_op, info->header, TRUE, view_msg_cb, info->row_reference);
1958 modest_mail_operation_find_msg (mail_op, info->folder, info->msg_uid, TRUE, view_msg_cb, NULL);
1959 g_object_unref (mail_op);
1961 /* Update dimming rules */
1962 modest_ui_actions_check_toolbar_dimming_rules (MODEST_WINDOW (parent_window));
1963 modest_ui_actions_check_menu_dimming_rules (MODEST_WINDOW (parent_window));
1966 /* Frees. The row_reference will be freed by the view_msg_cb callback */
1967 g_free (info->msg_uid);
1969 g_object_unref (info->folder);
1971 g_object_unref (info->header);
1972 g_slice_free (MsgReaderInfo, info);
1977 * Reads the message whose summary item is @header. It takes care of
1978 * several things, among others:
1980 * If the message was not previously downloaded then ask the user
1981 * before downloading. If there is no connection launch the connection
1982 * dialog. Update toolbar dimming rules.
1984 * Returns: TRUE if the mail operation was started, otherwise if the
1985 * user do not want to download the message, or if the user do not
1986 * want to connect, then the operation is not issued
1989 message_reader (ModestMsgViewWindow *window,
1990 ModestMsgViewWindowPrivate *priv,
1992 const gchar *msg_uid,
1994 GtkTreeRowReference *row_reference)
1996 ModestWindowMgr *mgr;
1997 TnyAccount *account = NULL;
1998 MsgReaderInfo *info;
2000 /* We set the header from model while we're loading */
2001 tny_header_view_set_header (TNY_HEADER_VIEW (priv->msg_view), header);
2002 gtk_window_set_title (GTK_WINDOW (window), _CS("ckdg_pb_updating"));
2008 g_object_ref (folder);
2010 mgr = modest_runtime_get_window_mgr ();
2011 /* Msg download completed */
2012 if (!header || !(tny_header_get_flags (header) & TNY_HEADER_FLAG_CACHED)) {
2014 /* Ask the user if he wants to download the message if
2016 if (!tny_device_is_online (modest_runtime_get_device())) {
2017 GtkResponseType response;
2019 response = modest_platform_run_confirmation_dialog (GTK_WINDOW (window),
2020 _("mcen_nc_get_msg"));
2021 if (response == GTK_RESPONSE_CANCEL) {
2022 update_window_title (window);
2027 folder = tny_header_get_folder (header);
2029 info = g_slice_new (MsgReaderInfo);
2030 info->msg_uid = g_strdup (msg_uid);
2032 info->header = g_object_ref (header);
2034 info->header = NULL;
2036 info->folder = g_object_ref (folder);
2038 info->folder = NULL;
2039 if (row_reference) {
2040 info->row_reference = gtk_tree_row_reference_copy (row_reference);
2042 info->row_reference = NULL;
2045 /* Offer the connection dialog if necessary */
2046 modest_platform_connect_if_remote_and_perform ((GtkWindow *) window,
2048 TNY_FOLDER_STORE (folder),
2049 message_reader_performer,
2052 g_object_unref (folder);
2058 folder = tny_header_get_folder (header);
2061 account = tny_folder_get_account (folder);
2063 info = g_slice_new (MsgReaderInfo);
2064 info->msg_uid = g_strdup (msg_uid);
2066 info->folder = g_object_ref (folder);
2068 info->folder = NULL;
2070 info->header = g_object_ref (header);
2072 info->header = NULL;
2074 info->row_reference = gtk_tree_row_reference_copy (row_reference);
2076 info->row_reference = NULL;
2078 message_reader_performer (FALSE, NULL, (GtkWindow *) window, account, info);
2080 g_object_unref (account);
2082 g_object_unref (folder);
2088 modest_msg_view_window_select_next_message (ModestMsgViewWindow *window)
2090 ModestMsgViewWindowPrivate *priv;
2091 GtkTreePath *path= NULL;
2092 GtkTreeIter tmp_iter;
2094 gboolean retval = TRUE;
2095 GtkTreeRowReference *row_reference = NULL;
2097 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), FALSE);
2098 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
2100 if (!priv->row_reference)
2103 /* Update the next row reference if it's not valid. This could
2104 happen if for example the header which it was pointing to,
2105 was deleted. The best place to do it is in the row-deleted
2106 handler but the tinymail model do not work like the glib
2107 tree models and reports the deletion when the row is still
2109 if (!gtk_tree_row_reference_valid (priv->next_row_reference)) {
2110 if (priv->next_row_reference) {
2111 gtk_tree_row_reference_free (priv->next_row_reference);
2113 if (gtk_tree_row_reference_valid (priv->row_reference)) {
2114 priv->next_row_reference = gtk_tree_row_reference_copy (priv->row_reference);
2115 select_next_valid_row (priv->header_model, &(priv->next_row_reference), FALSE, priv->is_outbox);
2117 priv->next_row_reference = NULL;
2120 if (priv->next_row_reference)
2121 path = gtk_tree_row_reference_get_path (priv->next_row_reference);
2125 row_reference = gtk_tree_row_reference_copy (priv->next_row_reference);
2127 gtk_tree_model_get_iter (priv->header_model,
2130 gtk_tree_path_free (path);
2132 gtk_tree_model_get (priv->header_model, &tmp_iter,
2133 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
2136 /* Read the message & show it */
2137 if (!message_reader (window, priv, header, NULL, NULL, row_reference)) {
2140 gtk_tree_row_reference_free (row_reference);
2143 g_object_unref (header);
2149 modest_msg_view_window_select_previous_message (ModestMsgViewWindow *window)
2151 ModestMsgViewWindowPrivate *priv = NULL;
2153 gboolean finished = FALSE;
2154 gboolean retval = FALSE;
2156 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), FALSE);
2157 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
2159 if (priv->row_reference && !gtk_tree_row_reference_valid (priv->row_reference)) {
2160 gtk_tree_row_reference_free (priv->row_reference);
2161 priv->row_reference = NULL;
2164 /* Return inmediatly if there is no header model */
2165 if (!priv->header_model || !priv->row_reference)
2168 path = gtk_tree_row_reference_get_path (priv->row_reference);
2169 while (!finished && gtk_tree_path_prev (path)) {
2173 gtk_tree_model_get_iter (priv->header_model, &iter, path);
2174 gtk_tree_model_get (priv->header_model, &iter,
2175 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
2179 if (msg_is_visible (header, priv->is_outbox)) {
2180 GtkTreeRowReference *row_reference;
2181 row_reference = gtk_tree_row_reference_new (priv->header_model, path);
2182 /* Read the message & show it */
2183 retval = message_reader (window, priv, header, NULL, NULL, row_reference);
2184 gtk_tree_row_reference_free (row_reference);
2188 g_object_unref (header);
2192 gtk_tree_path_free (path);
2197 view_msg_cb (ModestMailOperation *mail_op,
2204 ModestMsgViewWindow *self = NULL;
2205 ModestMsgViewWindowPrivate *priv = NULL;
2206 GtkTreeRowReference *row_reference = NULL;
2208 /* Unregister the header (it was registered before creating the mail operation) */
2209 modest_window_mgr_unregister_header (modest_runtime_get_window_mgr (), header);
2211 row_reference = (GtkTreeRowReference *) user_data;
2212 self = (ModestMsgViewWindow *) modest_mail_operation_get_source (mail_op);
2213 if (canceled || !self || MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self)->msg_view == NULL ) {
2215 gtk_tree_row_reference_free (row_reference);
2217 /* Restore window title */
2218 update_window_title (self);
2219 modest_ui_actions_on_close_window (NULL, MODEST_WINDOW (self));
2220 g_object_unref (self);
2225 /* If there was any error */
2226 if (!modest_ui_actions_msg_retrieval_check (mail_op, header, msg)) {
2228 gtk_tree_row_reference_free (row_reference);
2230 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
2231 /* First we check if the parent is a folder window */
2232 if (priv->msg_uid && !modest_hildon2_window_mgr_get_folder_window (MODEST_HILDON2_WINDOW_MGR (modest_runtime_get_window_mgr ()))) {
2234 TnyAccount *account = NULL;
2235 GtkWidget *header_window = NULL;
2237 is_merge = g_str_has_prefix (priv->msg_uid, "merge:");
2239 /* Get the account */
2241 account = tny_account_store_find_account (TNY_ACCOUNT_STORE (modest_runtime_get_account_store ()),
2244 if (is_merge || account) {
2245 TnyFolder *folder = NULL;
2247 /* Try to get the message, if it's already downloaded
2248 we don't need to connect */
2250 folder = modest_tny_folder_store_find_folder_from_uri (TNY_FOLDER_STORE (account),
2253 ModestTnyAccountStore *account_store;
2254 ModestTnyLocalFoldersAccount *local_folders_account;
2256 account_store = modest_runtime_get_account_store ();
2257 local_folders_account = MODEST_TNY_LOCAL_FOLDERS_ACCOUNT (
2258 modest_tny_account_store_get_local_folders_account (account_store));
2259 folder = modest_tny_local_folders_account_get_merged_outbox (local_folders_account);
2260 g_object_unref (local_folders_account);
2262 if (account) g_object_unref (account);
2265 header_window = (GtkWidget *)
2266 modest_header_window_new (
2268 modest_window_get_active_account (MODEST_WINDOW (self)),
2269 modest_window_get_active_mailbox (MODEST_WINDOW (self)));
2270 if (!modest_window_mgr_register_window (modest_runtime_get_window_mgr (),
2271 MODEST_WINDOW (header_window),
2273 gtk_widget_destroy (GTK_WIDGET (header_window));
2275 gtk_widget_show_all (GTK_WIDGET (header_window));
2277 g_object_unref (folder);
2283 /* Restore window title */
2284 update_window_title (self);
2285 modest_ui_actions_on_close_window (NULL, MODEST_WINDOW (self));
2286 g_object_unref (self);
2291 /* Get the window */
2292 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self));
2293 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
2295 /* Update the row reference */
2296 if (priv->row_reference != NULL) {
2297 gtk_tree_row_reference_free (priv->row_reference);
2298 priv->row_reference = (row_reference && gtk_tree_row_reference_valid (row_reference))?gtk_tree_row_reference_copy (row_reference):NULL;
2299 if (priv->next_row_reference != NULL) {
2300 gtk_tree_row_reference_free (priv->next_row_reference);
2302 if (priv->row_reference) {
2303 priv->next_row_reference = gtk_tree_row_reference_copy (priv->row_reference);
2304 select_next_valid_row (priv->header_model, &(priv->next_row_reference), TRUE, priv->is_outbox);
2306 priv->next_row_reference = NULL;
2310 /* Mark header as read */
2311 if (!(tny_header_get_flags (header) & TNY_HEADER_FLAG_SEEN)) {
2314 tny_header_set_flag (header, TNY_HEADER_FLAG_SEEN);
2315 uid = modest_tny_folder_get_header_unique_id (header);
2316 modest_platform_emit_msg_read_changed_signal (uid, TRUE);
2320 /* Set new message */
2321 if (priv->msg_view != NULL && TNY_IS_MSG_VIEW (priv->msg_view)) {
2322 tny_msg_view_set_msg (TNY_MSG_VIEW (priv->msg_view), msg);
2323 modest_msg_view_window_update_priority (self);
2324 update_window_title (MODEST_MSG_VIEW_WINDOW (self));
2325 update_branding (MODEST_MSG_VIEW_WINDOW (self));
2326 modest_msg_view_grab_focus (MODEST_MSG_VIEW (priv->msg_view));
2329 /* Set the new message uid of the window */
2330 if (priv->msg_uid) {
2331 g_free (priv->msg_uid);
2332 priv->msg_uid = modest_tny_folder_get_header_unique_id (header);
2335 /* Notify the observers */
2336 g_signal_emit (G_OBJECT (self), signals[MSG_CHANGED_SIGNAL],
2337 0, priv->header_model, priv->row_reference);
2339 /* Sync the flags if the message is not opened from a header
2340 model, i.e, if it's opened from a notification */
2341 if (!priv->header_model)
2345 g_object_unref (self);
2347 gtk_tree_row_reference_free (row_reference);
2351 modest_msg_view_window_get_folder_type (ModestMsgViewWindow *window)
2353 ModestMsgViewWindowPrivate *priv;
2355 TnyFolderType folder_type;
2357 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
2359 folder_type = TNY_FOLDER_TYPE_UNKNOWN;
2361 msg = tny_msg_view_get_msg (TNY_MSG_VIEW (priv->msg_view));
2365 folder = tny_msg_get_folder (msg);
2367 folder_type = modest_tny_folder_guess_folder_type (folder);
2368 g_object_unref (folder);
2370 g_object_unref (msg);
2378 modest_msg_view_window_update_priority (ModestMsgViewWindow *window)
2380 ModestMsgViewWindowPrivate *priv;
2381 TnyHeader *header = NULL;
2382 TnyHeaderFlags flags = 0;
2384 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
2386 if (priv->header_model && priv->row_reference && gtk_tree_row_reference_valid (priv->row_reference)) {
2388 GtkTreePath *path = NULL;
2390 path = gtk_tree_row_reference_get_path (priv->row_reference);
2391 g_return_if_fail (path != NULL);
2392 gtk_tree_model_get_iter (priv->header_model,
2394 gtk_tree_row_reference_get_path (priv->row_reference));
2396 gtk_tree_model_get (priv->header_model, &iter, TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
2398 gtk_tree_path_free (path);
2401 msg = tny_msg_view_get_msg (TNY_MSG_VIEW (priv->msg_view));
2403 header = tny_msg_get_header (msg);
2404 g_object_unref (msg);
2409 flags = tny_header_get_flags (header);
2410 g_object_unref(G_OBJECT(header));
2413 modest_msg_view_set_priority (MODEST_MSG_VIEW(priv->msg_view), flags);
2418 toolbar_resize (ModestMsgViewWindow *self)
2420 ModestMsgViewWindowPrivate *priv = NULL;
2421 ModestWindowPrivate *parent_priv = NULL;
2423 gint static_button_size;
2424 ModestWindowMgr *mgr;
2426 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self));
2427 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
2428 parent_priv = MODEST_WINDOW_GET_PRIVATE(self);
2430 mgr = modest_runtime_get_window_mgr ();
2431 static_button_size = modest_window_mgr_get_fullscreen_mode (mgr)?120:120;
2433 if (parent_priv->toolbar) {
2434 /* Set expandable and homogeneous tool buttons */
2435 widget = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/ToolbarMessageReply");
2436 gtk_tool_item_set_expand (GTK_TOOL_ITEM (widget), TRUE);
2437 gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (widget), TRUE);
2438 widget = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/ToolbarMessageReplyAll");
2439 gtk_tool_item_set_expand (GTK_TOOL_ITEM (widget), TRUE);
2440 gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (widget), TRUE);
2441 widget = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/ToolbarMessageForward");
2442 gtk_tool_item_set_expand (GTK_TOOL_ITEM (widget), TRUE);
2443 gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (widget), TRUE);
2444 widget = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/ToolbarDeleteMessage");
2445 gtk_tool_item_set_expand (GTK_TOOL_ITEM (widget), TRUE);
2446 gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (widget), TRUE);
2447 widget = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/ToolbarDownloadExternalImages");
2448 gtk_tool_item_set_expand (GTK_TOOL_ITEM (widget), TRUE);
2449 gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (widget), TRUE);
2450 gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (priv->next_toolitem), TRUE);
2451 gtk_tool_item_set_expand (GTK_TOOL_ITEM (priv->next_toolitem), TRUE);
2452 gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (priv->prev_toolitem), TRUE);
2453 gtk_tool_item_set_expand (GTK_TOOL_ITEM (priv->prev_toolitem), TRUE);
2458 modest_msg_view_window_show_toolbar (ModestWindow *self,
2459 gboolean show_toolbar)
2461 ModestMsgViewWindowPrivate *priv = NULL;
2462 ModestWindowPrivate *parent_priv;
2464 parent_priv = MODEST_WINDOW_GET_PRIVATE(self);
2465 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(self);
2467 /* Set optimized view status */
2468 priv->optimized_view = !show_toolbar;
2470 if (!parent_priv->toolbar) {
2471 parent_priv->toolbar = gtk_ui_manager_get_widget (parent_priv->ui_manager,
2473 gtk_toolbar_set_icon_size (GTK_TOOLBAR (parent_priv->toolbar), HILDON_ICON_SIZE_FINGER);
2474 gtk_widget_set_no_show_all (parent_priv->toolbar, TRUE);
2476 priv->next_toolitem = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/ToolbarMessageNext");
2477 priv->prev_toolitem = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/ToolbarMessageBack");
2478 toolbar_resize (MODEST_MSG_VIEW_WINDOW (self));
2481 hildon_window_add_toolbar (HILDON_WINDOW (self),
2482 GTK_TOOLBAR (parent_priv->toolbar));
2487 /* Quick hack: this prevents toolbar icons "dance" when progress bar show status is changed */
2488 /* TODO: resize mode migth be GTK_RESIZE_QUEUE, in order to avoid unneccesary shows */
2489 gtk_container_set_resize_mode (GTK_CONTAINER(parent_priv->toolbar), GTK_RESIZE_IMMEDIATE);
2491 gtk_widget_show (GTK_WIDGET (parent_priv->toolbar));
2492 if (modest_msg_view_window_transfer_mode_enabled (MODEST_MSG_VIEW_WINDOW (self)))
2493 set_progress_hint (MODEST_MSG_VIEW_WINDOW (self), TRUE);
2495 set_progress_hint (MODEST_MSG_VIEW_WINDOW (self), FALSE);
2498 gtk_widget_set_no_show_all (parent_priv->toolbar, TRUE);
2499 gtk_widget_hide (GTK_WIDGET (parent_priv->toolbar));
2504 modest_msg_view_window_clipboard_owner_change (GtkClipboard *clipboard,
2506 ModestMsgViewWindow *window)
2508 if (!GTK_WIDGET_VISIBLE (window))
2511 modest_window_check_dimming_rules_group (MODEST_WINDOW (window), MODEST_DIMMING_RULES_CLIPBOARD);
2515 modest_msg_view_window_transfer_mode_enabled (ModestMsgViewWindow *self)
2517 ModestMsgViewWindowPrivate *priv;
2519 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self), FALSE);
2520 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(self);
2522 return priv->progress_hint;
2526 observers_empty (ModestMsgViewWindow *self)
2529 ModestMsgViewWindowPrivate *priv;
2530 gboolean is_empty = TRUE;
2531 guint pending_ops = 0;
2533 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(self);
2534 tmp = priv->progress_widgets;
2536 /* Check all observers */
2537 while (tmp && is_empty) {
2538 pending_ops = modest_progress_object_num_pending_operations (MODEST_PROGRESS_OBJECT(tmp->data));
2539 is_empty = pending_ops == 0;
2541 tmp = g_slist_next(tmp);
2548 on_account_removed (TnyAccountStore *account_store,
2549 TnyAccount *account,
2552 /* Do nothing if it's a transport account, because we only
2553 show the messages of a store account */
2554 if (tny_account_get_account_type(account) == TNY_ACCOUNT_TYPE_STORE) {
2555 const gchar *parent_acc = NULL;
2556 const gchar *our_acc = NULL;
2558 our_acc = modest_window_get_active_account (MODEST_WINDOW (user_data));
2559 parent_acc = modest_tny_account_get_parent_modest_account_name_for_server_account (account);
2561 /* Close this window if I'm showing a message of the removed account */
2562 if (our_acc && parent_acc && strcmp (parent_acc, our_acc) == 0)
2563 modest_ui_actions_on_close_window (NULL, MODEST_WINDOW (user_data));
2568 on_mail_operation_started (ModestMailOperation *mail_op,
2571 ModestMsgViewWindow *self;
2572 ModestMailOperationTypeOperation op_type;
2574 ModestMsgViewWindowPrivate *priv;
2575 GObject *source = NULL;
2577 self = MODEST_MSG_VIEW_WINDOW (user_data);
2578 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
2579 op_type = modest_mail_operation_get_type_operation (mail_op);
2580 tmp = priv->progress_widgets;
2581 source = modest_mail_operation_get_source(mail_op);
2582 if (G_OBJECT (self) == source) {
2583 if (op_type == MODEST_MAIL_OPERATION_TYPE_RECEIVE ||
2584 op_type == MODEST_MAIL_OPERATION_TYPE_OPEN ||
2585 op_type == MODEST_MAIL_OPERATION_TYPE_DELETE) {
2586 set_progress_hint (self, TRUE);
2588 modest_progress_object_add_operation (
2589 MODEST_PROGRESS_OBJECT (tmp->data),
2591 tmp = g_slist_next (tmp);
2595 g_object_unref (source);
2597 /* Update dimming rules */
2598 check_dimming_rules_after_change (self);
2602 on_mail_operation_finished (ModestMailOperation *mail_op,
2605 ModestMsgViewWindow *self;
2606 ModestMailOperationTypeOperation op_type;
2608 ModestMsgViewWindowPrivate *priv;
2610 self = MODEST_MSG_VIEW_WINDOW (user_data);
2611 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
2612 op_type = modest_mail_operation_get_type_operation (mail_op);
2613 tmp = priv->progress_widgets;
2615 if (op_type == MODEST_MAIL_OPERATION_TYPE_RECEIVE ||
2616 op_type == MODEST_MAIL_OPERATION_TYPE_OPEN ||
2617 op_type == MODEST_MAIL_OPERATION_TYPE_DELETE) {
2619 modest_progress_object_remove_operation (MODEST_PROGRESS_OBJECT (tmp->data),
2621 tmp = g_slist_next (tmp);
2624 /* If no more operations are being observed, NORMAL mode is enabled again */
2625 if (observers_empty (self)) {
2626 set_progress_hint (self, FALSE);
2630 /* Update dimming rules. We have to do this right here
2631 and not in view_msg_cb because at that point the
2632 transfer mode is still enabled so the dimming rule
2633 won't let the user delete the message that has been
2634 readed for example */
2635 check_dimming_rules_after_change (self);
2639 on_queue_changed (ModestMailOperationQueue *queue,
2640 ModestMailOperation *mail_op,
2641 ModestMailOperationQueueNotification type,
2642 ModestMsgViewWindow *self)
2644 ModestMsgViewWindowPrivate *priv;
2646 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
2648 /* If this operations was created by another window, do nothing */
2649 if (!modest_mail_operation_is_mine (mail_op, G_OBJECT(self)))
2652 if (type == MODEST_MAIL_OPERATION_QUEUE_OPERATION_ADDED) {
2653 priv->sighandlers = modest_signal_mgr_connect (priv->sighandlers,
2655 "operation-started",
2656 G_CALLBACK (on_mail_operation_started),
2658 priv->sighandlers = modest_signal_mgr_connect (priv->sighandlers,
2660 "operation-finished",
2661 G_CALLBACK (on_mail_operation_finished),
2663 } else if (type == MODEST_MAIL_OPERATION_QUEUE_OPERATION_REMOVED) {
2664 priv->sighandlers = modest_signal_mgr_disconnect (priv->sighandlers,
2666 "operation-started");
2667 priv->sighandlers = modest_signal_mgr_disconnect (priv->sighandlers,
2669 "operation-finished");
2674 modest_msg_view_window_get_attachments (ModestMsgViewWindow *win)
2676 ModestMsgViewWindowPrivate *priv;
2677 TnyList *selected_attachments = NULL;
2679 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (win), NULL);
2680 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (win);
2682 /* In Hildon 2.2 as there's no selection we assume we have all attachments selected */
2683 selected_attachments = modest_msg_view_get_attachments (MODEST_MSG_VIEW (priv->msg_view));
2685 return selected_attachments;
2689 ModestMsgViewWindow *self;
2691 gchar *attachment_uid;
2692 } DecodeAsyncHelper;
2695 on_decode_to_stream_async_handler (TnyMimePart *mime_part,
2701 DecodeAsyncHelper *helper = (DecodeAsyncHelper *) user_data;
2702 const gchar *content_type;
2703 ModestMsgViewWindowPrivate *priv;
2705 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (helper->self);
2707 if (cancelled || err) {
2710 if ((err->domain == TNY_ERROR_DOMAIN) &&
2711 (err->code == TNY_IO_ERROR_WRITE) &&
2712 (errno == ENOSPC)) {
2713 msg = g_strdup_printf (_KR("cerm_device_memory_full"), "");
2715 msg = g_strdup (_("mail_ib_file_operation_failed"));
2717 modest_platform_information_banner (NULL, NULL, msg);
2723 /* It could happen that the window was closed. So we
2724 assume it is a cancelation */
2725 if (!GTK_WIDGET_VISIBLE (helper->self))
2728 /* Remove the progress hint */
2729 set_progress_hint (helper->self, FALSE);
2731 content_type = tny_mime_part_get_content_type (mime_part);
2732 if (content_type && g_str_has_prefix (content_type, "message/rfc822")) {
2733 ModestWindowMgr *mgr;
2734 ModestWindow *msg_win = NULL;
2737 const gchar *mailbox;
2738 TnyStream *file_stream;
2741 fd = g_open (helper->file_path, O_RDONLY, 0644);
2744 file_stream = tny_fs_stream_new (fd);
2746 mgr = modest_runtime_get_window_mgr ();
2748 account = g_strdup (modest_window_get_active_account (MODEST_WINDOW (helper->self)));
2749 mailbox = modest_window_get_active_mailbox (MODEST_WINDOW (helper->self));
2752 account = modest_account_mgr_get_default_account (modest_runtime_get_account_mgr ());
2754 msg = tny_camel_msg_new ();
2755 tny_camel_msg_parse (msg, file_stream);
2758 top_msg = g_object_ref (priv->top_msg);
2760 top_msg = tny_msg_view_get_msg (TNY_MSG_VIEW (priv->msg_view));
2762 msg_win = modest_msg_view_window_new_for_attachment (TNY_MSG (msg), top_msg,
2763 account, mailbox, helper->attachment_uid);
2764 if (top_msg) g_object_unref (top_msg);
2765 modest_window_set_zoom (MODEST_WINDOW (msg_win),
2766 modest_window_get_zoom (MODEST_WINDOW (helper->self)));
2767 if (modest_window_mgr_register_window (mgr, msg_win, MODEST_WINDOW (helper->self)))
2768 gtk_widget_show_all (GTK_WIDGET (msg_win));
2770 gtk_widget_destroy (GTK_WIDGET (msg_win));
2771 g_object_unref (msg);
2772 g_object_unref (file_stream);
2774 modest_platform_information_banner (NULL, NULL, _("mail_ib_file_operation_failed"));
2779 /* make the file read-only */
2780 g_chmod(helper->file_path, 0444);
2782 /* Activate the file */
2783 modest_platform_activate_file (helper->file_path, content_type);
2788 g_object_unref (helper->self);
2789 g_free (helper->file_path);
2790 g_free (helper->attachment_uid);
2791 g_slice_free (DecodeAsyncHelper, helper);
2795 view_attachment_connect_handler (gboolean canceled,
2797 GtkWindow *parent_window,
2798 TnyAccount *account,
2802 if (canceled || err) {
2803 g_object_unref (part);
2807 modest_msg_view_window_view_attachment (MODEST_MSG_VIEW_WINDOW (parent_window),
2809 g_object_unref (part);
2813 modest_msg_view_window_view_attachment (ModestMsgViewWindow *window,
2814 TnyMimePart *mime_part)
2816 ModestMsgViewWindowPrivate *priv;
2817 const gchar *msg_uid;
2818 gchar *attachment_uid = NULL;
2819 gint attachment_index = 0;
2820 TnyList *attachments;
2822 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window));
2823 g_return_if_fail (TNY_IS_MIME_PART (mime_part) || (mime_part == NULL));
2824 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
2826 msg_uid = modest_msg_view_window_get_message_uid (MODEST_MSG_VIEW_WINDOW (window));
2827 attachments = modest_msg_view_get_attachments (MODEST_MSG_VIEW (priv->msg_view));
2828 attachment_index = modest_list_index (attachments, (GObject *) mime_part);
2829 g_object_unref (attachments);
2831 if (msg_uid && attachment_index >= 0) {
2832 attachment_uid = g_strdup_printf ("%s/%d", msg_uid, attachment_index);
2835 if (mime_part == NULL) {
2836 gboolean error = FALSE;
2837 TnyList *selected_attachments = modest_msg_view_get_selected_attachments (MODEST_MSG_VIEW (priv->msg_view));
2838 if (selected_attachments == NULL || tny_list_get_length (selected_attachments) == 0) {
2840 } else if (tny_list_get_length (selected_attachments) > 1) {
2841 hildon_banner_show_information (NULL, NULL, _("mcen_ib_unable_to_display_more"));
2845 iter = tny_list_create_iterator (selected_attachments);
2846 mime_part = (TnyMimePart *) tny_iterator_get_current (iter);
2847 g_object_unref (iter);
2849 if (selected_attachments)
2850 g_object_unref (selected_attachments);
2855 g_object_ref (mime_part);
2858 if (tny_mime_part_is_purged (mime_part))
2861 if (TNY_IS_CAMEL_BS_MIME_PART (mime_part) &&
2862 !tny_camel_bs_mime_part_is_fetched (TNY_CAMEL_BS_MIME_PART (mime_part))) {
2864 TnyAccount *account;
2866 is_merge = g_str_has_prefix (priv->msg_uid, "merge:");
2868 /* Get the account */
2870 account = tny_account_store_find_account (TNY_ACCOUNT_STORE (modest_runtime_get_account_store ()),
2873 if (!tny_device_is_online (modest_runtime_get_device())) {
2874 modest_platform_connect_and_perform (GTK_WINDOW (window),
2876 TNY_ACCOUNT (account),
2877 (ModestConnectedPerformer) view_attachment_connect_handler,
2878 g_object_ref (mime_part));
2883 if (!modest_tny_mime_part_is_msg (mime_part) && tny_mime_part_get_filename (mime_part)) {
2884 gchar *filepath = NULL;
2885 const gchar *att_filename = tny_mime_part_get_filename (mime_part);
2886 gboolean show_error_banner = FALSE;
2887 TnyFsStream *temp_stream = NULL;
2888 temp_stream = modest_utils_create_temp_stream (att_filename, attachment_uid,
2891 if (temp_stream != NULL) {
2892 ModestAccountMgr *mgr;
2893 DecodeAsyncHelper *helper;
2894 gboolean decode_in_provider;
2895 ModestProtocol *protocol;
2896 const gchar *account;
2898 /* Activate progress hint */
2899 set_progress_hint (window, TRUE);
2901 helper = g_slice_new0 (DecodeAsyncHelper);
2902 helper->self = g_object_ref (window);
2903 helper->file_path = g_strdup (filepath);
2904 helper->attachment_uid = g_strdup (attachment_uid);
2906 decode_in_provider = FALSE;
2907 mgr = modest_runtime_get_account_mgr ();
2908 account = modest_window_get_active_account (MODEST_WINDOW (window));
2909 if (modest_account_mgr_account_is_multimailbox (mgr, account, &protocol)) {
2910 if (MODEST_IS_ACCOUNT_PROTOCOL (protocol)) {
2912 uri = g_strconcat ("file://", filepath, NULL);
2913 decode_in_provider =
2914 modest_account_protocol_decode_part_to_stream_async (
2915 MODEST_ACCOUNT_PROTOCOL (protocol),
2918 TNY_STREAM (temp_stream),
2919 on_decode_to_stream_async_handler,
2926 if (!decode_in_provider)
2927 tny_mime_part_decode_to_stream_async (mime_part, TNY_STREAM (temp_stream),
2928 on_decode_to_stream_async_handler,
2931 g_object_unref (temp_stream);
2932 /* NOTE: files in the temporary area will be automatically
2933 * cleaned after some time if they are no longer in use */
2936 const gchar *content_type;
2937 /* the file may already exist but it isn't writable,
2938 * let's try to open it anyway */
2939 content_type = tny_mime_part_get_content_type (mime_part);
2940 modest_platform_activate_file (filepath, content_type);
2942 g_warning ("%s: modest_utils_create_temp_stream failed", __FUNCTION__);
2943 show_error_banner = TRUE;
2948 if (show_error_banner)
2949 modest_platform_information_banner (NULL, NULL, _("mail_ib_file_operation_failed"));
2950 } else if (!modest_tny_mime_part_is_msg (mime_part)) {
2951 ModestWindowMgr *mgr;
2952 ModestWindow *msg_win = NULL;
2953 TnyMsg *current_msg;
2957 current_msg = modest_msg_view_window_get_message (MODEST_MSG_VIEW_WINDOW (window));
2958 mgr = modest_runtime_get_window_mgr ();
2959 header = tny_msg_get_header (TNY_MSG (current_msg));
2960 found = modest_window_mgr_find_registered_message_uid (mgr,
2965 g_debug ("window for this body is already being created");
2969 /* it's not found, so create a new window for it */
2970 modest_window_mgr_register_header (mgr, header, attachment_uid); /* register the uid before building the window */
2971 gchar *account = g_strdup (modest_window_get_active_account (MODEST_WINDOW (window)));
2972 const gchar *mailbox = modest_window_get_active_mailbox (MODEST_WINDOW (window));
2974 account = modest_account_mgr_get_default_account (modest_runtime_get_account_mgr ());
2977 top_msg = g_object_ref (priv->top_msg);
2979 top_msg = tny_msg_view_get_msg (TNY_MSG_VIEW (priv->msg_view));
2981 msg_win = modest_msg_view_window_new_with_other_body (TNY_MSG (current_msg), TNY_MIME_PART (mime_part), top_msg,
2982 account, mailbox, attachment_uid);
2984 if (top_msg) g_object_unref (top_msg);
2986 modest_window_set_zoom (MODEST_WINDOW (msg_win),
2987 modest_window_get_zoom (MODEST_WINDOW (window)));
2988 if (modest_window_mgr_register_window (mgr, msg_win, MODEST_WINDOW (window)))
2989 gtk_widget_show_all (GTK_WIDGET (msg_win));
2991 gtk_widget_destroy (GTK_WIDGET (msg_win));
2993 g_object_unref (current_msg);
2995 /* message attachment */
2996 TnyHeader *header = NULL;
2997 ModestWindowMgr *mgr;
2998 ModestWindow *msg_win = NULL;
3001 header = tny_msg_get_header (TNY_MSG (mime_part));
3002 mgr = modest_runtime_get_window_mgr ();
3003 found = modest_window_mgr_find_registered_header (mgr, header, &msg_win);
3006 /* if it's found, but there is no msg_win, it's probably in the process of being created;
3007 * thus, we don't do anything */
3008 g_debug ("window for is already being created");
3011 /* it's not found, so create a new window for it */
3012 modest_window_mgr_register_header (mgr, header, attachment_uid); /* register the uid before building the window */
3013 gchar *account = g_strdup (modest_window_get_active_account (MODEST_WINDOW (window)));
3014 const gchar *mailbox = modest_window_get_active_mailbox (MODEST_WINDOW (window));
3016 account = modest_account_mgr_get_default_account (modest_runtime_get_account_mgr ());
3018 top_msg = g_object_ref (priv->top_msg);
3020 top_msg = tny_msg_view_get_msg (TNY_MSG_VIEW (priv->msg_view));
3021 msg_win = modest_msg_view_window_new_for_attachment (
3022 TNY_MSG (mime_part), top_msg, account,
3023 mailbox, attachment_uid);
3024 modest_window_set_zoom (MODEST_WINDOW (msg_win),
3025 modest_window_get_zoom (MODEST_WINDOW (window)));
3026 if (modest_window_mgr_register_window (mgr, msg_win, MODEST_WINDOW (window)))
3027 gtk_widget_show_all (GTK_WIDGET (msg_win));
3029 gtk_widget_destroy (GTK_WIDGET (msg_win));
3035 g_free (attachment_uid);
3037 g_object_unref (mime_part);
3049 GnomeVFSResult result;
3051 ModestMsgViewWindow *window;
3054 static void save_mime_part_info_free (SaveMimePartInfo *info, gboolean with_struct);
3055 static gboolean idle_save_mime_part_show_result (SaveMimePartInfo *info);
3056 static gpointer save_mime_part_to_file (SaveMimePartInfo *info);
3057 static void save_mime_parts_to_file_with_checks (GtkWindow *parent, SaveMimePartInfo *info);
3060 save_mime_part_info_free (SaveMimePartInfo *info, gboolean with_struct)
3064 for (node = info->pairs; node != NULL; node = g_list_next (node)) {
3065 SaveMimePartPair *pair = (SaveMimePartPair *) node->data;
3066 g_free (pair->filename);
3067 g_object_unref (pair->part);
3068 g_slice_free (SaveMimePartPair, pair);
3070 g_list_free (info->pairs);
3073 g_object_unref (info->window);
3074 info->window = NULL;
3076 g_slice_free (SaveMimePartInfo, info);
3081 idle_save_mime_part_show_result (SaveMimePartInfo *info)
3083 /* This is a GDK lock because we are an idle callback and
3084 * hildon_banner_show_information is or does Gtk+ code */
3086 gdk_threads_enter (); /* CHECKED */
3087 if (info->result == GNOME_VFS_ERROR_CANCELLED) {
3089 } else if (info->result == GNOME_VFS_OK) {
3090 hildon_banner_show_information (NULL, NULL, _CS("sfil_ib_saved"));
3091 } else if (info->result == GNOME_VFS_ERROR_NO_SPACE) {
3094 /* Check if the uri belongs to the external mmc */
3095 if (g_str_has_prefix (info->uri, g_getenv (MODEST_MMC1_VOLUMEPATH_ENV)))
3096 msg = g_strdup_printf (_KR("cerm_device_memory_full"), "");
3098 msg = g_strdup (_KR("cerm_memory_card_full"));
3099 modest_platform_information_banner (NULL, NULL, msg);
3102 hildon_banner_show_information (NULL, NULL, _("mail_ib_file_operation_failed"));
3104 set_progress_hint (info->window, FALSE);
3105 save_mime_part_info_free (info, FALSE);
3106 gdk_threads_leave (); /* CHECKED */
3112 save_mime_part_to_file_connect_handler (gboolean canceled,
3114 GtkWindow *parent_window,
3115 TnyAccount *account,
3116 SaveMimePartInfo *info)
3118 if (canceled || err) {
3119 if (canceled && !err) {
3120 info->result = GNOME_VFS_ERROR_CANCELLED;
3122 g_idle_add ((GSourceFunc) idle_save_mime_part_show_result, info);
3124 g_thread_create ((GThreadFunc)save_mime_part_to_file, info, FALSE, NULL);
3129 save_mime_part_to_file_connect_idle (SaveMimePartInfo *info)
3132 TnyAccount *account;
3133 ModestMsgViewWindowPrivate *priv;
3135 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (info->window);
3137 is_merge = g_str_has_prefix (priv->msg_uid, "merge:");
3140 /* Get the account */
3142 account = tny_account_store_find_account (TNY_ACCOUNT_STORE (modest_runtime_get_account_store ()),
3145 modest_platform_connect_and_perform (GTK_WINDOW (info->window),
3147 TNY_ACCOUNT (account),
3148 (ModestConnectedPerformer) save_mime_part_to_file_connect_handler,
3152 g_object_unref (account);
3158 save_mime_part_to_file (SaveMimePartInfo *info)
3160 GnomeVFSHandle *handle;
3162 SaveMimePartPair *pair = (SaveMimePartPair *) info->pairs->data;
3164 if (TNY_IS_CAMEL_BS_MIME_PART (pair->part) &&
3165 !tny_camel_bs_mime_part_is_fetched (TNY_CAMEL_BS_MIME_PART (pair->part))) {
3166 gboolean check_online = TRUE;
3167 ModestMsgViewWindowPrivate *priv = NULL;
3169 /* Check if we really need to connect to save the mime part */
3170 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (info->window);
3171 if (g_str_has_prefix (priv->msg_uid, "merge:")) {
3172 check_online = FALSE;
3174 TnyAccountStore *acc_store;
3175 TnyAccount *account = NULL;
3177 acc_store = (TnyAccountStore*) modest_runtime_get_account_store ();
3178 account = tny_account_store_find_account (acc_store, priv->msg_uid);
3181 if (tny_account_get_connection_status (account) ==
3182 TNY_CONNECTION_STATUS_CONNECTED)
3183 check_online = FALSE;
3184 g_object_unref (account);
3186 check_online = !tny_device_is_online (tny_account_store_get_device (acc_store));
3191 g_idle_add ((GSourceFunc) save_mime_part_to_file_connect_idle, info);
3196 info->result = gnome_vfs_create (&handle, pair->filename, GNOME_VFS_OPEN_WRITE, FALSE, 0644);
3197 if (info->result == GNOME_VFS_OK) {
3198 GError *error = NULL;
3199 gboolean decode_in_provider;
3201 ModestAccountMgr *mgr;
3202 const gchar *account;
3203 ModestProtocol *protocol = NULL;
3205 stream = tny_vfs_stream_new (handle);
3207 decode_in_provider = FALSE;
3208 mgr = modest_runtime_get_account_mgr ();
3209 account = modest_window_get_active_account (MODEST_WINDOW (info->window));
3210 if (modest_account_mgr_account_is_multimailbox (mgr, account, &protocol)) {
3211 if (MODEST_IS_ACCOUNT_PROTOCOL (protocol)) {
3212 decode_in_provider =
3213 modest_account_protocol_decode_part_to_stream (
3214 MODEST_ACCOUNT_PROTOCOL (protocol),
3222 if (!decode_in_provider)
3223 written = tny_mime_part_decode_to_stream (pair->part, stream, &error);
3226 g_warning ("modest: could not save attachment %s: %d (%s)\n", pair->filename, error?error->code:-1, error?error->message:"Unknown error");
3228 if (error && (error->domain == TNY_ERROR_DOMAIN) &&
3229 (error->code == TNY_IO_ERROR_WRITE) &&
3230 (errno == ENOSPC)) {
3231 info->result = GNOME_VFS_ERROR_NO_SPACE;
3233 info->result = GNOME_VFS_ERROR_IO;
3236 g_object_unref (G_OBJECT (stream));
3238 g_warning ("Could not create save attachment %s: %s\n",
3239 pair->filename, gnome_vfs_result_to_string (info->result));
3242 /* Go on saving remaining files */
3243 info->pairs = g_list_remove_link (info->pairs, info->pairs);
3244 if (info->pairs != NULL) {
3245 save_mime_part_to_file (info);
3247 g_idle_add ((GSourceFunc) idle_save_mime_part_show_result, info);
3254 save_mime_parts_to_file_with_checks (GtkWindow *parent,
3255 SaveMimePartInfo *info)
3257 gboolean is_ok = TRUE;
3258 gint replaced_files = 0;
3259 const GList *files = info->pairs;
3260 const GList *iter, *to_replace = NULL;
3262 for (iter = files; (iter != NULL) && (replaced_files < 2); iter = g_list_next(iter)) {
3263 SaveMimePartPair *pair = iter->data;
3264 gchar *unescaped = g_uri_unescape_string (pair->filename, NULL);
3266 if (modest_utils_file_exists (unescaped)) {
3268 if (replaced_files == 1)
3273 if (replaced_files) {
3276 if (replaced_files == 1) {
3277 SaveMimePartPair *pair = to_replace->data;
3278 const gchar *basename = strrchr (pair->filename, G_DIR_SEPARATOR) + 1;
3279 gchar *escaped_basename, *message;
3281 escaped_basename = g_uri_unescape_string (basename, NULL);
3282 message = g_strdup_printf ("%s\n%s",
3283 _FM("docm_nc_replace_file"),
3284 (escaped_basename) ? escaped_basename : "");
3285 response = modest_platform_run_confirmation_dialog (parent, message);
3287 g_free (escaped_basename);
3289 response = modest_platform_run_confirmation_dialog (parent,
3290 _FM("docm_nc_replace_multiple"));
3292 if (response != GTK_RESPONSE_OK)
3297 save_mime_part_info_free (info, TRUE);
3299 /* Start progress and launch thread */
3300 set_progress_hint (info->window, TRUE);
3301 g_thread_create ((GThreadFunc)save_mime_part_to_file, info, FALSE, NULL);
3306 typedef struct _SaveAttachmentsInfo {
3307 TnyList *attachments_list;
3308 ModestMsgViewWindow *window;
3309 } SaveAttachmentsInfo;
3312 save_attachments_response (GtkDialog *dialog,
3316 TnyList *mime_parts;
3318 GList *files_to_save = NULL;
3319 gchar *current_folder;
3320 SaveAttachmentsInfo *sa_info = (SaveAttachmentsInfo *) user_data;
3322 mime_parts = TNY_LIST (sa_info->attachments_list);
3324 if (arg1 != GTK_RESPONSE_OK)
3327 chooser_uri = gtk_file_chooser_get_uri (GTK_FILE_CHOOSER (dialog));
3328 current_folder = gtk_file_chooser_get_current_folder_uri (GTK_FILE_CHOOSER (dialog));
3329 if (current_folder && *current_folder != '\0') {
3331 modest_conf_set_string (modest_runtime_get_conf (), MODEST_CONF_LATEST_SAVE_ATTACHMENT_PATH,
3332 current_folder,&err);
3334 g_debug ("Error storing latest used folder: %s", err->message);
3338 g_free (current_folder);
3340 if (!modest_utils_folder_writable (chooser_uri)) {
3341 const gchar *err_msg;
3343 #ifdef MODEST_PLATFORM_MAEMO
3344 if (modest_maemo_utils_in_usb_mode ()) {
3345 err_msg = dgettext ("hildon-status-bar-usb", "usbh_ib_mmc_usb_connected");
3347 err_msg = _FM("sfil_ib_readonly_location");
3350 err_msg = _FM("sfil_ib_readonly_location");
3352 hildon_banner_show_information (NULL, NULL, err_msg);
3356 iter = tny_list_create_iterator (mime_parts);
3357 while (!tny_iterator_is_done (iter)) {
3358 TnyMimePart *mime_part = (TnyMimePart *) tny_iterator_get_current (iter);
3360 if ((modest_tny_mime_part_is_attachment_for_modest (mime_part)) &&
3361 !tny_mime_part_is_purged (mime_part) &&
3362 (tny_mime_part_get_filename (mime_part) != NULL)) {
3363 SaveMimePartPair *pair;
3365 pair = g_slice_new0 (SaveMimePartPair);
3367 if (tny_list_get_length (mime_parts) > 1) {
3369 gnome_vfs_escape_slashes (tny_mime_part_get_filename (mime_part));
3370 pair->filename = g_build_filename (chooser_uri, escaped, NULL);
3373 pair->filename = g_strdup (chooser_uri);
3375 pair->part = mime_part;
3376 files_to_save = g_list_prepend (files_to_save, pair);
3378 tny_iterator_next (iter);
3380 g_object_unref (iter);
3383 if (files_to_save != NULL) {
3384 SaveMimePartInfo *info = g_slice_new0 (SaveMimePartInfo);
3385 info->pairs = files_to_save;
3386 info->result = TRUE;
3387 info->uri = g_strdup (chooser_uri);
3388 info->window = g_object_ref (sa_info->window);
3389 save_mime_parts_to_file_with_checks ((GtkWindow *) dialog, info);
3391 g_free (chooser_uri);
3394 /* Free and close the dialog */
3395 g_object_unref (mime_parts);
3396 g_object_unref (sa_info->window);
3397 g_slice_free (SaveAttachmentsInfo, sa_info);
3398 gtk_widget_destroy (GTK_WIDGET (dialog));
3402 msg_is_attachment (TnyList *mime_parts)
3405 gboolean retval = FALSE;
3407 if (tny_list_get_length (mime_parts) > 1)
3410 iter = tny_list_create_iterator (mime_parts);
3412 TnyMimePart *part = TNY_MIME_PART (tny_iterator_get_current (iter));
3414 if (TNY_IS_MSG (part))
3416 g_object_unref (part);
3418 g_object_unref (iter);
3424 modest_msg_view_window_save_attachments (ModestMsgViewWindow *window,
3425 TnyList *mime_parts)
3427 ModestMsgViewWindowPrivate *priv;
3428 GtkWidget *save_dialog = NULL;
3429 gchar *conf_folder = NULL;
3430 gchar *filename = NULL;
3431 gchar *save_multiple_str = NULL;
3432 const gchar *root_folder = "file:///";
3434 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window));
3435 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
3437 if (mime_parts == NULL) {
3438 gboolean allow_msgs = FALSE;
3440 /* In Hildon 2.2 save and delete operate over all the attachments as there's no
3441 * selection available */
3442 mime_parts = modest_msg_view_get_attachments (MODEST_MSG_VIEW (priv->msg_view));
3444 /* Check if the message is composed by an unique MIME
3445 part whose content disposition is attachment. There
3446 could be messages like this:
3448 Date: Tue, 12 Jan 2010 20:40:59 +0000
3449 From: <sender@example.org>
3450 To: <recipient@example.org>
3452 Content-Type: image/jpeg
3453 Content-Disposition: attachment; filename="bug7718.jpeg"
3455 whose unique MIME part is the message itself whose
3456 content disposition is attachment
3458 if (mime_parts && msg_is_attachment (mime_parts))
3462 !modest_maemo_utils_select_attachments (GTK_WINDOW (window), mime_parts, allow_msgs)) {
3463 g_object_unref (mime_parts);
3466 if (mime_parts == NULL || tny_list_get_length (mime_parts) == 0) {
3468 g_object_unref (mime_parts);
3474 g_object_ref (mime_parts);
3477 /* prepare dialog */
3478 if (tny_list_get_length (mime_parts) == 1) {
3480 /* only one attachment selected */
3481 iter = tny_list_create_iterator (mime_parts);
3482 TnyMimePart *mime_part = (TnyMimePart *) tny_iterator_get_current (iter);
3483 g_object_unref (iter);
3484 if (!modest_tny_mime_part_is_msg (mime_part) &&
3485 modest_tny_mime_part_is_attachment_for_modest (mime_part) &&
3486 !tny_mime_part_is_purged (mime_part)) {
3487 filename = g_strdup (tny_mime_part_get_filename (mime_part));
3489 /* TODO: show any error? */
3490 g_warning ("%s: Tried to save a non-file attachment", __FUNCTION__);
3491 g_object_unref (mime_parts);
3494 g_object_unref (mime_part);
3496 gint num = tny_list_get_length (mime_parts);
3497 save_multiple_str = g_strdup_printf (dngettext("hildon-fm",
3498 "sfil_va_number_of_objects_attachment",
3499 "sfil_va_number_of_objects_attachments",
3503 save_dialog = hildon_file_chooser_dialog_new (GTK_WINDOW (window),
3504 GTK_FILE_CHOOSER_ACTION_SAVE);
3506 /* Get last used folder */
3507 conf_folder = modest_conf_get_string (modest_runtime_get_conf (),
3508 MODEST_CONF_LATEST_SAVE_ATTACHMENT_PATH, NULL);
3510 /* File chooser stops working if we select "file:///" as current folder */
3511 if (conf_folder && g_ascii_strcasecmp (root_folder, conf_folder) != 0) {
3512 g_free (conf_folder);
3516 if (conf_folder && conf_folder[0] != '\0') {
3517 gtk_file_chooser_set_current_folder_uri (GTK_FILE_CHOOSER (save_dialog), conf_folder);
3520 /* Set the default folder to documents folder */
3521 docs_folder = (gchar *) g_strdup(g_get_user_special_dir (G_USER_DIRECTORY_DOCUMENTS));
3524 docs_folder = g_build_filename (g_getenv (MYDOCS_ENV), DOCS_FOLDER, NULL);
3526 gtk_file_chooser_set_current_folder (GTK_FILE_CHOOSER (save_dialog), docs_folder);
3527 g_free (docs_folder);
3529 g_free (conf_folder);
3533 gtk_file_chooser_set_current_name (GTK_FILE_CHOOSER (save_dialog),
3538 /* if multiple, set multiple string */
3539 if (save_multiple_str) {
3540 g_object_set (G_OBJECT (save_dialog), "save-multiple", save_multiple_str, NULL);
3541 gtk_window_set_title (GTK_WINDOW (save_dialog), _FM("sfil_ti_save_objects_files"));
3542 g_free (save_multiple_str);
3545 /* We must run this asynchronously, because the hildon dialog
3546 performs a gtk_dialog_run by itself which leads to gdk
3548 SaveAttachmentsInfo *sa_info;
3549 sa_info = g_slice_new (SaveAttachmentsInfo);
3550 sa_info->attachments_list = mime_parts;
3551 sa_info->window = g_object_ref (window);
3552 g_signal_connect (save_dialog, "response",
3553 G_CALLBACK (save_attachments_response), sa_info);
3555 gtk_widget_show_all (save_dialog);
3559 show_remove_attachment_information (gpointer userdata)
3561 ModestMsgViewWindow *window = (ModestMsgViewWindow *) userdata;
3562 ModestMsgViewWindowPrivate *priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
3564 /* We're outside the main lock */
3565 gdk_threads_enter ();
3567 if (priv->remove_attachment_banner != NULL) {
3568 gtk_widget_destroy (priv->remove_attachment_banner);
3569 g_object_unref (priv->remove_attachment_banner);
3572 priv->remove_attachment_banner = g_object_ref (
3573 hildon_banner_show_animation (NULL, NULL, _("mcen_me_inbox_remove_attachments")));
3575 gdk_threads_leave ();
3581 modest_msg_view_window_remove_attachments (ModestMsgViewWindow *window, gboolean get_all)
3583 ModestMsgViewWindowPrivate *priv;
3584 TnyList *mime_parts = NULL, *tmp;
3585 gchar *confirmation_message;
3591 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window));
3592 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
3594 /* In hildon 2.2 we ignore the get_all flag as we always get all attachments. This is
3595 * because we don't have selection
3597 mime_parts = modest_msg_view_get_attachments (MODEST_MSG_VIEW (priv->msg_view));
3599 /* Remove already purged messages from mime parts list. We use
3600 a copy of the list to remove items in the original one */
3601 tmp = tny_list_copy (mime_parts);
3602 iter = tny_list_create_iterator (tmp);
3603 while (!tny_iterator_is_done (iter)) {
3604 TnyMimePart *part = TNY_MIME_PART (tny_iterator_get_current (iter));
3605 if (tny_mime_part_is_purged (part))
3606 tny_list_remove (mime_parts, (GObject *) part);
3608 g_object_unref (part);
3609 tny_iterator_next (iter);
3611 g_object_unref (tmp);
3612 g_object_unref (iter);
3614 if (!modest_maemo_utils_select_attachments (GTK_WINDOW (window), mime_parts, TRUE) ||
3615 tny_list_get_length (mime_parts) == 0) {
3616 g_object_unref (mime_parts);
3620 n_attachments = tny_list_get_length (mime_parts);
3621 if (n_attachments == 1) {
3625 iter = tny_list_create_iterator (mime_parts);
3626 part = (TnyMimePart *) tny_iterator_get_current (iter);
3627 g_object_unref (iter);
3628 if (modest_tny_mime_part_is_msg (part)) {
3630 header = tny_msg_get_header (TNY_MSG (part));
3631 filename = tny_header_dup_subject (header);
3632 g_object_unref (header);
3633 if (filename == NULL)
3634 filename = g_strdup (_("mail_va_no_subject"));
3636 filename = g_strdup (tny_mime_part_get_filename (TNY_MIME_PART (part)));
3638 confirmation_message = g_strdup_printf (_("mcen_nc_purge_file_text"), filename);
3640 g_object_unref (part);
3642 confirmation_message = g_strdup_printf (ngettext("mcen_nc_purge_file_text",
3643 "mcen_nc_purge_files_text",
3644 n_attachments), n_attachments);
3646 response = modest_platform_run_confirmation_dialog (GTK_WINDOW (window),
3647 confirmation_message);
3648 g_free (confirmation_message);
3650 if (response != GTK_RESPONSE_OK) {
3651 g_object_unref (mime_parts);
3655 priv->purge_timeout = g_timeout_add (2000, show_remove_attachment_information, window);
3657 iter = tny_list_create_iterator (mime_parts);
3658 while (!tny_iterator_is_done (iter)) {
3661 part = (TnyMimePart *) tny_iterator_get_current (iter);
3662 tny_mime_part_set_purged (TNY_MIME_PART (part));
3663 g_object_unref (part);
3664 tny_iterator_next (iter);
3666 g_object_unref (iter);
3668 msg = tny_msg_view_get_msg (TNY_MSG_VIEW (priv->msg_view));
3669 tny_msg_view_clear (TNY_MSG_VIEW (priv->msg_view));
3670 tny_msg_rewrite_cache (msg);
3671 tny_msg_view_set_msg (TNY_MSG_VIEW (priv->msg_view), msg);
3672 g_object_unref (msg);
3673 update_branding (MODEST_MSG_VIEW_WINDOW (window));
3675 g_object_unref (mime_parts);
3677 if (priv->purge_timeout > 0) {
3678 g_source_remove (priv->purge_timeout);
3679 priv->purge_timeout = 0;
3682 if (priv->remove_attachment_banner) {
3683 gtk_widget_destroy (priv->remove_attachment_banner);
3684 g_object_unref (priv->remove_attachment_banner);
3685 priv->remove_attachment_banner = NULL;
3691 update_window_title (ModestMsgViewWindow *window)
3693 ModestMsgViewWindowPrivate *priv;
3695 TnyHeader *header = NULL;
3696 gchar *subject = NULL;
3698 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
3700 /* Note that if the window is closed while we're retrieving
3701 the message, this widget could de deleted */
3702 if (!priv->msg_view)
3705 msg = tny_msg_view_get_msg (TNY_MSG_VIEW (priv->msg_view));
3707 if (priv->other_body) {
3710 description = modest_tny_mime_part_get_header_value (priv->other_body, "Content-Description");
3712 g_strstrip (description);
3713 subject = description;
3715 } else if (msg != NULL) {
3716 header = tny_msg_get_header (msg);
3717 subject = tny_header_dup_subject (header);
3718 g_object_unref (header);
3719 g_object_unref (msg);
3722 if ((subject == NULL)||(subject[0] == '\0')) {
3724 subject = g_strdup (_("mail_va_no_subject"));
3727 gtk_window_set_title (GTK_WINDOW (window), subject);
3732 on_move_focus (GtkWidget *widget,
3733 GtkDirectionType direction,
3736 g_signal_stop_emission_by_name (G_OBJECT (widget), "move-focus");
3740 fetch_image_open_stream (TnyStreamCache *self, gint64 *expected_size, gchar *uri)
3742 GnomeVFSResult result;
3743 GnomeVFSHandle *handle = NULL;
3744 GnomeVFSFileInfo *info = NULL;
3747 result = gnome_vfs_open (&handle, uri, GNOME_VFS_OPEN_READ);
3748 if (result != GNOME_VFS_OK) {
3753 info = gnome_vfs_file_info_new ();
3754 result = gnome_vfs_get_file_info_from_handle (handle, info, GNOME_VFS_FILE_INFO_DEFAULT);
3755 if (result != GNOME_VFS_OK || ! (info->valid_fields & GNOME_VFS_FILE_INFO_FIELDS_SIZE)) {
3756 /* We put a "safe" default size for going to cache */
3757 *expected_size = (300*1024);
3759 *expected_size = info->size;
3761 gnome_vfs_file_info_unref (info);
3763 stream = tny_vfs_stream_new (handle);
3772 TnyStream *output_stream;
3773 GtkWidget *msg_view;
3778 on_fetch_image_timeout_refresh_view (gpointer userdata)
3780 ModestMsgViewWindowPrivate *priv;
3782 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (userdata);
3783 update_progress_hint (MODEST_MSG_VIEW_WINDOW (userdata));
3784 /* Note that priv->msg_view is set to NULL when this window is
3786 if (priv->msg_view && GTK_WIDGET_DRAWABLE (priv->msg_view)) {
3787 gtk_widget_queue_draw (GTK_WIDGET (priv->msg_view));
3789 priv->fetch_image_redraw_handler = 0;
3790 g_object_unref (userdata);
3795 on_fetch_image_idle_refresh_view (gpointer userdata)
3798 FetchImageData *fidata = (FetchImageData *) userdata;
3800 gdk_threads_enter ();
3801 if (GTK_WIDGET_DRAWABLE (fidata->msg_view)) {
3802 ModestMsgViewWindowPrivate *priv;
3804 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (fidata->window);
3805 priv->fetching_images--;
3806 if (priv->fetch_image_redraw_handler == 0) {
3807 priv->fetch_image_redraw_handler = g_timeout_add (500, on_fetch_image_timeout_refresh_view, g_object_ref (fidata->window));
3811 gdk_threads_leave ();
3813 g_object_unref (fidata->msg_view);
3814 g_object_unref (fidata->window);
3815 g_slice_free (FetchImageData, fidata);
3820 on_fetch_image_thread (gpointer userdata)
3822 FetchImageData *fidata = (FetchImageData *) userdata;
3823 TnyStreamCache *cache;
3824 TnyStream *cache_stream;
3826 cache = modest_runtime_get_images_cache ();
3828 tny_stream_cache_get_stream (cache,
3830 (TnyStreamCacheOpenStreamFetcher) fetch_image_open_stream,
3831 (gpointer) fidata->uri);
3832 g_free (fidata->cache_id);
3833 g_free (fidata->uri);
3835 if (cache_stream != NULL) {
3838 while (G_LIKELY (!tny_stream_is_eos (cache_stream))) {
3841 nb_read = tny_stream_read (cache_stream, buffer, sizeof (buffer));
3842 if (G_UNLIKELY (nb_read < 0)) {
3844 } else if (G_LIKELY (nb_read > 0)) {
3845 gssize nb_written = 0;
3847 while (G_UNLIKELY (nb_written < nb_read)) {
3850 len = tny_stream_write (fidata->output_stream, buffer + nb_written,
3851 nb_read - nb_written);
3852 if (G_UNLIKELY (len < 0))
3858 tny_stream_close (cache_stream);
3859 g_object_unref (cache_stream);
3862 tny_stream_close (fidata->output_stream);
3863 g_object_unref (fidata->output_stream);
3865 g_idle_add (on_fetch_image_idle_refresh_view, fidata);
3871 on_fetch_image (ModestMsgView *msgview,
3874 ModestMsgViewWindow *window)
3876 const gchar *current_account;
3877 ModestMsgViewWindowPrivate *priv;
3878 FetchImageData *fidata;
3880 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
3882 current_account = modest_window_get_active_account (MODEST_WINDOW (window));
3884 fidata = g_slice_new0 (FetchImageData);
3885 fidata->msg_view = g_object_ref (msgview);
3886 fidata->window = g_object_ref (window);
3887 fidata->uri = g_strdup (uri);
3888 fidata->cache_id = modest_images_cache_get_id (current_account, uri);
3889 fidata->output_stream = g_object_ref (stream);
3891 priv->fetching_images++;
3892 if (g_thread_create (on_fetch_image_thread, fidata, FALSE, NULL) == NULL) {
3893 g_object_unref (fidata->output_stream);
3894 g_free (fidata->cache_id);
3895 g_free (fidata->uri);
3896 g_object_unref (fidata->msg_view);
3897 g_slice_free (FetchImageData, fidata);
3898 tny_stream_close (stream);
3899 priv->fetching_images--;
3900 update_progress_hint (window);
3903 update_progress_hint (window);
3909 setup_menu (ModestMsgViewWindow *self)
3911 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW(self));
3913 /* Settings menu buttons */
3914 modest_hildon2_window_add_to_menu (MODEST_HILDON2_WINDOW (self), _("mcen_me_new_message"), "<Control>n",
3915 APP_MENU_CALLBACK (modest_ui_actions_on_new_msg),
3916 MODEST_DIMMING_CALLBACK (modest_ui_dimming_rules_on_new_msg));
3918 modest_hildon2_window_add_to_menu (MODEST_HILDON2_WINDOW (self),
3919 dngettext(GETTEXT_PACKAGE,
3920 "mcen_me_move_message",
3921 "mcen_me_move_messages",
3924 APP_MENU_CALLBACK (modest_ui_actions_on_move_to),
3925 MODEST_DIMMING_CALLBACK (modest_ui_dimming_rules_on_move_to));
3927 modest_hildon2_window_add_to_menu (MODEST_HILDON2_WINDOW (self), _("mcen_me_inbox_mark_as_read"), NULL,
3928 APP_MENU_CALLBACK (modest_ui_actions_on_mark_as_read),
3929 MODEST_DIMMING_CALLBACK (modest_ui_dimming_rules_on_mark_as_read_msg_in_view));
3931 modest_hildon2_window_add_to_menu (MODEST_HILDON2_WINDOW (self), _("mcen_me_inbox_mark_as_unread"), NULL,
3932 APP_MENU_CALLBACK (modest_ui_actions_on_mark_as_unread),
3933 MODEST_DIMMING_CALLBACK (modest_ui_dimming_rules_on_mark_as_unread_msg_in_view));
3935 modest_hildon2_window_add_to_menu (MODEST_HILDON2_WINDOW (self), _("mcen_me_viewer_save_attachments"), NULL,
3936 APP_MENU_CALLBACK (modest_ui_actions_save_attachments),
3937 MODEST_DIMMING_CALLBACK (modest_ui_dimming_rules_on_save_attachments));
3938 modest_hildon2_window_add_to_menu (MODEST_HILDON2_WINDOW (self), _("mcen_me_inbox_remove_attachments"), NULL,
3939 APP_MENU_CALLBACK (modest_ui_actions_remove_attachments),
3940 MODEST_DIMMING_CALLBACK (modest_ui_dimming_rules_on_remove_attachments));
3942 modest_hildon2_window_add_to_menu (MODEST_HILDON2_WINDOW (self), _("mcen_me_viewer_addtocontacts"), NULL,
3943 APP_MENU_CALLBACK (modest_ui_actions_add_to_contacts),
3944 MODEST_DIMMING_CALLBACK (modest_ui_dimming_rules_on_add_to_contacts));
3946 modest_hildon2_window_add_to_menu (MODEST_HILDON2_WINDOW (self), _("mcen_ti_message_properties"), NULL,
3947 APP_MENU_CALLBACK (modest_ui_actions_on_details),
3948 MODEST_DIMMING_CALLBACK (modest_ui_dimming_rules_on_details));
3952 modest_msg_view_window_add_to_contacts (ModestMsgViewWindow *self)
3954 ModestMsgViewWindowPrivate *priv;
3955 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
3956 GSList *recipients = NULL;
3959 msg = tny_msg_view_get_msg (TNY_MSG_VIEW (priv->msg_view));
3963 header = modest_msg_view_window_get_header (self);
3966 recipients = modest_tny_msg_header_get_all_recipients_list (header);
3967 g_object_unref (header);
3969 recipients = modest_tny_msg_get_all_recipients_list (msg);
3970 g_object_unref (msg);
3974 /* Offer the user to add recipients to the address book */
3975 modest_address_book_add_address_list_with_selector (recipients, (GtkWindow *) self);
3976 g_slist_foreach (recipients, (GFunc) g_free, NULL); g_slist_free (recipients);
3981 _modest_msg_view_window_map_event (GtkWidget *widget,
3985 ModestMsgViewWindow *self = (ModestMsgViewWindow *) userdata;
3987 update_progress_hint (self);
3993 modest_msg_view_window_fetch_images (ModestMsgViewWindow *self)
3995 ModestMsgViewWindowPrivate *priv;
3996 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
3998 modest_msg_view_request_fetch_images (MODEST_MSG_VIEW (priv->msg_view));
4002 modest_msg_view_window_has_blocked_external_images (ModestMsgViewWindow *self)
4004 ModestMsgViewWindowPrivate *priv;
4005 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
4007 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self), FALSE);
4009 return modest_msg_view_has_blocked_external_images (MODEST_MSG_VIEW (priv->msg_view));
4013 modest_msg_view_window_reload (ModestMsgViewWindow *self)
4015 ModestMsgViewWindowPrivate *priv;
4016 const gchar *msg_uid;
4017 TnyHeader *header = NULL;
4018 TnyFolder *folder = NULL;
4020 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self));
4022 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
4024 header = modest_msg_view_window_get_header (MODEST_MSG_VIEW_WINDOW (self));
4028 folder = tny_header_get_folder (header);
4029 g_object_unref (header);
4034 msg_uid = modest_msg_view_window_get_message_uid (self);
4036 GtkTreeRowReference *row_reference;
4038 if (priv->row_reference && gtk_tree_row_reference_valid (priv->row_reference)) {
4039 row_reference = priv->row_reference;
4041 row_reference = NULL;
4043 if (!message_reader (self, priv, NULL, msg_uid, folder, row_reference))
4044 g_warning ("Shouldn't happen, trying to reload a message failed");
4047 g_object_unref (folder);
4051 update_branding (ModestMsgViewWindow *self)
4053 const gchar *account;
4054 const gchar *mailbox;
4055 ModestAccountMgr *mgr;
4056 ModestProtocol *protocol = NULL;
4057 gchar *service_name = NULL;
4058 const GdkPixbuf *service_icon = NULL;
4059 ModestMsgViewWindowPrivate *priv;
4061 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
4063 account = modest_window_get_active_account (MODEST_WINDOW (self));
4064 mailbox = modest_window_get_active_mailbox (MODEST_WINDOW (self));
4066 mgr = modest_runtime_get_account_mgr ();
4068 if (modest_account_mgr_account_is_multimailbox (mgr, account, &protocol)) {
4069 if (MODEST_IS_ACCOUNT_PROTOCOL (protocol)) {
4070 service_name = modest_account_protocol_get_service_name (MODEST_ACCOUNT_PROTOCOL (protocol),
4072 service_icon = modest_account_protocol_get_service_icon (MODEST_ACCOUNT_PROTOCOL (protocol),
4073 account, mailbox, MODEST_ICON_SIZE_SMALL);
4077 modest_msg_view_set_branding (MODEST_MSG_VIEW (priv->msg_view), service_name, service_icon);
4078 g_free (service_name);
4082 sync_flags (ModestMsgViewWindow *self)
4084 TnyHeader *header = NULL;
4086 header = modest_msg_view_window_get_header (self);
4088 TnyMsg *msg = modest_msg_view_window_get_message (self);
4090 header = tny_msg_get_header (msg);
4091 g_object_unref (msg);
4096 TnyFolder *folder = tny_header_get_folder (header);
4099 ModestMailOperation *mail_op;
4101 /* Sync folder, we need this to save the seen flag */
4102 mail_op = modest_mail_operation_new (NULL);
4103 modest_mail_operation_queue_add (modest_runtime_get_mail_operation_queue (),
4105 modest_mail_operation_sync_folder (mail_op, folder, FALSE, NULL, NULL);
4106 g_object_unref (mail_op);
4107 g_object_unref (folder);
4109 g_object_unref (header);
4114 on_realize (GtkWidget *widget,
4117 GdkDisplay *display;
4119 unsigned long val = 1;
4121 display = gdk_drawable_get_display (widget->window);
4122 atom = gdk_x11_get_xatom_by_name_for_display (display, "_HILDON_ZOOM_KEY_ATOM");
4123 XChangeProperty (GDK_DISPLAY_XDISPLAY (display),
4124 GDK_WINDOW_XID (widget->window), atom,
4125 XA_INTEGER, 32, PropModeReplace,
4126 (unsigned char *) &val, 1);
4132 on_handle_calendar (ModestMsgView *msgview, TnyMimePart *calendar_part, GtkContainer *container, ModestMsgViewWindow *self)
4134 const gchar *account_name;
4135 ModestProtocolType proto_type;
4136 ModestProtocol *protocol;
4137 gboolean retval = FALSE;
4139 account_name = modest_window_get_active_account (MODEST_WINDOW (self));
4142 proto_type = modest_account_mgr_get_store_protocol (modest_runtime_get_account_mgr (),
4145 modest_protocol_registry_get_protocol_by_type (modest_runtime_get_protocol_registry (),
4148 if (MODEST_IS_ACCOUNT_PROTOCOL (protocol)) {
4149 retval = modest_account_protocol_handle_calendar (MODEST_ACCOUNT_PROTOCOL (protocol), MODEST_WINDOW (self),
4150 calendar_part, container);