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-toolkit-utils.h>
41 #include <modest-tny-msg.h>
42 #include <modest-msg-view-window.h>
43 #include "modest-msg-view-window-ui-dimming.h"
44 #include <modest-widget-memory.h>
45 #include <modest-progress-object.h>
46 #include <modest-runtime.h>
47 #include <modest-window-priv.h>
48 #include <modest-tny-folder.h>
49 #include <modest-text-utils.h>
50 #include <modest-account-mgr-helpers.h>
51 #include <modest-toolkit-factory.h>
52 #include <modest-scrollable.h>
53 #include <modest-isearch-toolbar.h>
54 #include "modest-defs.h"
55 #include "modest-ui-dimming-manager.h"
56 #include <gdk/gdkkeysyms.h>
57 #include <modest-tny-account.h>
58 #include <modest-mime-part-view.h>
59 #include <modest-isearch-view.h>
60 #include <modest-tny-mime-part.h>
61 #include <modest-address-book.h>
64 #include <glib/gstdio.h>
65 #include <modest-debug.h>
66 #include <modest-header-window.h>
67 #include <modest-account-protocol.h>
68 #include <modest-icon-names.h>
69 #include <modest-ui-actions.h>
70 #include <modest-window-mgr.h>
71 #include <tny-camel-msg.h>
72 #include <modest-icon-names.h>
74 #ifdef MODEST_PLATFORM_MAEMO
75 #include <modest-maemo-utils.h>
78 #ifdef MODEST_TOOLKIT_HILDON2
79 #include <hildon/hildon.h>
81 #include <X11/Xatom.h>
82 #include <X11/XKBlib.h>
83 #include <X11/Xdmcp.h>
86 #include <tny-camel-bs-mime-part.h>
87 #include <tny-camel-bs-msg.h>
89 #define MYDOCS_ENV "MYDOCSDIR"
90 #define DOCS_FOLDER ".documents"
92 typedef struct _ModestMsgViewWindowPrivate ModestMsgViewWindowPrivate;
93 struct _ModestMsgViewWindowPrivate {
96 GtkWidget *main_scroll;
97 GtkWidget *isearch_toolbar;
100 /* Progress observers */
101 GSList *progress_widgets;
104 GtkWidget *prev_toolitem;
105 GtkWidget *next_toolitem;
106 gboolean progress_hint;
107 gint fetching_images;
109 /* Optimized view enabled */
110 gboolean optimized_view;
112 /* Whether this was created via the *_new_for_search_result() function. */
113 gboolean is_search_result;
115 /* Whether the message is in outbox */
118 /* A reference to the @model of the header view
119 * to allow selecting previous/next messages,
120 * if the message is currently selected in the header view.
122 const gchar *header_folder_id;
123 GtkTreeModel *header_model;
124 GtkTreeRowReference *row_reference;
125 GtkTreeRowReference *next_row_reference;
127 gulong clipboard_change_handler;
128 gulong queue_change_handler;
129 gulong account_removed_handler;
130 gulong row_changed_handler;
131 gulong row_deleted_handler;
132 gulong row_inserted_handler;
133 gulong rows_reordered_handler;
134 gulong fetch_image_redraw_handler;
137 GtkWidget *remove_attachment_banner;
140 TnyMimePart *other_body;
146 static void modest_msg_view_window_class_init (ModestMsgViewWindowClass *klass);
147 static void modest_msg_view_window_init (ModestMsgViewWindow *obj);
148 static void modest_header_view_observer_init (ModestHeaderViewObserverIface *iface_class);
149 static void modest_msg_view_window_finalize (GObject *obj);
150 static void modest_msg_view_window_show_isearch_toolbar (GtkWidget *obj, gpointer data);
151 static void modest_msg_view_window_isearch_toolbar_close (GtkWidget *widget,
152 ModestMsgViewWindow *obj);
153 static void modest_msg_view_window_isearch_toolbar_search (GtkWidget *widget,
154 ModestMsgViewWindow *obj);
155 static void modest_msg_view_window_toggle_isearch_toolbar (GtkWidget *obj,
157 static void modest_msg_view_window_disconnect_signals (ModestWindow *self);
159 static gdouble modest_msg_view_window_get_zoom (ModestWindow *window);
160 static void modest_msg_view_window_set_zoom (ModestWindow *window,
162 static gboolean modest_msg_view_window_zoom_minus (ModestWindow *window);
163 static gboolean modest_msg_view_window_zoom_plus (ModestWindow *window);
164 static gboolean modest_msg_view_window_key_event (GtkWidget *window,
167 static void modest_msg_view_window_update_priority (ModestMsgViewWindow *window);
169 static void modest_msg_view_window_show_toolbar (ModestWindow *window,
170 gboolean show_toolbar);
172 static void modest_msg_view_window_clipboard_owner_change (GtkClipboard *clipboard,
174 ModestMsgViewWindow *window);
176 static void modest_msg_view_window_on_row_changed (GtkTreeModel *header_model,
179 ModestMsgViewWindow *window);
181 static void modest_msg_view_window_on_row_deleted (GtkTreeModel *header_model,
183 ModestMsgViewWindow *window);
185 static void modest_msg_view_window_on_row_inserted (GtkTreeModel *header_model,
186 GtkTreePath *tree_path,
187 GtkTreeIter *tree_iter,
188 ModestMsgViewWindow *window);
190 static void modest_msg_view_window_on_row_reordered (GtkTreeModel *header_model,
194 ModestMsgViewWindow *window);
196 static void modest_msg_view_window_update_model_replaced (ModestHeaderViewObserver *window,
198 const gchar *tny_folder_id);
200 static void on_queue_changed (ModestMailOperationQueue *queue,
201 ModestMailOperation *mail_op,
202 ModestMailOperationQueueNotification type,
203 ModestMsgViewWindow *self);
205 static void on_account_removed (TnyAccountStore *account_store,
209 static void on_move_focus (GtkWidget *widget,
210 GtkDirectionType direction,
213 static void view_msg_cb (ModestMailOperation *mail_op,
220 static void set_progress_hint (ModestMsgViewWindow *self,
223 static void update_window_title (ModestMsgViewWindow *window);
225 static void init_window (ModestMsgViewWindow *obj);
227 static gboolean msg_is_visible (TnyHeader *header, gboolean check_outbox);
229 static void check_dimming_rules_after_change (ModestMsgViewWindow *window);
231 static gboolean on_fetch_image (ModestMsgView *msgview,
234 ModestMsgViewWindow *window);
236 static gboolean modest_msg_view_window_scroll_child (ModestMsgViewWindow *self,
237 GtkScrollType scroll_type,
240 static gboolean message_reader (ModestMsgViewWindow *window,
241 ModestMsgViewWindowPrivate *priv,
243 const gchar *msg_uid,
245 GtkTreeRowReference *row_reference);
247 static void setup_menu (ModestMsgViewWindow *self);
248 static gboolean _modest_msg_view_window_map_event (GtkWidget *widget,
251 static void update_branding (ModestMsgViewWindow *self);
252 static void sync_flags (ModestMsgViewWindow *self);
253 static gboolean on_handle_calendar (ModestMsgView *msgview, TnyMimePart *calendar_part,
254 GtkContainer *container, ModestMsgViewWindow *self);
256 static gboolean on_realize (GtkWidget *widget,
259 /* list my signals */
266 static const GtkActionEntry msg_view_toolbar_action_entries [] = {
269 { "ToolbarMessageReply", MODEST_STOCK_REPLY, N_("mcen_me_inbox_reply"), "<CTRL>R", NULL, G_CALLBACK (modest_ui_actions_on_reply) },
270 { "ToolbarMessageReplyAll", MODEST_STOCK_REPLY_ALL, N_("mcen_me_inbox_replytoall"), NULL, NULL, G_CALLBACK (modest_ui_actions_on_reply_all) },
271 { "ToolbarMessageForward", MODEST_STOCK_FORWARD, N_("mcen_me_inbox_forward"), NULL, NULL, G_CALLBACK (modest_ui_actions_on_forward) },
272 { "ToolbarDeleteMessage", MODEST_STOCK_DELETE, N_("qgn_toolb_gene_deletebutton"), NULL, NULL, G_CALLBACK (modest_ui_actions_on_delete_message_or_folder) },
273 { "ToolbarMessageBack", MODEST_TOOLBAR_ICON_PREV, N_("qgn_toolb_gene_back"), NULL, NULL, G_CALLBACK (modest_ui_actions_on_prev) },
274 { "ToolbarMessageNext", MODEST_TOOLBAR_ICON_NEXT, N_("qgn_toolb_gene_forward"), NULL, NULL, G_CALLBACK (modest_ui_actions_on_next) },
275 { "ToolbarDownloadExternalImages", MODEST_TOOLBAR_ICON_DOWNLOAD_IMAGES, N_("mail_bd_external_images"), NULL, NULL, G_CALLBACK (modest_ui_actions_on_fetch_images) },
278 static const GtkToggleActionEntry msg_view_toggle_action_entries [] = {
279 { "FindInMessage", MODEST_TOOLBAR_ICON_FIND, N_("qgn_toolb_gene_find"), "<CTRL>F", NULL, G_CALLBACK (modest_msg_view_window_toggle_isearch_toolbar), FALSE },
282 #define MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE((o), \
283 MODEST_TYPE_MSG_VIEW_WINDOW, \
284 ModestMsgViewWindowPrivate))
286 static GtkWindowClass *parent_class = NULL;
288 /* uncomment the following if you have defined any signals */
289 static guint signals[LAST_SIGNAL] = {0};
292 modest_msg_view_window_get_type (void)
294 static GType my_type = 0;
296 static const GTypeInfo my_info = {
297 sizeof(ModestMsgViewWindowClass),
298 NULL, /* base init */
299 NULL, /* base finalize */
300 (GClassInitFunc) modest_msg_view_window_class_init,
301 NULL, /* class finalize */
302 NULL, /* class data */
303 sizeof(ModestMsgViewWindow),
305 (GInstanceInitFunc) modest_msg_view_window_init,
308 #ifndef MODEST_TOOLKIT_HILDON2
309 my_type = g_type_register_static (MODEST_TYPE_SHELL_WINDOW,
310 "ModestMsgViewWindow",
313 my_type = g_type_register_static (MODEST_TYPE_HILDON2_WINDOW,
314 "ModestMsgViewWindow",
318 static const GInterfaceInfo modest_header_view_observer_info =
320 (GInterfaceInitFunc) modest_header_view_observer_init,
321 NULL, /* interface_finalize */
322 NULL /* interface_data */
325 g_type_add_interface_static (my_type,
326 MODEST_TYPE_HEADER_VIEW_OBSERVER,
327 &modest_header_view_observer_info);
333 save_state (ModestWindow *self)
335 modest_widget_memory_save (modest_runtime_get_conf (),
337 MODEST_CONF_MSG_VIEW_WINDOW_KEY);
341 modest_msg_view_window_scroll_child (ModestMsgViewWindow *self,
342 GtkScrollType scroll_type,
346 ModestMsgViewWindowPrivate *priv;
349 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(self);
351 switch (scroll_type) {
352 case GTK_SCROLL_STEP_UP:
355 case GTK_SCROLL_STEP_DOWN:
358 case GTK_SCROLL_PAGE_UP:
361 case GTK_SCROLL_PAGE_DOWN:
364 case GTK_SCROLL_START:
375 modest_scrollable_scroll ((ModestScrollable *) priv->main_scroll, 0, step);
377 return (gboolean) step;
381 add_scroll_binding (GtkBindingSet *binding_set,
383 GtkScrollType scroll)
385 guint keypad_keyval = keyval - GDK_Left + GDK_KP_Left;
387 gtk_binding_entry_add_signal (binding_set, keyval, 0,
389 GTK_TYPE_SCROLL_TYPE, scroll,
390 G_TYPE_BOOLEAN, FALSE);
391 gtk_binding_entry_add_signal (binding_set, keypad_keyval, 0,
393 GTK_TYPE_SCROLL_TYPE, scroll,
394 G_TYPE_BOOLEAN, FALSE);
398 modest_msg_view_window_class_init (ModestMsgViewWindowClass *klass)
400 GObjectClass *gobject_class;
401 ModestWindowClass *modest_window_class;
402 GtkBindingSet *binding_set;
404 gobject_class = (GObjectClass*) klass;
405 modest_window_class = (ModestWindowClass *) klass;
407 parent_class = g_type_class_peek_parent (klass);
408 gobject_class->finalize = modest_msg_view_window_finalize;
410 modest_window_class->set_zoom_func = modest_msg_view_window_set_zoom;
411 modest_window_class->get_zoom_func = modest_msg_view_window_get_zoom;
412 modest_window_class->zoom_plus_func = modest_msg_view_window_zoom_plus;
413 modest_window_class->zoom_minus_func = modest_msg_view_window_zoom_minus;
414 modest_window_class->show_toolbar_func = modest_msg_view_window_show_toolbar;
415 modest_window_class->disconnect_signals_func = modest_msg_view_window_disconnect_signals;
417 modest_window_class->save_state_func = save_state;
419 klass->scroll_child = modest_msg_view_window_scroll_child;
421 signals[MSG_CHANGED_SIGNAL] =
422 g_signal_new ("msg-changed",
423 G_TYPE_FROM_CLASS (gobject_class),
425 G_STRUCT_OFFSET (ModestMsgViewWindowClass, msg_changed),
427 modest_marshal_VOID__POINTER_POINTER,
428 G_TYPE_NONE, 2, G_TYPE_POINTER, G_TYPE_POINTER);
430 signals[SCROLL_CHILD_SIGNAL] =
431 g_signal_new ("scroll-child",
432 G_TYPE_FROM_CLASS (gobject_class),
433 G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
434 G_STRUCT_OFFSET (ModestMsgViewWindowClass, scroll_child),
436 modest_marshal_BOOLEAN__ENUM_BOOLEAN,
437 G_TYPE_BOOLEAN, 2, GTK_TYPE_SCROLL_TYPE, G_TYPE_BOOLEAN);
439 binding_set = gtk_binding_set_by_class (klass);
440 add_scroll_binding (binding_set, GDK_Up, GTK_SCROLL_STEP_UP);
441 add_scroll_binding (binding_set, GDK_Down, GTK_SCROLL_STEP_DOWN);
442 add_scroll_binding (binding_set, GDK_Page_Up, GTK_SCROLL_PAGE_UP);
443 add_scroll_binding (binding_set, GDK_Page_Down, GTK_SCROLL_PAGE_DOWN);
444 add_scroll_binding (binding_set, GDK_Home, GTK_SCROLL_START);
445 add_scroll_binding (binding_set, GDK_End, GTK_SCROLL_END);
447 g_type_class_add_private (gobject_class, sizeof(ModestMsgViewWindowPrivate));
451 static void modest_header_view_observer_init(
452 ModestHeaderViewObserverIface *iface_class)
454 iface_class->update_func = modest_msg_view_window_update_model_replaced;
458 modest_msg_view_window_init (ModestMsgViewWindow *obj)
460 ModestMsgViewWindowPrivate *priv;
461 ModestWindowPrivate *parent_priv = NULL;
462 GtkActionGroup *action_group = NULL;
463 GError *error = NULL;
465 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(obj);
466 parent_priv = MODEST_WINDOW_GET_PRIVATE(obj);
467 parent_priv->ui_manager = gtk_ui_manager_new();
469 action_group = gtk_action_group_new ("ModestMsgViewWindowActions");
470 gtk_action_group_set_translation_domain (action_group, GETTEXT_PACKAGE);
472 /* Add common actions */
473 gtk_action_group_add_actions (action_group,
474 msg_view_toolbar_action_entries,
475 G_N_ELEMENTS (msg_view_toolbar_action_entries),
477 gtk_action_group_add_toggle_actions (action_group,
478 msg_view_toggle_action_entries,
479 G_N_ELEMENTS (msg_view_toggle_action_entries),
482 gtk_ui_manager_insert_action_group (parent_priv->ui_manager, action_group, 0);
483 g_object_unref (action_group);
485 /* Load the UI definition */
486 gtk_ui_manager_add_ui_from_file (parent_priv->ui_manager, MODEST_UIDIR "modest-msg-view-window-ui.xml",
489 g_printerr ("modest: could not merge modest-msg-view-window-ui.xml: %s\n", error->message);
490 g_error_free (error);
494 priv->is_search_result = FALSE;
495 priv->is_outbox = FALSE;
497 priv->msg_view = NULL;
498 priv->header_model = NULL;
499 priv->header_folder_id = NULL;
500 priv->clipboard_change_handler = 0;
501 priv->queue_change_handler = 0;
502 priv->account_removed_handler = 0;
503 priv->row_changed_handler = 0;
504 priv->row_deleted_handler = 0;
505 priv->row_inserted_handler = 0;
506 priv->rows_reordered_handler = 0;
507 priv->fetch_image_redraw_handler = 0;
508 priv->progress_hint = FALSE;
509 priv->fetching_images = 0;
511 priv->optimized_view = FALSE;
512 priv->purge_timeout = 0;
513 priv->remove_attachment_banner = NULL;
514 priv->msg_uid = NULL;
515 priv->other_body = NULL;
517 priv->sighandlers = NULL;
520 init_window (MODEST_MSG_VIEW_WINDOW(obj));
522 #ifdef MODEST_TOOLKIT_HILDON2
523 /* Grab the zoom keys, it will be used for Zoom and not for
525 g_signal_connect (G_OBJECT (obj), "realize",
526 G_CALLBACK (on_realize),
532 update_progress_hint (ModestMsgViewWindow *self)
534 ModestMsgViewWindowPrivate *priv;
535 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(self);
537 if (GTK_WIDGET_VISIBLE (self)) {
538 modest_window_show_progress (MODEST_WINDOW (self),
539 (priv->progress_hint || (priv->fetching_images > 0))?1:0);
544 set_progress_hint (ModestMsgViewWindow *self,
547 ModestWindowPrivate *parent_priv;
548 ModestMsgViewWindowPrivate *priv;
550 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self));
552 parent_priv = MODEST_WINDOW_GET_PRIVATE(self);
553 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(self);
555 /* Sets current progress hint */
556 priv->progress_hint = enabled;
558 update_progress_hint (self);
564 init_window (ModestMsgViewWindow *obj)
566 GtkWidget *main_vbox;
567 ModestMsgViewWindowPrivate *priv;
569 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(obj);
571 priv->msg_view = GTK_WIDGET (tny_platform_factory_new_msg_view (modest_tny_platform_factory_get_instance ()));
572 modest_msg_view_set_shadow_type (MODEST_MSG_VIEW (priv->msg_view), GTK_SHADOW_NONE);
573 main_vbox = gtk_vbox_new (FALSE, 6);
575 priv->main_scroll = modest_toolkit_factory_create_scrollable (modest_runtime_get_toolkit_factory ());
576 modest_scrollable_set_horizontal_policy (MODEST_SCROLLABLE (priv->main_scroll), GTK_POLICY_AUTOMATIC);
577 g_object_set (G_OBJECT (priv->main_scroll),
578 "movement-mode", MODEST_MOVEMENT_MODE_BOTH,
579 "horizontal-max-overshoot", 0,
581 gtk_container_add (GTK_CONTAINER (priv->main_scroll), priv->msg_view);
582 gtk_box_pack_start (GTK_BOX(main_vbox), priv->main_scroll, TRUE, TRUE, 0);
583 gtk_container_add (GTK_CONTAINER(obj), main_vbox);
585 /* NULL-ize fields if the window is destroyed */
586 g_signal_connect (priv->msg_view, "destroy", G_CALLBACK (gtk_widget_destroyed), &(priv->msg_view));
588 gtk_widget_show_all (GTK_WIDGET(main_vbox));
592 modest_msg_view_window_disconnect_signals (ModestWindow *self)
594 ModestMsgViewWindowPrivate *priv;
595 GtkWidget *header_view = NULL;
596 GtkWindow *parent_window = NULL;
598 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
600 if (gtk_clipboard_get (GDK_SELECTION_PRIMARY) &&
601 g_signal_handler_is_connected (gtk_clipboard_get (GDK_SELECTION_PRIMARY),
602 priv->clipboard_change_handler))
603 g_signal_handler_disconnect (gtk_clipboard_get (GDK_SELECTION_PRIMARY),
604 priv->clipboard_change_handler);
606 if (g_signal_handler_is_connected (G_OBJECT (modest_runtime_get_mail_operation_queue ()),
607 priv->queue_change_handler))
608 g_signal_handler_disconnect (G_OBJECT (modest_runtime_get_mail_operation_queue ()),
609 priv->queue_change_handler);
611 if (g_signal_handler_is_connected (G_OBJECT (modest_runtime_get_account_store ()),
612 priv->account_removed_handler))
613 g_signal_handler_disconnect (G_OBJECT (modest_runtime_get_account_store ()),
614 priv->account_removed_handler);
616 if (priv->header_model) {
617 if (g_signal_handler_is_connected(G_OBJECT (priv->header_model),
618 priv->row_changed_handler))
619 g_signal_handler_disconnect(G_OBJECT (priv->header_model),
620 priv->row_changed_handler);
622 if (g_signal_handler_is_connected(G_OBJECT (priv->header_model),
623 priv->row_deleted_handler))
624 g_signal_handler_disconnect(G_OBJECT (priv->header_model),
625 priv->row_deleted_handler);
627 if (g_signal_handler_is_connected(G_OBJECT (priv->header_model),
628 priv->row_inserted_handler))
629 g_signal_handler_disconnect(G_OBJECT (priv->header_model),
630 priv->row_inserted_handler);
632 if (g_signal_handler_is_connected(G_OBJECT (priv->header_model),
633 priv->rows_reordered_handler))
634 g_signal_handler_disconnect(G_OBJECT (priv->header_model),
635 priv->rows_reordered_handler);
638 modest_signal_mgr_disconnect_all_and_destroy (priv->sighandlers);
639 priv->sighandlers = NULL;
641 parent_window = gtk_window_get_transient_for (GTK_WINDOW (self));
642 if (parent_window && MODEST_IS_HEADER_WINDOW (parent_window)) {
643 header_view = GTK_WIDGET (modest_header_window_get_header_view (MODEST_HEADER_WINDOW (parent_window)));
645 modest_header_view_remove_observer(MODEST_HEADER_VIEW (header_view),
646 MODEST_HEADER_VIEW_OBSERVER(self));
652 modest_msg_view_window_finalize (GObject *obj)
654 ModestMsgViewWindowPrivate *priv;
656 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (obj);
658 /* Sanity check: shouldn't be needed, the window mgr should
659 call this function before */
660 modest_msg_view_window_disconnect_signals (MODEST_WINDOW (obj));
662 if (priv->fetch_image_redraw_handler > 0) {
663 g_source_remove (priv->fetch_image_redraw_handler);
664 priv->fetch_image_redraw_handler = 0;
667 if (priv->other_body != NULL) {
668 g_object_unref (priv->other_body);
669 priv->other_body = NULL;
672 if (priv->top_msg != NULL) {
673 g_object_unref (priv->top_msg);
674 priv->top_msg = NULL;
677 if (priv->header_model != NULL) {
678 g_object_unref (priv->header_model);
679 priv->header_model = NULL;
682 if (priv->remove_attachment_banner) {
683 gtk_widget_destroy (priv->remove_attachment_banner);
684 g_object_unref (priv->remove_attachment_banner);
685 priv->remove_attachment_banner = NULL;
688 if (priv->purge_timeout > 0) {
689 g_source_remove (priv->purge_timeout);
690 priv->purge_timeout = 0;
693 if (priv->row_reference) {
694 gtk_tree_row_reference_free (priv->row_reference);
695 priv->row_reference = NULL;
698 if (priv->next_row_reference) {
699 gtk_tree_row_reference_free (priv->next_row_reference);
700 priv->next_row_reference = NULL;
704 g_free (priv->msg_uid);
705 priv->msg_uid = NULL;
708 G_OBJECT_CLASS(parent_class)->finalize (obj);
712 select_next_valid_row (GtkTreeModel *model,
713 GtkTreeRowReference **row_reference,
717 GtkTreeIter tmp_iter;
719 GtkTreePath *next = NULL;
720 gboolean retval = FALSE, finished;
722 g_return_val_if_fail (gtk_tree_row_reference_valid (*row_reference), FALSE);
724 path = gtk_tree_row_reference_get_path (*row_reference);
725 gtk_tree_model_get_iter (model, &tmp_iter, path);
726 gtk_tree_row_reference_free (*row_reference);
727 *row_reference = NULL;
731 TnyHeader *header = NULL;
733 if (gtk_tree_model_iter_next (model, &tmp_iter)) {
734 gtk_tree_model_get (model, &tmp_iter,
735 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
739 if (msg_is_visible (header, is_outbox)) {
740 next = gtk_tree_model_get_path (model, &tmp_iter);
741 *row_reference = gtk_tree_row_reference_new (model, next);
742 gtk_tree_path_free (next);
746 g_object_unref (header);
749 } else if (cycle && gtk_tree_model_get_iter_first (model, &tmp_iter)) {
750 next = gtk_tree_model_get_path (model, &tmp_iter);
752 /* Ensure that we are not selecting the same */
753 if (gtk_tree_path_compare (path, next) != 0) {
754 gtk_tree_model_get (model, &tmp_iter,
755 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
758 if (msg_is_visible (header, is_outbox)) {
759 *row_reference = gtk_tree_row_reference_new (model, next);
763 g_object_unref (header);
767 /* If we ended up in the same message
768 then there is no valid next
772 gtk_tree_path_free (next);
774 /* If there are no more messages and we don't
775 want to start again in the first one then
776 there is no valid next message */
782 gtk_tree_path_free (path);
787 /* TODO: This should be in _init(), with the parameters as properties. */
789 modest_msg_view_window_construct (ModestMsgViewWindow *self,
790 const gchar *modest_account_name,
791 const gchar *mailbox,
792 const gchar *msg_uid)
795 ModestMsgViewWindowPrivate *priv = NULL;
796 ModestWindowPrivate *parent_priv = NULL;
797 ModestDimmingRulesGroup *toolbar_rules_group = NULL;
798 ModestDimmingRulesGroup *clipboard_rules_group = NULL;
800 obj = G_OBJECT (self);
801 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(obj);
802 parent_priv = MODEST_WINDOW_GET_PRIVATE(obj);
804 priv->msg_uid = g_strdup (msg_uid);
807 parent_priv->menubar = NULL;
809 toolbar_rules_group = modest_dimming_rules_group_new (MODEST_DIMMING_RULES_TOOLBAR, TRUE);
810 clipboard_rules_group = modest_dimming_rules_group_new (MODEST_DIMMING_RULES_CLIPBOARD, FALSE);
813 /* Add common dimming rules */
814 modest_dimming_rules_group_add_rules (toolbar_rules_group,
815 modest_msg_view_toolbar_dimming_entries,
816 G_N_ELEMENTS (modest_msg_view_toolbar_dimming_entries),
817 MODEST_WINDOW (self));
818 modest_dimming_rules_group_add_rules (clipboard_rules_group,
819 modest_msg_view_clipboard_dimming_entries,
820 G_N_ELEMENTS (modest_msg_view_clipboard_dimming_entries),
821 MODEST_WINDOW (self));
823 /* Insert dimming rules group for this window */
824 modest_ui_dimming_manager_insert_rules_group (parent_priv->ui_dimming_manager, toolbar_rules_group);
825 modest_ui_dimming_manager_insert_rules_group (parent_priv->ui_dimming_manager, clipboard_rules_group);
826 g_object_unref (toolbar_rules_group);
827 g_object_unref (clipboard_rules_group);
829 /* g_signal_connect (G_OBJECT(obj), "delete-event", G_CALLBACK(on_delete_event), obj); */
831 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);
832 g_signal_connect (G_OBJECT(priv->msg_view), "activate_link",
833 G_CALLBACK (modest_ui_actions_on_msg_link_clicked), obj);
834 g_signal_connect (G_OBJECT(priv->msg_view), "link_hover",
835 G_CALLBACK (modest_ui_actions_on_msg_link_hover), obj);
836 g_signal_connect (G_OBJECT(priv->msg_view), "attachment_clicked",
837 G_CALLBACK (modest_ui_actions_on_msg_attachment_clicked), obj);
838 g_signal_connect (G_OBJECT(priv->msg_view), "recpt_activated",
839 G_CALLBACK (modest_ui_actions_on_msg_recpt_activated), obj);
840 g_signal_connect (G_OBJECT(priv->msg_view), "show_details",
841 G_CALLBACK (modest_ui_actions_on_details), obj);
842 g_signal_connect (G_OBJECT(priv->msg_view), "link_contextual",
843 G_CALLBACK (modest_ui_actions_on_msg_link_contextual), obj);
844 g_signal_connect (G_OBJECT(priv->msg_view), "limit_error",
845 G_CALLBACK (modest_ui_actions_on_limit_error), obj);
846 g_signal_connect (G_OBJECT(priv->msg_view), "handle_calendar",
847 G_CALLBACK (on_handle_calendar), obj);
848 g_signal_connect (G_OBJECT (priv->msg_view), "fetch_image",
849 G_CALLBACK (on_fetch_image), obj);
851 g_signal_connect (G_OBJECT (obj), "key-release-event",
852 G_CALLBACK (modest_msg_view_window_key_event),
855 g_signal_connect (G_OBJECT (obj), "key-press-event",
856 G_CALLBACK (modest_msg_view_window_key_event),
859 g_signal_connect (G_OBJECT (obj), "move-focus",
860 G_CALLBACK (on_move_focus), obj);
862 g_signal_connect (G_OBJECT (obj), "map-event",
863 G_CALLBACK (_modest_msg_view_window_map_event),
866 /* Mail Operation Queue */
867 priv->queue_change_handler = g_signal_connect (G_OBJECT (modest_runtime_get_mail_operation_queue ()),
869 G_CALLBACK (on_queue_changed),
872 /* Account manager */
873 priv->account_removed_handler = g_signal_connect (G_OBJECT (modest_runtime_get_account_store ()),
875 G_CALLBACK(on_account_removed),
878 modest_window_set_active_account (MODEST_WINDOW(obj), modest_account_name);
879 modest_window_set_active_mailbox (MODEST_WINDOW(obj), mailbox);
881 /* First add out toolbar ... */
882 modest_msg_view_window_show_toolbar (MODEST_WINDOW (obj), TRUE);
884 priv->isearch_toolbar = modest_toolkit_factory_create_isearch_toolbar (modest_runtime_get_toolkit_factory (),
886 modest_window_add_toolbar (MODEST_WINDOW (obj), GTK_TOOLBAR (priv->isearch_toolbar));
887 gtk_widget_set_no_show_all (priv->isearch_toolbar, TRUE);
888 g_signal_connect (G_OBJECT (priv->isearch_toolbar), "isearch-close",
889 G_CALLBACK (modest_msg_view_window_isearch_toolbar_close), obj);
890 g_signal_connect (G_OBJECT (priv->isearch_toolbar), "isearch-search",
891 G_CALLBACK (modest_msg_view_window_isearch_toolbar_search), obj);
892 priv->last_search = NULL;
894 /* Init the clipboard actions dim status */
895 modest_msg_view_grab_focus(MODEST_MSG_VIEW (priv->msg_view));
897 update_window_title (MODEST_MSG_VIEW_WINDOW (obj));
902 /* FIXME: parameter checks */
904 modest_msg_view_window_new_with_header_model (TnyMsg *msg,
905 const gchar *modest_account_name,
906 const gchar *mailbox,
907 const gchar *msg_uid,
909 GtkTreeRowReference *row_reference)
911 ModestMsgViewWindow *window = NULL;
912 ModestMsgViewWindowPrivate *priv = NULL;
913 TnyFolder *header_folder = NULL;
914 ModestHeaderView *header_view = NULL;
915 ModestWindowMgr *mgr = NULL;
918 modest_tny_mime_part_to_string (TNY_MIME_PART (msg), 0);
921 mgr = modest_runtime_get_window_mgr ();
922 window = MODEST_MSG_VIEW_WINDOW (modest_window_mgr_get_msg_view_window (mgr));
923 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), NULL);
925 modest_msg_view_window_construct (window, modest_account_name, mailbox, msg_uid);
927 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
928 priv->top_msg = NULL;
930 /* Remember the message list's TreeModel so we can detect changes
931 * and change the list selection when necessary: */
932 header_folder = modest_header_view_get_folder (header_view);
934 priv->is_outbox = (modest_tny_folder_guess_folder_type (header_folder) ==
935 TNY_FOLDER_TYPE_OUTBOX);
936 priv->header_folder_id = tny_folder_get_id (header_folder);
937 g_object_unref(header_folder);
940 /* Setup row references and connect signals */
941 priv->header_model = g_object_ref (model);
943 if (row_reference && gtk_tree_row_reference_valid (row_reference)) {
944 priv->row_reference = gtk_tree_row_reference_copy (row_reference);
945 priv->next_row_reference = gtk_tree_row_reference_copy (row_reference);
946 select_next_valid_row (model, &(priv->next_row_reference), TRUE, priv->is_outbox);
948 priv->row_reference = NULL;
949 priv->next_row_reference = NULL;
952 /* Connect signals */
953 priv->row_changed_handler =
954 g_signal_connect (GTK_TREE_MODEL(model), "row-changed",
955 G_CALLBACK(modest_msg_view_window_on_row_changed),
957 priv->row_deleted_handler =
958 g_signal_connect (GTK_TREE_MODEL(model), "row-deleted",
959 G_CALLBACK(modest_msg_view_window_on_row_deleted),
961 priv->row_inserted_handler =
962 g_signal_connect (GTK_TREE_MODEL(model), "row-inserted",
963 G_CALLBACK(modest_msg_view_window_on_row_inserted),
965 priv->rows_reordered_handler =
966 g_signal_connect(GTK_TREE_MODEL(model), "rows-reordered",
967 G_CALLBACK(modest_msg_view_window_on_row_reordered),
970 if (header_view != NULL){
971 modest_header_view_add_observer(header_view,
972 MODEST_HEADER_VIEW_OBSERVER(window));
975 tny_msg_view_set_msg (TNY_MSG_VIEW (priv->msg_view), msg);
976 update_window_title (MODEST_MSG_VIEW_WINDOW (window));
977 update_branding (MODEST_MSG_VIEW_WINDOW (window));
979 /* gtk_widget_show_all (GTK_WIDGET (window)); */
980 modest_msg_view_window_update_priority (window);
981 /* Check dimming rules */
982 modest_ui_actions_check_toolbar_dimming_rules (MODEST_WINDOW (window));
983 modest_ui_actions_check_menu_dimming_rules (MODEST_WINDOW (window));
984 modest_window_check_dimming_rules_group (MODEST_WINDOW (window), MODEST_DIMMING_RULES_CLIPBOARD);
986 return MODEST_WINDOW(window);
990 modest_msg_view_window_new_from_uid (const gchar *modest_account_name,
991 const gchar *mailbox,
992 const gchar *msg_uid)
994 ModestMsgViewWindow *window = NULL;
995 ModestMsgViewWindowPrivate *priv = NULL;
996 ModestWindowMgr *mgr = NULL;
998 TnyAccount *account = NULL;
1000 mgr = modest_runtime_get_window_mgr ();
1001 window = MODEST_MSG_VIEW_WINDOW (modest_window_mgr_get_msg_view_window (mgr));
1002 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), NULL);
1004 modest_msg_view_window_construct (window, modest_account_name, mailbox, msg_uid);
1006 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1007 priv->top_msg = NULL;
1009 is_merge = g_str_has_prefix (msg_uid, "merge:");
1011 /* Get the account */
1013 account = tny_account_store_find_account (TNY_ACCOUNT_STORE (modest_runtime_get_account_store ()),
1016 if (is_merge || account) {
1017 TnyFolder *folder = NULL;
1019 /* Try to get the message, if it's already downloaded
1020 we don't need to connect */
1022 folder = modest_tny_folder_store_find_folder_from_uri (TNY_FOLDER_STORE (account), msg_uid);
1024 ModestTnyAccountStore *account_store;
1025 ModestTnyLocalFoldersAccount *local_folders_account;
1027 account_store = modest_runtime_get_account_store ();
1028 local_folders_account = MODEST_TNY_LOCAL_FOLDERS_ACCOUNT (
1029 modest_tny_account_store_get_local_folders_account (account_store));
1030 folder = modest_tny_local_folders_account_get_merged_outbox (local_folders_account);
1031 g_object_unref (local_folders_account);
1035 gboolean device_online;
1037 device = modest_runtime_get_device();
1038 device_online = tny_device_is_online (device);
1039 if (device_online) {
1040 message_reader (window, priv, NULL, msg_uid, folder, NULL);
1042 TnyMsg *msg = tny_folder_find_msg (folder, msg_uid, NULL);
1044 tny_msg_view_set_msg (TNY_MSG_VIEW (priv->msg_view), msg);
1045 update_window_title (MODEST_MSG_VIEW_WINDOW (window));
1046 update_branding (MODEST_MSG_VIEW_WINDOW (window));
1047 g_object_unref (msg);
1048 /* Sync flags to server */
1049 sync_flags (MODEST_MSG_VIEW_WINDOW (window));
1051 message_reader (window, priv, NULL, msg_uid, folder, NULL);
1054 g_object_unref (folder);
1059 /* Check dimming rules */
1060 modest_ui_actions_check_toolbar_dimming_rules (MODEST_WINDOW (window));
1061 modest_ui_actions_check_menu_dimming_rules (MODEST_WINDOW (window));
1062 modest_window_check_dimming_rules_group (MODEST_WINDOW (window), MODEST_DIMMING_RULES_CLIPBOARD);
1064 return MODEST_WINDOW(window);
1068 modest_msg_view_window_new_from_header_view (ModestHeaderView *header_view,
1069 const gchar *modest_account_name,
1070 const gchar *mailbox,
1071 const gchar *msg_uid,
1072 GtkTreeRowReference *row_reference)
1074 ModestMsgViewWindow *window = NULL;
1075 ModestMsgViewWindowPrivate *priv = NULL;
1076 TnyFolder *header_folder = NULL;
1077 ModestWindowMgr *mgr = NULL;
1081 mgr = modest_runtime_get_window_mgr ();
1082 window = MODEST_MSG_VIEW_WINDOW (modest_window_mgr_get_msg_view_window (mgr));
1083 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), NULL);
1085 modest_msg_view_window_construct (window, modest_account_name, mailbox, msg_uid);
1087 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1088 priv->top_msg = NULL;
1090 /* Remember the message list's TreeModel so we can detect changes
1091 * and change the list selection when necessary: */
1093 if (header_view != NULL){
1094 header_folder = modest_header_view_get_folder(header_view);
1095 /* This could happen if the header folder was
1096 unseleted before opening this msg window (for
1097 example if the user selects an account in the
1098 folder view of the main window */
1099 if (header_folder) {
1100 priv->is_outbox = (modest_tny_folder_guess_folder_type (header_folder) ==
1101 TNY_FOLDER_TYPE_OUTBOX);
1102 priv->header_folder_id = tny_folder_get_id(header_folder);
1103 g_object_unref(header_folder);
1107 /* Setup row references and connect signals */
1108 priv->header_model = gtk_tree_view_get_model (GTK_TREE_VIEW (header_view));
1109 g_object_ref (priv->header_model);
1111 if (row_reference && gtk_tree_row_reference_valid (row_reference)) {
1112 priv->row_reference = gtk_tree_row_reference_copy (row_reference);
1113 priv->next_row_reference = gtk_tree_row_reference_copy (row_reference);
1114 select_next_valid_row (priv->header_model, &(priv->next_row_reference), TRUE, priv->is_outbox);
1116 priv->row_reference = NULL;
1117 priv->next_row_reference = NULL;
1120 /* Connect signals */
1121 priv->row_changed_handler =
1122 g_signal_connect (GTK_TREE_MODEL(priv->header_model), "row-changed",
1123 G_CALLBACK(modest_msg_view_window_on_row_changed),
1125 priv->row_deleted_handler =
1126 g_signal_connect (GTK_TREE_MODEL(priv->header_model), "row-deleted",
1127 G_CALLBACK(modest_msg_view_window_on_row_deleted),
1129 priv->row_inserted_handler =
1130 g_signal_connect (GTK_TREE_MODEL(priv->header_model), "row-inserted",
1131 G_CALLBACK(modest_msg_view_window_on_row_inserted),
1133 priv->rows_reordered_handler =
1134 g_signal_connect(GTK_TREE_MODEL(priv->header_model), "rows-reordered",
1135 G_CALLBACK(modest_msg_view_window_on_row_reordered),
1138 if (header_view != NULL){
1139 modest_header_view_add_observer(header_view,
1140 MODEST_HEADER_VIEW_OBSERVER(window));
1143 tny_msg_view_set_msg (TNY_MSG_VIEW (priv->msg_view), NULL);
1144 update_branding (MODEST_MSG_VIEW_WINDOW (window));
1146 if (priv->row_reference) {
1147 path = gtk_tree_row_reference_get_path (priv->row_reference);
1148 if (gtk_tree_model_get_iter (priv->header_model, &iter, path)) {
1150 gtk_tree_model_get (priv->header_model, &iter,
1151 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
1153 message_reader (window, priv, header, NULL, NULL, priv->row_reference);
1154 g_object_unref (header);
1156 gtk_tree_path_free (path);
1158 /* Check dimming rules */
1159 modest_ui_actions_check_toolbar_dimming_rules (MODEST_WINDOW (window));
1160 modest_ui_actions_check_menu_dimming_rules (MODEST_WINDOW (window));
1161 modest_window_check_dimming_rules_group (MODEST_WINDOW (window), MODEST_DIMMING_RULES_CLIPBOARD);
1163 return MODEST_WINDOW(window);
1167 modest_msg_view_window_new_for_search_result (TnyMsg *msg,
1168 const gchar *modest_account_name,
1169 const gchar *mailbox,
1170 const gchar *msg_uid)
1172 ModestMsgViewWindow *window = NULL;
1173 ModestMsgViewWindowPrivate *priv = NULL;
1174 ModestWindowMgr *mgr = NULL;
1176 mgr = modest_runtime_get_window_mgr ();
1177 window = MODEST_MSG_VIEW_WINDOW (modest_window_mgr_get_msg_view_window (mgr));
1178 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), NULL);
1179 modest_msg_view_window_construct (window, modest_account_name, mailbox, msg_uid);
1181 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1182 priv->top_msg = NULL;
1184 /* Remember that this is a search result,
1185 * so we can disable some UI appropriately: */
1186 priv->is_search_result = TRUE;
1188 tny_msg_view_set_msg (TNY_MSG_VIEW (priv->msg_view), msg);
1189 update_branding (MODEST_MSG_VIEW_WINDOW (window));
1191 update_window_title (window);
1192 /* gtk_widget_show_all (GTK_WIDGET (window));*/
1193 modest_msg_view_window_update_priority (window);
1195 /* Check dimming rules */
1196 modest_ui_actions_check_toolbar_dimming_rules (MODEST_WINDOW (window));
1197 modest_ui_actions_check_menu_dimming_rules (MODEST_WINDOW (window));
1198 modest_window_check_dimming_rules_group (MODEST_WINDOW (window), MODEST_DIMMING_RULES_CLIPBOARD);
1200 return MODEST_WINDOW(window);
1204 modest_msg_view_window_is_other_body (ModestMsgViewWindow *self)
1206 ModestMsgViewWindowPrivate *priv = NULL;
1208 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self), FALSE);
1209 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
1211 return (priv->other_body != NULL);
1215 modest_msg_view_window_new_with_other_body (TnyMsg *msg,
1216 TnyMimePart *other_body,
1218 const gchar *modest_account_name,
1219 const gchar *mailbox,
1220 const gchar *msg_uid)
1222 GObject *obj = NULL;
1223 ModestMsgViewWindowPrivate *priv;
1224 ModestWindowMgr *mgr = NULL;
1226 g_return_val_if_fail (msg, NULL);
1227 mgr = modest_runtime_get_window_mgr ();
1228 obj = G_OBJECT (modest_window_mgr_get_msg_view_window (mgr));
1229 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (obj);
1230 modest_msg_view_window_construct (MODEST_MSG_VIEW_WINDOW (obj),
1231 modest_account_name, mailbox, msg_uid);
1234 priv->other_body = g_object_ref (other_body);
1235 modest_msg_view_set_msg_with_other_body (MODEST_MSG_VIEW (priv->msg_view), msg, other_body);
1237 tny_msg_view_set_msg (TNY_MSG_VIEW (priv->msg_view), msg);
1240 priv->top_msg = g_object_ref (top_msg);
1242 priv->top_msg = NULL;
1244 update_window_title (MODEST_MSG_VIEW_WINDOW (obj));
1245 update_branding (MODEST_MSG_VIEW_WINDOW (obj));
1247 /* gtk_widget_show_all (GTK_WIDGET (obj)); */
1249 /* Check dimming rules */
1250 modest_ui_actions_check_toolbar_dimming_rules (MODEST_WINDOW (obj));
1251 modest_ui_actions_check_menu_dimming_rules (MODEST_WINDOW (obj));
1252 modest_window_check_dimming_rules_group (MODEST_WINDOW (obj), MODEST_DIMMING_RULES_CLIPBOARD);
1254 return MODEST_WINDOW(obj);
1258 modest_msg_view_window_new_for_attachment (TnyMsg *msg,
1260 const gchar *modest_account_name,
1261 const gchar *mailbox,
1262 const gchar *msg_uid)
1264 return modest_msg_view_window_new_with_other_body (msg, NULL, top_msg, modest_account_name, mailbox, msg_uid);
1268 modest_msg_view_window_on_row_changed (GtkTreeModel *header_model,
1271 ModestMsgViewWindow *window)
1273 check_dimming_rules_after_change (window);
1277 modest_msg_view_window_on_row_deleted(GtkTreeModel *header_model,
1279 ModestMsgViewWindow *window)
1281 check_dimming_rules_after_change (window);
1283 /* The window could have dissapeared */
1286 check_dimming_rules_after_change (ModestMsgViewWindow *window)
1288 modest_ui_actions_check_menu_dimming_rules (MODEST_WINDOW (window));
1289 modest_ui_actions_check_toolbar_dimming_rules (MODEST_WINDOW (window));
1293 /* On insertions we check if the folder still has the message we are
1294 * showing or do not. If do not, we do nothing. Which means we are still
1295 * not attached to any header folder and thus next/prev buttons are
1296 * still dimmed. Once the message that is shown by msg-view is found, the
1297 * new model of header-view will be attached and the references will be set.
1298 * On each further insertions dimming rules will be checked. However
1299 * this requires extra CPU time at least works.
1300 * (An message might be deleted from TnyFolder and thus will not be
1301 * inserted into the model again for example if it is removed by the
1302 * imap server and the header view is refreshed.)
1305 modest_msg_view_window_on_row_inserted (GtkTreeModel *model,
1306 GtkTreePath *tree_path,
1307 GtkTreeIter *tree_iter,
1308 ModestMsgViewWindow *window)
1310 ModestMsgViewWindowPrivate *priv = NULL;
1311 TnyHeader *header = NULL;
1313 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window));
1314 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1316 g_assert (model == priv->header_model);
1318 /* Check if the newly inserted message is the same we are actually
1319 * showing. IF not, we should remain detached from the header model
1320 * and thus prev and next toolbar buttons should remain dimmed. */
1321 gtk_tree_model_get (model, tree_iter,
1322 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
1325 if (TNY_IS_HEADER (header)) {
1328 uid = modest_tny_folder_get_header_unique_id (header);
1329 if (!g_str_equal(priv->msg_uid, uid)) {
1330 check_dimming_rules_after_change (window);
1332 g_object_unref (G_OBJECT(header));
1336 g_object_unref(G_OBJECT(header));
1339 if (priv->row_reference) {
1340 gtk_tree_row_reference_free (priv->row_reference);
1343 /* Setup row_reference for the actual msg. */
1344 priv->row_reference = gtk_tree_row_reference_new (priv->header_model, tree_path);
1345 if (priv->row_reference == NULL) {
1346 g_warning("%s: No reference for msg header item.", __FUNCTION__);
1350 /* Now set up next_row_reference. */
1351 if (priv->next_row_reference) {
1352 gtk_tree_row_reference_free (priv->next_row_reference);
1355 priv->next_row_reference = gtk_tree_row_reference_copy (priv->row_reference);
1356 select_next_valid_row (priv->header_model,
1357 &(priv->next_row_reference), FALSE, priv->is_outbox);
1359 /* Connect the remaining callbacks to become able to detect
1360 * changes in header-view. */
1361 priv->row_changed_handler =
1362 g_signal_connect (priv->header_model, "row-changed",
1363 G_CALLBACK (modest_msg_view_window_on_row_changed),
1365 priv->row_deleted_handler =
1366 g_signal_connect (priv->header_model, "row-deleted",
1367 G_CALLBACK (modest_msg_view_window_on_row_deleted),
1369 priv->rows_reordered_handler =
1370 g_signal_connect (priv->header_model, "rows-reordered",
1371 G_CALLBACK (modest_msg_view_window_on_row_reordered),
1374 check_dimming_rules_after_change (window);
1378 modest_msg_view_window_on_row_reordered (GtkTreeModel *header_model,
1382 ModestMsgViewWindow *window)
1384 ModestMsgViewWindowPrivate *priv = NULL;
1385 gboolean already_changed = FALSE;
1387 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(window);
1389 /* If the current row was reordered select the proper next
1390 valid row. The same if the next row reference changes */
1391 if (!priv->row_reference ||
1392 !gtk_tree_row_reference_valid (priv->row_reference))
1395 if (priv->next_row_reference &&
1396 gtk_tree_row_reference_valid (priv->next_row_reference)) {
1397 GtkTreePath *cur, *next;
1398 /* Check that the order is still the correct one */
1399 cur = gtk_tree_row_reference_get_path (priv->row_reference);
1400 next = gtk_tree_row_reference_get_path (priv->next_row_reference);
1401 gtk_tree_path_next (cur);
1402 if (gtk_tree_path_compare (cur, next) != 0) {
1403 gtk_tree_row_reference_free (priv->next_row_reference);
1404 priv->next_row_reference = gtk_tree_row_reference_copy (priv->row_reference);
1405 select_next_valid_row (header_model, &(priv->next_row_reference), FALSE, priv->is_outbox);
1406 already_changed = TRUE;
1408 gtk_tree_path_free (cur);
1409 gtk_tree_path_free (next);
1411 if (priv->next_row_reference)
1412 gtk_tree_row_reference_free (priv->next_row_reference);
1413 /* Update next row reference */
1414 priv->next_row_reference = gtk_tree_row_reference_copy (priv->row_reference);
1415 select_next_valid_row (header_model, &(priv->next_row_reference), FALSE, priv->is_outbox);
1416 already_changed = TRUE;
1419 check_dimming_rules_after_change (window);
1422 /* The modest_msg_view_window_update_model_replaced implements update
1423 * function for ModestHeaderViewObserver. Checks whether the TnyFolder
1424 * actually belongs to the header-view is the same as the TnyFolder of
1425 * the message of msg-view or not. If they are different, there is
1426 * nothing to do. If they are the same, then the model has replaced and
1427 * the reference in msg-view shall be replaced from the old model to
1428 * the new model. In this case the view will be detached from it's
1429 * header folder. From this point the next/prev buttons are dimmed.
1432 modest_msg_view_window_update_model_replaced (ModestHeaderViewObserver *observer,
1433 GtkTreeModel *model,
1434 const gchar *tny_folder_id)
1436 ModestMsgViewWindowPrivate *priv = NULL;
1437 ModestMsgViewWindow *window = NULL;
1439 g_assert(MODEST_IS_HEADER_VIEW_OBSERVER(observer));
1440 g_assert(MODEST_IS_MSG_VIEW_WINDOW(observer));
1442 window = MODEST_MSG_VIEW_WINDOW(observer);
1443 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(window);
1445 /* If there is an other folder in the header-view then we do
1446 * not care about it's model (msg list). Else if the
1447 * header-view shows the folder the msg shown by us is in, we
1448 * shall replace our model reference and make some check. */
1449 if(model == NULL || tny_folder_id == NULL ||
1450 (priv->header_folder_id && !g_str_equal(tny_folder_id, priv->header_folder_id)))
1453 /* Model is changed(replaced), so we should forget the old
1454 * one. Because there might be other references and there
1455 * might be some change on the model even if we unreferenced
1456 * it, we need to disconnect our signals here. */
1457 if (priv->header_model) {
1458 if (g_signal_handler_is_connected(G_OBJECT (priv->header_model),
1459 priv->row_changed_handler))
1460 g_signal_handler_disconnect(G_OBJECT (priv->header_model),
1461 priv->row_changed_handler);
1462 if (g_signal_handler_is_connected(G_OBJECT (priv->header_model),
1463 priv->row_deleted_handler))
1464 g_signal_handler_disconnect(G_OBJECT (priv->header_model),
1465 priv->row_deleted_handler);
1466 if (g_signal_handler_is_connected(G_OBJECT (priv->header_model),
1467 priv->row_inserted_handler))
1468 g_signal_handler_disconnect(G_OBJECT (priv->header_model),
1469 priv->row_inserted_handler);
1470 if (g_signal_handler_is_connected(G_OBJECT (priv->header_model),
1471 priv->rows_reordered_handler))
1472 g_signal_handler_disconnect(G_OBJECT (priv->header_model),
1473 priv->rows_reordered_handler);
1476 if (priv->row_reference)
1477 gtk_tree_row_reference_free (priv->row_reference);
1478 if (priv->next_row_reference)
1479 gtk_tree_row_reference_free (priv->next_row_reference);
1480 g_object_unref(priv->header_model);
1483 priv->row_changed_handler = 0;
1484 priv->row_deleted_handler = 0;
1485 priv->row_inserted_handler = 0;
1486 priv->rows_reordered_handler = 0;
1487 priv->next_row_reference = NULL;
1488 priv->row_reference = NULL;
1489 priv->header_model = NULL;
1492 priv->header_model = g_object_ref (model);
1494 /* Also we must connect to the new model for row insertions.
1495 * Only for insertions now. We will need other ones only after
1496 * the msg is show by msg-view is added to the new model. */
1497 priv->row_inserted_handler =
1498 g_signal_connect (priv->header_model, "row-inserted",
1499 G_CALLBACK(modest_msg_view_window_on_row_inserted),
1502 modest_ui_actions_check_menu_dimming_rules(MODEST_WINDOW(window));
1503 modest_ui_actions_check_toolbar_dimming_rules(MODEST_WINDOW(window));
1507 modest_msg_view_window_toolbar_on_transfer_mode (ModestMsgViewWindow *self)
1509 ModestMsgViewWindowPrivate *priv= NULL;
1511 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self), FALSE);
1512 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
1514 return priv->progress_hint;
1518 modest_msg_view_window_get_header (ModestMsgViewWindow *self)
1520 ModestMsgViewWindowPrivate *priv= NULL;
1522 TnyHeader *header = NULL;
1523 GtkTreePath *path = NULL;
1526 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self), NULL);
1527 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
1529 /* If the message was not obtained from a treemodel,
1530 * for instance if it was opened directly by the search UI:
1532 if (priv->header_model == NULL ||
1533 priv->row_reference == NULL ||
1534 !gtk_tree_row_reference_valid (priv->row_reference)) {
1535 msg = modest_msg_view_window_get_message (self);
1537 header = tny_msg_get_header (msg);
1538 g_object_unref (msg);
1543 /* Get iter of the currently selected message in the header view: */
1544 path = gtk_tree_row_reference_get_path (priv->row_reference);
1545 g_return_val_if_fail (path != NULL, NULL);
1546 gtk_tree_model_get_iter (priv->header_model,
1550 /* Get current message header */
1551 gtk_tree_model_get (priv->header_model, &iter,
1552 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
1555 gtk_tree_path_free (path);
1560 modest_msg_view_window_get_message (ModestMsgViewWindow *self)
1562 ModestMsgViewWindowPrivate *priv;
1564 g_return_val_if_fail (self, NULL);
1566 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(self);
1568 return tny_msg_view_get_msg (TNY_MSG_VIEW (priv->msg_view));
1572 modest_msg_view_window_get_top_message (ModestMsgViewWindow *self)
1574 ModestMsgViewWindowPrivate *priv;
1576 g_return_val_if_fail (self, NULL);
1578 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(self);
1581 return g_object_ref (priv->top_msg);
1587 modest_msg_view_window_get_message_uid (ModestMsgViewWindow *self)
1589 ModestMsgViewWindowPrivate *priv;
1591 g_return_val_if_fail (self, NULL);
1593 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
1595 return (const gchar*) priv->msg_uid;
1598 /* Used for the Ctrl+F accelerator */
1600 modest_msg_view_window_toggle_isearch_toolbar (GtkWidget *obj,
1603 ModestMsgViewWindow *window = MODEST_MSG_VIEW_WINDOW (data);
1604 ModestMsgViewWindowPrivate *priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1606 if (GTK_WIDGET_VISIBLE (priv->isearch_toolbar)) {
1607 modest_msg_view_window_isearch_toolbar_close (obj, data);
1609 modest_msg_view_window_show_isearch_toolbar (obj, data);
1613 /* Handler for menu option */
1615 modest_msg_view_window_show_isearch_toolbar (GtkWidget *obj,
1618 ModestMsgViewWindow *window = MODEST_MSG_VIEW_WINDOW (data);
1619 ModestMsgViewWindowPrivate *priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1621 gtk_widget_show (priv->isearch_toolbar);
1622 modest_isearch_toolbar_highlight_entry (MODEST_ISEARCH_TOOLBAR (priv->isearch_toolbar), TRUE);
1625 /* Handler for click on the "X" close button in isearch toolbar */
1627 modest_msg_view_window_isearch_toolbar_close (GtkWidget *widget,
1628 ModestMsgViewWindow *obj)
1630 ModestMsgViewWindowPrivate *priv;
1632 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (obj);
1635 gtk_widget_hide (priv->isearch_toolbar);
1636 modest_msg_view_grab_focus (MODEST_MSG_VIEW (priv->msg_view));
1640 modest_msg_view_window_isearch_toolbar_search (GtkWidget *widget,
1641 ModestMsgViewWindow *obj)
1643 const gchar *current_search;
1644 ModestMsgViewWindowPrivate *priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (obj);
1646 if (modest_mime_part_view_is_empty (MODEST_MIME_PART_VIEW (priv->msg_view))) {
1647 modest_platform_system_banner (NULL, NULL, _("mail_ib_nothing_to_find"));
1651 current_search = modest_isearch_toolbar_get_search (MODEST_ISEARCH_TOOLBAR (widget));
1653 if ((current_search == NULL) || (strcmp (current_search, "") == 0)) {
1654 modest_platform_system_banner (NULL, NULL, _CS_FIND_REP_ENTER_TEXT);
1658 if ((priv->last_search == NULL) || (strcmp (priv->last_search, current_search) != 0)) {
1660 g_free (priv->last_search);
1661 priv->last_search = g_strdup (current_search);
1662 result = modest_isearch_view_search (MODEST_ISEARCH_VIEW (priv->msg_view),
1665 modest_platform_system_banner (NULL, NULL,
1666 _HL_IB_FIND_NO_MATCHES);
1667 g_free (priv->last_search);
1668 priv->last_search = NULL;
1670 modest_isearch_toolbar_highlight_entry (MODEST_ISEARCH_TOOLBAR (priv->isearch_toolbar), TRUE);
1673 if (!modest_isearch_view_search_next (MODEST_ISEARCH_VIEW (priv->msg_view))) {
1674 modest_platform_system_banner (NULL, NULL,
1675 _HL_IB_FIND_COMPLETE);
1676 g_free (priv->last_search);
1677 priv->last_search = NULL;
1679 modest_isearch_toolbar_highlight_entry (MODEST_ISEARCH_TOOLBAR (priv->isearch_toolbar), TRUE);
1686 modest_msg_view_window_set_zoom (ModestWindow *window,
1689 ModestMsgViewWindowPrivate *priv;
1690 ModestWindowPrivate *parent_priv;
1692 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window));
1694 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1695 parent_priv = MODEST_WINDOW_GET_PRIVATE (window);
1696 modest_zoomable_set_zoom (MODEST_ZOOMABLE (priv->msg_view), zoom);
1701 modest_msg_view_window_get_zoom (ModestWindow *window)
1703 ModestMsgViewWindowPrivate *priv;
1705 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), 1.0);
1707 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1708 return modest_zoomable_get_zoom (MODEST_ZOOMABLE (priv->msg_view));
1712 modest_msg_view_window_zoom_plus (ModestWindow *window)
1715 ModestMsgViewWindowPrivate *priv;
1719 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), 1.0);
1720 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1722 zoom_level = modest_zoomable_get_zoom (MODEST_ZOOMABLE (priv->msg_view));
1724 if (zoom_level >= 2.0) {
1725 modest_platform_system_banner (NULL, NULL,
1726 _CS_MAX_ZOOM_LEVEL_REACHED);
1728 } else if (zoom_level >= 1.5) {
1730 } else if (zoom_level >= 1.2) {
1732 } else if (zoom_level >= 1.0) {
1734 } else if (zoom_level >= 0.8) {
1736 } else if (zoom_level >= 0.5) {
1742 /* set zoom level */
1743 int_zoom = (gint) rint (zoom_level*100.0+0.1);
1744 banner_text = g_strdup_printf (_HL_IB_ZOOM, int_zoom);
1745 modest_platform_information_banner (GTK_WIDGET (window), NULL, banner_text);
1746 g_free (banner_text);
1747 modest_zoomable_set_zoom (MODEST_ZOOMABLE (priv->msg_view), zoom_level);
1753 modest_msg_view_window_zoom_minus (ModestWindow *window)
1756 ModestMsgViewWindowPrivate *priv;
1760 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), 1.0);
1761 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1763 zoom_level = modest_zoomable_get_zoom (MODEST_ZOOMABLE (priv->msg_view));
1765 if (zoom_level <= 0.5) {
1766 modest_platform_system_banner (NULL, NULL,
1767 _CS_MIN_ZOOM_LEVEL_REACHED);
1769 } else if (zoom_level <= 0.8) {
1771 } else if (zoom_level <= 1.0) {
1773 } else if (zoom_level <= 1.2) {
1775 } else if (zoom_level <= 1.5) {
1777 } else if (zoom_level <= 2.0) {
1783 /* set zoom level */
1784 int_zoom = (gint) rint (zoom_level*100.0+0.1);
1785 banner_text = g_strdup_printf (_HL_IB_ZOOM, int_zoom);
1786 modest_platform_information_banner (GTK_WIDGET (window), NULL, banner_text);
1787 g_free (banner_text);
1788 modest_zoomable_set_zoom (MODEST_ZOOMABLE (priv->msg_view), zoom_level);
1794 modest_msg_view_window_key_event (GtkWidget *window,
1800 focus = gtk_container_get_focus_child ((GtkContainer *) window);
1802 /* for the isearch toolbar case */
1803 if (focus && GTK_IS_ENTRY (focus)) {
1804 if (event->keyval == GDK_BackSpace) {
1806 copy = gdk_event_copy ((GdkEvent *) event);
1807 gtk_widget_event (focus, copy);
1808 gdk_event_free (copy);
1818 modest_msg_view_window_last_message_selected (ModestMsgViewWindow *window)
1821 ModestMsgViewWindowPrivate *priv;
1822 GtkTreeIter tmp_iter;
1823 gboolean is_last_selected;
1825 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), TRUE);
1826 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1828 /*if no model (so no rows at all), then virtually we are the last*/
1829 if (!priv->header_model || !priv->row_reference)
1832 if (!gtk_tree_row_reference_valid (priv->row_reference))
1835 path = gtk_tree_row_reference_get_path (priv->row_reference);
1839 is_last_selected = TRUE;
1840 while (is_last_selected) {
1842 gtk_tree_path_next (path);
1843 if (!gtk_tree_model_get_iter (priv->header_model, &tmp_iter, path))
1845 gtk_tree_model_get (priv->header_model, &tmp_iter,
1846 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
1849 if (msg_is_visible (header, priv->is_outbox))
1850 is_last_selected = FALSE;
1851 g_object_unref(G_OBJECT(header));
1854 gtk_tree_path_free (path);
1855 return is_last_selected;
1859 modest_msg_view_window_has_headers_model (ModestMsgViewWindow *window)
1861 ModestMsgViewWindowPrivate *priv;
1863 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), TRUE);
1864 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1866 return priv->header_model != NULL;
1870 modest_msg_view_window_is_search_result (ModestMsgViewWindow *window)
1872 ModestMsgViewWindowPrivate *priv;
1874 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), TRUE);
1875 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1877 return priv->is_search_result;
1881 msg_is_visible (TnyHeader *header, gboolean check_outbox)
1883 if ((tny_header_get_flags(header) & TNY_HEADER_FLAG_DELETED))
1885 if (!check_outbox) {
1888 ModestTnySendQueueStatus status;
1889 status = modest_tny_all_send_queues_get_msg_status (header);
1890 return ((status != MODEST_TNY_SEND_QUEUE_FAILED) &&
1891 (status != MODEST_TNY_SEND_QUEUE_SENDING));
1896 modest_msg_view_window_first_message_selected (ModestMsgViewWindow *window)
1899 ModestMsgViewWindowPrivate *priv;
1900 gboolean is_first_selected;
1901 GtkTreeIter tmp_iter;
1903 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), TRUE);
1904 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1906 /*if no model (so no rows at all), then virtually we are the first*/
1907 if (!priv->header_model || !priv->row_reference)
1910 if (!gtk_tree_row_reference_valid (priv->row_reference))
1913 path = gtk_tree_row_reference_get_path (priv->row_reference);
1917 is_first_selected = TRUE;
1918 while (is_first_selected) {
1920 if(!gtk_tree_path_prev (path))
1922 /* Here the 'if' is needless for logic, but let make sure
1923 * iter is valid for gtk_tree_model_get. */
1924 if (!gtk_tree_model_get_iter (priv->header_model, &tmp_iter, path))
1926 gtk_tree_model_get (priv->header_model, &tmp_iter,
1927 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
1930 if (msg_is_visible (header, priv->is_outbox))
1931 is_first_selected = FALSE;
1932 g_object_unref(G_OBJECT(header));
1935 gtk_tree_path_free (path);
1936 return is_first_selected;
1943 GtkTreeRowReference *row_reference;
1947 message_reader_performer (gboolean canceled,
1949 ModestWindow *parent_window,
1950 TnyAccount *account,
1953 ModestMailOperation *mail_op = NULL;
1954 MsgReaderInfo *info;
1956 info = (MsgReaderInfo *) user_data;
1957 if (canceled || err) {
1958 update_window_title (MODEST_MSG_VIEW_WINDOW (parent_window));
1959 modest_ui_actions_on_close_window (NULL, MODEST_WINDOW (parent_window));
1963 /* Register the header - it'll be unregistered in the callback */
1965 modest_window_mgr_register_header (modest_runtime_get_window_mgr (), info->header, NULL);
1967 /* New mail operation */
1968 mail_op = modest_mail_operation_new_with_error_handling (G_OBJECT(parent_window),
1969 modest_ui_actions_disk_operations_error_handler,
1972 modest_mail_operation_queue_add (modest_runtime_get_mail_operation_queue (), mail_op);
1974 modest_mail_operation_get_msg (mail_op, info->header, TRUE, view_msg_cb, info->row_reference);
1976 modest_mail_operation_find_msg (mail_op, info->folder, info->msg_uid, TRUE, view_msg_cb, NULL);
1977 g_object_unref (mail_op);
1979 /* Update dimming rules */
1980 modest_ui_actions_check_toolbar_dimming_rules (MODEST_WINDOW (parent_window));
1981 modest_ui_actions_check_menu_dimming_rules (MODEST_WINDOW (parent_window));
1984 /* Frees. The row_reference will be freed by the view_msg_cb callback */
1985 g_free (info->msg_uid);
1987 g_object_unref (info->folder);
1989 g_object_unref (info->header);
1990 g_slice_free (MsgReaderInfo, info);
1995 * Reads the message whose summary item is @header. It takes care of
1996 * several things, among others:
1998 * If the message was not previously downloaded then ask the user
1999 * before downloading. If there is no connection launch the connection
2000 * dialog. Update toolbar dimming rules.
2002 * Returns: TRUE if the mail operation was started, otherwise if the
2003 * user do not want to download the message, or if the user do not
2004 * want to connect, then the operation is not issued
2007 message_reader (ModestMsgViewWindow *window,
2008 ModestMsgViewWindowPrivate *priv,
2010 const gchar *msg_uid,
2012 GtkTreeRowReference *row_reference)
2014 ModestWindowMgr *mgr;
2015 TnyAccount *account = NULL;
2016 MsgReaderInfo *info;
2018 /* We set the header from model while we're loading */
2019 tny_header_view_set_header (TNY_HEADER_VIEW (priv->msg_view), header);
2020 modest_window_set_title (MODEST_WINDOW (window), _CS_UPDATING);
2026 g_object_ref (folder);
2028 mgr = modest_runtime_get_window_mgr ();
2029 /* Msg download completed */
2030 if (!header || !(tny_header_get_flags (header) & TNY_HEADER_FLAG_CACHED)) {
2032 /* Ask the user if he wants to download the message if
2034 if (!tny_device_is_online (modest_runtime_get_device())) {
2035 GtkResponseType response;
2036 GtkWindow *toplevel;
2038 toplevel = (GtkWindow *) gtk_widget_get_toplevel ((GtkWidget *) window);
2039 response = modest_platform_run_confirmation_dialog (toplevel, _("mcen_nc_get_msg"));
2040 if (response == GTK_RESPONSE_CANCEL) {
2041 update_window_title (window);
2046 folder = tny_header_get_folder (header);
2048 info = g_slice_new (MsgReaderInfo);
2049 info->msg_uid = g_strdup (msg_uid);
2051 info->header = g_object_ref (header);
2053 info->header = NULL;
2055 info->folder = g_object_ref (folder);
2057 info->folder = NULL;
2058 if (row_reference) {
2059 info->row_reference = gtk_tree_row_reference_copy (row_reference);
2061 info->row_reference = NULL;
2064 /* Offer the connection dialog if necessary */
2065 modest_platform_connect_if_remote_and_perform ((ModestWindow *) window,
2067 TNY_FOLDER_STORE (folder),
2068 message_reader_performer,
2071 g_object_unref (folder);
2077 folder = tny_header_get_folder (header);
2080 account = tny_folder_get_account (folder);
2082 info = g_slice_new (MsgReaderInfo);
2083 info->msg_uid = g_strdup (msg_uid);
2085 info->folder = g_object_ref (folder);
2087 info->folder = NULL;
2089 info->header = g_object_ref (header);
2091 info->header = NULL;
2093 info->row_reference = gtk_tree_row_reference_copy (row_reference);
2095 info->row_reference = NULL;
2097 message_reader_performer (FALSE, NULL, (ModestWindow *) window, account, info);
2099 g_object_unref (account);
2101 g_object_unref (folder);
2107 modest_msg_view_window_select_next_message (ModestMsgViewWindow *window)
2109 ModestMsgViewWindowPrivate *priv;
2110 GtkTreePath *path= NULL;
2111 GtkTreeIter tmp_iter;
2113 gboolean retval = TRUE;
2114 GtkTreeRowReference *row_reference = NULL;
2116 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), FALSE);
2117 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
2119 if (!priv->row_reference)
2122 /* Update the next row reference if it's not valid. This could
2123 happen if for example the header which it was pointing to,
2124 was deleted. The best place to do it is in the row-deleted
2125 handler but the tinymail model do not work like the glib
2126 tree models and reports the deletion when the row is still
2128 if (!gtk_tree_row_reference_valid (priv->next_row_reference)) {
2129 if (priv->next_row_reference) {
2130 gtk_tree_row_reference_free (priv->next_row_reference);
2132 if (gtk_tree_row_reference_valid (priv->row_reference)) {
2133 priv->next_row_reference = gtk_tree_row_reference_copy (priv->row_reference);
2134 select_next_valid_row (priv->header_model, &(priv->next_row_reference), FALSE, priv->is_outbox);
2136 priv->next_row_reference = NULL;
2139 if (priv->next_row_reference)
2140 path = gtk_tree_row_reference_get_path (priv->next_row_reference);
2144 row_reference = gtk_tree_row_reference_copy (priv->next_row_reference);
2146 gtk_tree_model_get_iter (priv->header_model,
2149 gtk_tree_path_free (path);
2151 gtk_tree_model_get (priv->header_model, &tmp_iter,
2152 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
2155 /* Read the message & show it */
2156 if (!message_reader (window, priv, header, NULL, NULL, row_reference)) {
2159 gtk_tree_row_reference_free (row_reference);
2162 g_object_unref (header);
2168 modest_msg_view_window_select_previous_message (ModestMsgViewWindow *window)
2170 ModestMsgViewWindowPrivate *priv = NULL;
2172 gboolean finished = FALSE;
2173 gboolean retval = FALSE;
2175 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), FALSE);
2176 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
2178 if (priv->row_reference && !gtk_tree_row_reference_valid (priv->row_reference)) {
2179 gtk_tree_row_reference_free (priv->row_reference);
2180 priv->row_reference = NULL;
2183 /* Return inmediatly if there is no header model */
2184 if (!priv->header_model || !priv->row_reference)
2187 path = gtk_tree_row_reference_get_path (priv->row_reference);
2188 while (!finished && gtk_tree_path_prev (path)) {
2192 gtk_tree_model_get_iter (priv->header_model, &iter, path);
2193 gtk_tree_model_get (priv->header_model, &iter,
2194 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
2198 if (msg_is_visible (header, priv->is_outbox)) {
2199 GtkTreeRowReference *row_reference;
2200 row_reference = gtk_tree_row_reference_new (priv->header_model, path);
2201 /* Read the message & show it */
2202 retval = message_reader (window, priv, header, NULL, NULL, row_reference);
2203 gtk_tree_row_reference_free (row_reference);
2207 g_object_unref (header);
2211 gtk_tree_path_free (path);
2216 view_msg_cb (ModestMailOperation *mail_op,
2223 ModestMsgViewWindow *self = NULL;
2224 ModestMsgViewWindowPrivate *priv = NULL;
2225 GtkTreeRowReference *row_reference = NULL;
2227 /* Unregister the header (it was registered before creating the mail operation) */
2228 modest_window_mgr_unregister_header (modest_runtime_get_window_mgr (), header);
2230 row_reference = (GtkTreeRowReference *) user_data;
2233 gtk_tree_row_reference_free (row_reference);
2234 self = (ModestMsgViewWindow *) modest_mail_operation_get_source (mail_op);
2236 /* Restore window title */
2237 update_window_title (self);
2238 modest_ui_actions_on_close_window (NULL, MODEST_WINDOW (self));
2239 g_object_unref (self);
2244 /* If there was any error */
2245 if (!modest_ui_actions_msg_retrieval_check (mail_op, header, msg)) {
2247 gtk_tree_row_reference_free (row_reference);
2248 self = (ModestMsgViewWindow *) modest_mail_operation_get_source (mail_op);
2250 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
2251 /* First we check if the parent is a folder window */
2252 if (priv->msg_uid && !modest_window_mgr_get_folder_window (MODEST_WINDOW_MGR (modest_runtime_get_window_mgr ()))) {
2254 TnyAccount *account = NULL;
2255 GtkWidget *header_window = NULL;
2257 is_merge = g_str_has_prefix (priv->msg_uid, "merge:");
2259 /* Get the account */
2261 account = tny_account_store_find_account (TNY_ACCOUNT_STORE (modest_runtime_get_account_store ()),
2264 if (is_merge || account) {
2265 TnyFolder *folder = NULL;
2267 /* Try to get the message, if it's already downloaded
2268 we don't need to connect */
2270 folder = modest_tny_folder_store_find_folder_from_uri (TNY_FOLDER_STORE (account),
2273 ModestTnyAccountStore *account_store;
2274 ModestTnyLocalFoldersAccount *local_folders_account;
2276 account_store = modest_runtime_get_account_store ();
2277 local_folders_account = MODEST_TNY_LOCAL_FOLDERS_ACCOUNT (
2278 modest_tny_account_store_get_local_folders_account (account_store));
2279 folder = modest_tny_local_folders_account_get_merged_outbox (local_folders_account);
2280 g_object_unref (local_folders_account);
2282 if (account) g_object_unref (account);
2285 header_window = (GtkWidget *)
2286 modest_header_window_new (
2288 modest_window_get_active_account (MODEST_WINDOW (self)),
2289 modest_window_get_active_mailbox (MODEST_WINDOW (self)));
2290 if (!modest_window_mgr_register_window (modest_runtime_get_window_mgr (),
2291 MODEST_WINDOW (header_window),
2293 gtk_widget_destroy (GTK_WIDGET (header_window));
2295 gtk_widget_show_all (GTK_WIDGET (header_window));
2297 g_object_unref (folder);
2303 /* Restore window title */
2304 update_window_title (self);
2305 modest_ui_actions_on_close_window (NULL, MODEST_WINDOW (self));
2306 g_object_unref (self);
2311 /* Get the window */
2312 self = (ModestMsgViewWindow *) modest_mail_operation_get_source (mail_op);
2313 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self));
2314 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
2316 /* Update the row reference */
2317 if (priv->row_reference != NULL) {
2318 gtk_tree_row_reference_free (priv->row_reference);
2319 priv->row_reference = (row_reference && gtk_tree_row_reference_valid (row_reference))?gtk_tree_row_reference_copy (row_reference):NULL;
2320 if (priv->next_row_reference != NULL) {
2321 gtk_tree_row_reference_free (priv->next_row_reference);
2323 if (priv->row_reference) {
2324 priv->next_row_reference = gtk_tree_row_reference_copy (priv->row_reference);
2325 select_next_valid_row (priv->header_model, &(priv->next_row_reference), TRUE, priv->is_outbox);
2327 priv->next_row_reference = NULL;
2331 /* Mark header as read */
2332 if (!(tny_header_get_flags (header) & TNY_HEADER_FLAG_SEEN))
2333 tny_header_set_flag (header, TNY_HEADER_FLAG_SEEN);
2335 /* Set new message */
2336 if (priv->msg_view != NULL && TNY_IS_MSG_VIEW (priv->msg_view)) {
2337 tny_msg_view_set_msg (TNY_MSG_VIEW (priv->msg_view), msg);
2338 modest_msg_view_window_update_priority (self);
2339 update_window_title (MODEST_MSG_VIEW_WINDOW (self));
2340 update_branding (MODEST_MSG_VIEW_WINDOW (self));
2341 modest_msg_view_grab_focus (MODEST_MSG_VIEW (priv->msg_view));
2344 /* Set the new message uid of the window */
2345 if (priv->msg_uid) {
2346 g_free (priv->msg_uid);
2347 priv->msg_uid = modest_tny_folder_get_header_unique_id (header);
2350 /* Notify the observers */
2351 g_signal_emit (G_OBJECT (self), signals[MSG_CHANGED_SIGNAL],
2352 0, priv->header_model, priv->row_reference);
2354 /* Sync the flags if the message is not opened from a header
2355 model, i.e, if it's opened from a notification */
2356 if (!priv->header_model)
2360 g_object_unref (self);
2362 gtk_tree_row_reference_free (row_reference);
2366 modest_msg_view_window_get_folder_type (ModestMsgViewWindow *window)
2368 ModestMsgViewWindowPrivate *priv;
2370 TnyFolderType folder_type;
2372 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
2374 folder_type = TNY_FOLDER_TYPE_UNKNOWN;
2376 msg = tny_msg_view_get_msg (TNY_MSG_VIEW (priv->msg_view));
2380 folder = tny_msg_get_folder (msg);
2382 folder_type = modest_tny_folder_guess_folder_type (folder);
2383 g_object_unref (folder);
2385 g_object_unref (msg);
2393 modest_msg_view_window_update_priority (ModestMsgViewWindow *window)
2395 ModestMsgViewWindowPrivate *priv;
2396 TnyHeader *header = NULL;
2397 TnyHeaderFlags flags = 0;
2399 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
2401 if (priv->header_model && priv->row_reference && gtk_tree_row_reference_valid (priv->row_reference)) {
2403 GtkTreePath *path = NULL;
2405 path = gtk_tree_row_reference_get_path (priv->row_reference);
2406 g_return_if_fail (path != NULL);
2407 gtk_tree_model_get_iter (priv->header_model,
2409 gtk_tree_row_reference_get_path (priv->row_reference));
2411 gtk_tree_model_get (priv->header_model, &iter, TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
2413 gtk_tree_path_free (path);
2416 msg = tny_msg_view_get_msg (TNY_MSG_VIEW (priv->msg_view));
2418 header = tny_msg_get_header (msg);
2419 g_object_unref (msg);
2424 flags = tny_header_get_flags (header);
2425 g_object_unref(G_OBJECT(header));
2428 modest_msg_view_set_priority (MODEST_MSG_VIEW(priv->msg_view), flags);
2433 toolbar_resize (ModestMsgViewWindow *self)
2435 ModestMsgViewWindowPrivate *priv = NULL;
2436 ModestWindowPrivate *parent_priv = NULL;
2438 gint static_button_size;
2439 ModestWindowMgr *mgr;
2441 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self));
2442 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
2443 parent_priv = MODEST_WINDOW_GET_PRIVATE(self);
2445 mgr = modest_runtime_get_window_mgr ();
2446 static_button_size = modest_window_mgr_get_fullscreen_mode (mgr)?120:120;
2448 if (parent_priv->toolbar) {
2449 /* Set expandable and homogeneous tool buttons */
2450 widget = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/ToolbarMessageReply");
2451 gtk_tool_item_set_expand (GTK_TOOL_ITEM (widget), TRUE);
2452 gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (widget), TRUE);
2453 widget = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/ToolbarMessageReplyAll");
2454 gtk_tool_item_set_expand (GTK_TOOL_ITEM (widget), TRUE);
2455 gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (widget), TRUE);
2456 widget = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/ToolbarMessageForward");
2457 gtk_tool_item_set_expand (GTK_TOOL_ITEM (widget), TRUE);
2458 gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (widget), TRUE);
2459 widget = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/ToolbarDeleteMessage");
2460 gtk_tool_item_set_expand (GTK_TOOL_ITEM (widget), TRUE);
2461 gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (widget), TRUE);
2462 widget = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/ToolbarDownloadExternalImages");
2463 gtk_tool_item_set_expand (GTK_TOOL_ITEM (widget), TRUE);
2464 gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (widget), TRUE);
2465 gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (priv->next_toolitem), TRUE);
2466 gtk_tool_item_set_expand (GTK_TOOL_ITEM (priv->next_toolitem), TRUE);
2467 gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (priv->prev_toolitem), TRUE);
2468 gtk_tool_item_set_expand (GTK_TOOL_ITEM (priv->prev_toolitem), TRUE);
2473 modest_msg_view_window_show_toolbar (ModestWindow *self,
2474 gboolean show_toolbar)
2476 ModestMsgViewWindowPrivate *priv = NULL;
2477 ModestWindowPrivate *parent_priv;
2479 parent_priv = MODEST_WINDOW_GET_PRIVATE(self);
2480 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(self);
2482 /* Set optimized view status */
2483 priv->optimized_view = !show_toolbar;
2485 if (!parent_priv->toolbar) {
2486 parent_priv->toolbar = gtk_ui_manager_get_widget (parent_priv->ui_manager,
2489 #ifdef MODEST_TOOLKIT_HILDON2
2490 gtk_toolbar_set_icon_size (GTK_TOOLBAR (parent_priv->toolbar), HILDON_ICON_SIZE_FINGER);
2492 gtk_toolbar_set_icon_size (GTK_TOOLBAR (parent_priv->toolbar), GTK_ICON_SIZE_LARGE_TOOLBAR);
2494 gtk_widget_set_no_show_all (parent_priv->toolbar, TRUE);
2496 priv->next_toolitem = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/ToolbarMessageNext");
2497 priv->prev_toolitem = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/ToolbarMessageBack");
2498 toolbar_resize (MODEST_MSG_VIEW_WINDOW (self));
2500 modest_window_add_toolbar (MODEST_WINDOW (self),
2501 GTK_TOOLBAR (parent_priv->toolbar));
2506 /* Quick hack: this prevents toolbar icons "dance" when progress bar show status is changed */
2507 /* TODO: resize mode migth be GTK_RESIZE_QUEUE, in order to avoid unneccesary shows */
2508 gtk_container_set_resize_mode (GTK_CONTAINER(parent_priv->toolbar), GTK_RESIZE_IMMEDIATE);
2510 gtk_widget_show (GTK_WIDGET (parent_priv->toolbar));
2511 if (modest_msg_view_window_transfer_mode_enabled (MODEST_MSG_VIEW_WINDOW (self)))
2512 set_progress_hint (MODEST_MSG_VIEW_WINDOW (self), TRUE);
2514 set_progress_hint (MODEST_MSG_VIEW_WINDOW (self), FALSE);
2517 gtk_widget_set_no_show_all (parent_priv->toolbar, TRUE);
2518 gtk_widget_hide (GTK_WIDGET (parent_priv->toolbar));
2523 modest_msg_view_window_clipboard_owner_change (GtkClipboard *clipboard,
2525 ModestMsgViewWindow *window)
2527 if (!GTK_WIDGET_VISIBLE (window))
2530 modest_window_check_dimming_rules_group (MODEST_WINDOW (window), MODEST_DIMMING_RULES_CLIPBOARD);
2534 modest_msg_view_window_transfer_mode_enabled (ModestMsgViewWindow *self)
2536 ModestMsgViewWindowPrivate *priv;
2538 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self), FALSE);
2539 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(self);
2541 return priv->progress_hint;
2545 observers_empty (ModestMsgViewWindow *self)
2548 ModestMsgViewWindowPrivate *priv;
2549 gboolean is_empty = TRUE;
2550 guint pending_ops = 0;
2552 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(self);
2553 tmp = priv->progress_widgets;
2555 /* Check all observers */
2556 while (tmp && is_empty) {
2557 pending_ops = modest_progress_object_num_pending_operations (MODEST_PROGRESS_OBJECT(tmp->data));
2558 is_empty = pending_ops == 0;
2560 tmp = g_slist_next(tmp);
2567 on_account_removed (TnyAccountStore *account_store,
2568 TnyAccount *account,
2571 /* Do nothing if it's a transport account, because we only
2572 show the messages of a store account */
2573 if (tny_account_get_account_type(account) == TNY_ACCOUNT_TYPE_STORE) {
2574 const gchar *parent_acc = NULL;
2575 const gchar *our_acc = NULL;
2577 our_acc = modest_window_get_active_account (MODEST_WINDOW (user_data));
2578 parent_acc = modest_tny_account_get_parent_modest_account_name_for_server_account (account);
2580 /* Close this window if I'm showing a message of the removed account */
2581 if (our_acc && parent_acc && strcmp (parent_acc, our_acc) == 0)
2582 modest_ui_actions_on_close_window (NULL, MODEST_WINDOW (user_data));
2587 on_mail_operation_started (ModestMailOperation *mail_op,
2590 ModestMsgViewWindow *self;
2591 ModestMailOperationTypeOperation op_type;
2593 ModestMsgViewWindowPrivate *priv;
2594 GObject *source = NULL;
2596 self = MODEST_MSG_VIEW_WINDOW (user_data);
2597 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
2598 op_type = modest_mail_operation_get_type_operation (mail_op);
2599 tmp = priv->progress_widgets;
2600 source = modest_mail_operation_get_source(mail_op);
2601 if (G_OBJECT (self) == source) {
2602 if (op_type == MODEST_MAIL_OPERATION_TYPE_RECEIVE ||
2603 op_type == MODEST_MAIL_OPERATION_TYPE_OPEN ||
2604 op_type == MODEST_MAIL_OPERATION_TYPE_DELETE) {
2605 set_progress_hint (self, TRUE);
2607 modest_progress_object_add_operation (
2608 MODEST_PROGRESS_OBJECT (tmp->data),
2610 tmp = g_slist_next (tmp);
2614 g_object_unref (source);
2616 /* Update dimming rules */
2617 check_dimming_rules_after_change (self);
2621 on_mail_operation_finished (ModestMailOperation *mail_op,
2624 ModestMsgViewWindow *self;
2625 ModestMailOperationTypeOperation op_type;
2627 ModestMsgViewWindowPrivate *priv;
2629 self = MODEST_MSG_VIEW_WINDOW (user_data);
2630 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
2631 op_type = modest_mail_operation_get_type_operation (mail_op);
2632 tmp = priv->progress_widgets;
2634 if (op_type == MODEST_MAIL_OPERATION_TYPE_RECEIVE ||
2635 op_type == MODEST_MAIL_OPERATION_TYPE_OPEN ||
2636 op_type == MODEST_MAIL_OPERATION_TYPE_DELETE) {
2638 modest_progress_object_remove_operation (MODEST_PROGRESS_OBJECT (tmp->data),
2640 tmp = g_slist_next (tmp);
2643 /* If no more operations are being observed, NORMAL mode is enabled again */
2644 if (observers_empty (self)) {
2645 set_progress_hint (self, FALSE);
2649 /* Update dimming rules. We have to do this right here
2650 and not in view_msg_cb because at that point the
2651 transfer mode is still enabled so the dimming rule
2652 won't let the user delete the message that has been
2653 readed for example */
2654 check_dimming_rules_after_change (self);
2658 on_queue_changed (ModestMailOperationQueue *queue,
2659 ModestMailOperation *mail_op,
2660 ModestMailOperationQueueNotification type,
2661 ModestMsgViewWindow *self)
2663 ModestMsgViewWindowPrivate *priv;
2665 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
2667 /* If this operations was created by another window, do nothing */
2668 if (!modest_mail_operation_is_mine (mail_op, G_OBJECT(self)))
2671 if (type == MODEST_MAIL_OPERATION_QUEUE_OPERATION_ADDED) {
2672 priv->sighandlers = modest_signal_mgr_connect (priv->sighandlers,
2674 "operation-started",
2675 G_CALLBACK (on_mail_operation_started),
2677 priv->sighandlers = modest_signal_mgr_connect (priv->sighandlers,
2679 "operation-finished",
2680 G_CALLBACK (on_mail_operation_finished),
2682 } else if (type == MODEST_MAIL_OPERATION_QUEUE_OPERATION_REMOVED) {
2683 priv->sighandlers = modest_signal_mgr_disconnect (priv->sighandlers,
2685 "operation-started");
2686 priv->sighandlers = modest_signal_mgr_disconnect (priv->sighandlers,
2688 "operation-finished");
2693 modest_msg_view_window_get_attachments (ModestMsgViewWindow *win)
2695 ModestMsgViewWindowPrivate *priv;
2696 TnyList *selected_attachments = NULL;
2698 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (win), NULL);
2699 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (win);
2701 /* In Hildon 2.2 as there's no selection we assume we have all attachments selected */
2702 selected_attachments = modest_msg_view_get_attachments (MODEST_MSG_VIEW (priv->msg_view));
2704 return selected_attachments;
2708 ModestMsgViewWindow *self;
2710 gchar *attachment_uid;
2711 } DecodeAsyncHelper;
2714 on_decode_to_stream_async_handler (TnyMimePart *mime_part,
2720 DecodeAsyncHelper *helper = (DecodeAsyncHelper *) user_data;
2721 const gchar *content_type;
2722 ModestMsgViewWindowPrivate *priv;
2724 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (helper->self);
2726 if (cancelled || err) {
2729 if ((err->domain == TNY_ERROR_DOMAIN) &&
2730 (err->code == TNY_IO_ERROR_WRITE) &&
2731 (errno == ENOSPC)) {
2732 msg = g_strdup_printf (_KR("cerm_device_memory_full"), "");
2734 msg = g_strdup (_("mail_ib_file_operation_failed"));
2736 modest_platform_information_banner (NULL, NULL, msg);
2742 /* It could happen that the window was closed. So we
2743 assume it is a cancelation */
2744 if (!GTK_WIDGET_VISIBLE (helper->self))
2747 /* Remove the progress hint */
2748 set_progress_hint (helper->self, FALSE);
2750 content_type = tny_mime_part_get_content_type (mime_part);
2751 if (content_type && g_str_has_prefix (content_type, "message/rfc822")) {
2752 ModestWindowMgr *mgr;
2753 ModestWindow *msg_win = NULL;
2756 const gchar *mailbox;
2757 TnyStream *file_stream;
2760 fd = g_open (helper->file_path, O_RDONLY, 0644);
2763 file_stream = tny_fs_stream_new (fd);
2765 mgr = modest_runtime_get_window_mgr ();
2767 account = g_strdup (modest_window_get_active_account (MODEST_WINDOW (helper->self)));
2768 mailbox = modest_window_get_active_mailbox (MODEST_WINDOW (helper->self));
2771 account = modest_account_mgr_get_default_account (modest_runtime_get_account_mgr ());
2773 msg = tny_camel_msg_new ();
2774 tny_camel_msg_parse (TNY_CAMEL_MSG (msg), file_stream);
2777 top_msg = g_object_ref (priv->top_msg);
2779 top_msg = tny_msg_view_get_msg (TNY_MSG_VIEW (priv->msg_view));
2781 msg_win = modest_msg_view_window_new_for_attachment (TNY_MSG (msg), top_msg,
2782 account, mailbox, helper->attachment_uid);
2783 if (top_msg) g_object_unref (top_msg);
2784 modest_window_set_zoom (MODEST_WINDOW (msg_win),
2785 modest_window_get_zoom (MODEST_WINDOW (helper->self)));
2786 if (modest_window_mgr_register_window (mgr, msg_win, MODEST_WINDOW (helper->self)))
2787 gtk_widget_show_all (GTK_WIDGET (msg_win));
2789 gtk_widget_destroy (GTK_WIDGET (msg_win));
2790 g_object_unref (msg);
2791 g_object_unref (file_stream);
2793 modest_platform_information_banner (NULL, NULL, _("mail_ib_file_operation_failed"));
2798 /* make the file read-only */
2799 g_chmod(helper->file_path, 0444);
2801 /* Activate the file */
2802 modest_platform_activate_file (helper->file_path, content_type);
2807 g_object_unref (helper->self);
2808 g_free (helper->file_path);
2809 g_free (helper->attachment_uid);
2810 g_slice_free (DecodeAsyncHelper, helper);
2814 view_attachment_connect_handler (gboolean canceled,
2816 GtkWindow *parent_window,
2817 TnyAccount *account,
2821 if (canceled || err) {
2822 g_object_unref (part);
2826 modest_msg_view_window_view_attachment (MODEST_MSG_VIEW_WINDOW (parent_window),
2828 g_object_unref (part);
2832 modest_msg_view_window_view_attachment (ModestMsgViewWindow *window,
2833 TnyMimePart *mime_part)
2835 ModestMsgViewWindowPrivate *priv;
2836 const gchar *msg_uid;
2837 gchar *attachment_uid = NULL;
2838 gint attachment_index = 0;
2839 TnyList *attachments;
2841 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window));
2842 g_return_if_fail (TNY_IS_MIME_PART (mime_part) || (mime_part == NULL));
2843 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
2845 msg_uid = modest_msg_view_window_get_message_uid (MODEST_MSG_VIEW_WINDOW (window));
2846 attachments = modest_msg_view_get_attachments (MODEST_MSG_VIEW (priv->msg_view));
2847 attachment_index = modest_list_index (attachments, (GObject *) mime_part);
2848 g_object_unref (attachments);
2850 if (msg_uid && attachment_index >= 0) {
2851 attachment_uid = g_strdup_printf ("%s/%d", msg_uid, attachment_index);
2854 if (mime_part == NULL) {
2855 gboolean error = FALSE;
2856 TnyList *selected_attachments = modest_msg_view_get_selected_attachments (MODEST_MSG_VIEW (priv->msg_view));
2857 if (selected_attachments == NULL || tny_list_get_length (selected_attachments) == 0) {
2859 } else if (tny_list_get_length (selected_attachments) > 1) {
2860 modest_platform_system_banner (NULL, NULL, _("mcen_ib_unable_to_display_more"));
2864 iter = tny_list_create_iterator (selected_attachments);
2865 mime_part = (TnyMimePart *) tny_iterator_get_current (iter);
2866 g_object_unref (iter);
2868 if (selected_attachments)
2869 g_object_unref (selected_attachments);
2874 g_object_ref (mime_part);
2877 if (tny_mime_part_is_purged (mime_part))
2880 if (TNY_IS_CAMEL_BS_MIME_PART (mime_part) &&
2881 !tny_camel_bs_mime_part_is_fetched (TNY_CAMEL_BS_MIME_PART (mime_part))) {
2883 TnyAccount *account;
2885 is_merge = g_str_has_prefix (priv->msg_uid, "merge:");
2887 /* Get the account */
2889 account = tny_account_store_find_account (TNY_ACCOUNT_STORE (modest_runtime_get_account_store ()),
2892 if (!tny_device_is_online (modest_runtime_get_device())) {
2893 modest_platform_connect_and_perform ((ModestWindow *) window,
2895 TNY_ACCOUNT (account),
2896 (ModestConnectedPerformer) view_attachment_connect_handler,
2897 g_object_ref (mime_part));
2902 if (!modest_tny_mime_part_is_msg (mime_part) && tny_mime_part_get_filename (mime_part)) {
2903 gchar *filepath = NULL;
2904 const gchar *att_filename = tny_mime_part_get_filename (mime_part);
2905 gboolean show_error_banner = FALSE;
2906 TnyFsStream *temp_stream = NULL;
2907 temp_stream = modest_utils_create_temp_stream (att_filename, attachment_uid,
2910 if (temp_stream != NULL) {
2911 ModestAccountMgr *mgr;
2912 DecodeAsyncHelper *helper;
2913 gboolean decode_in_provider;
2914 ModestProtocol *protocol;
2915 const gchar *account;
2917 /* Activate progress hint */
2918 set_progress_hint (window, TRUE);
2920 helper = g_slice_new0 (DecodeAsyncHelper);
2921 helper->self = g_object_ref (window);
2922 helper->file_path = g_strdup (filepath);
2923 helper->attachment_uid = g_strdup (attachment_uid);
2925 decode_in_provider = FALSE;
2926 mgr = modest_runtime_get_account_mgr ();
2927 account = modest_window_get_active_account (MODEST_WINDOW (window));
2928 if (modest_account_mgr_account_is_multimailbox (mgr, account, &protocol)) {
2929 if (MODEST_IS_ACCOUNT_PROTOCOL (protocol)) {
2931 uri = g_strconcat ("file://", filepath, NULL);
2932 decode_in_provider =
2933 modest_account_protocol_decode_part_to_stream_async (
2934 MODEST_ACCOUNT_PROTOCOL (protocol),
2937 TNY_STREAM (temp_stream),
2938 on_decode_to_stream_async_handler,
2945 if (!decode_in_provider)
2946 tny_mime_part_decode_to_stream_async (mime_part, TNY_STREAM (temp_stream),
2947 on_decode_to_stream_async_handler,
2950 g_object_unref (temp_stream);
2951 /* NOTE: files in the temporary area will be automatically
2952 * cleaned after some time if they are no longer in use */
2955 const gchar *content_type;
2956 /* the file may already exist but it isn't writable,
2957 * let's try to open it anyway */
2958 content_type = tny_mime_part_get_content_type (mime_part);
2959 modest_platform_activate_file (filepath, content_type);
2961 g_warning ("%s: modest_utils_create_temp_stream failed", __FUNCTION__);
2962 show_error_banner = TRUE;
2967 if (show_error_banner)
2968 modest_platform_information_banner (NULL, NULL, _("mail_ib_file_operation_failed"));
2969 } else if (!modest_tny_mime_part_is_msg (mime_part)) {
2970 ModestWindowMgr *mgr;
2971 ModestWindow *msg_win = NULL;
2972 TnyMsg *current_msg;
2976 current_msg = modest_msg_view_window_get_message (MODEST_MSG_VIEW_WINDOW (window));
2977 mgr = modest_runtime_get_window_mgr ();
2978 header = tny_msg_get_header (TNY_MSG (current_msg));
2979 found = modest_window_mgr_find_registered_message_uid (mgr,
2984 g_debug ("window for this body is already being created");
2988 /* it's not found, so create a new window for it */
2989 modest_window_mgr_register_header (mgr, header, attachment_uid); /* register the uid before building the window */
2990 gchar *account = g_strdup (modest_window_get_active_account (MODEST_WINDOW (window)));
2991 const gchar *mailbox = modest_window_get_active_mailbox (MODEST_WINDOW (window));
2993 account = modest_account_mgr_get_default_account (modest_runtime_get_account_mgr ());
2996 top_msg = g_object_ref (priv->top_msg);
2998 top_msg = tny_msg_view_get_msg (TNY_MSG_VIEW (priv->msg_view));
3000 msg_win = modest_msg_view_window_new_with_other_body (TNY_MSG (current_msg), TNY_MIME_PART (mime_part), top_msg,
3001 account, mailbox, attachment_uid);
3003 if (top_msg) g_object_unref (top_msg);
3005 modest_window_set_zoom (MODEST_WINDOW (msg_win),
3006 modest_window_get_zoom (MODEST_WINDOW (window)));
3007 if (modest_window_mgr_register_window (mgr, msg_win, MODEST_WINDOW (window)))
3008 gtk_widget_show_all (GTK_WIDGET (msg_win));
3010 gtk_widget_destroy (GTK_WIDGET (msg_win));
3012 g_object_unref (current_msg);
3014 /* message attachment */
3015 TnyHeader *header = NULL;
3016 ModestWindowMgr *mgr;
3017 ModestWindow *msg_win = NULL;
3020 header = tny_msg_get_header (TNY_MSG (mime_part));
3021 mgr = modest_runtime_get_window_mgr ();
3022 found = modest_window_mgr_find_registered_header (mgr, header, &msg_win);
3025 /* if it's found, but there is no msg_win, it's probably in the process of being created;
3026 * thus, we don't do anything */
3027 g_debug ("window for is already being created");
3030 /* it's not found, so create a new window for it */
3031 modest_window_mgr_register_header (mgr, header, attachment_uid); /* register the uid before building the window */
3032 gchar *account = g_strdup (modest_window_get_active_account (MODEST_WINDOW (window)));
3033 const gchar *mailbox = modest_window_get_active_mailbox (MODEST_WINDOW (window));
3035 account = modest_account_mgr_get_default_account (modest_runtime_get_account_mgr ());
3037 top_msg = g_object_ref (priv->top_msg);
3039 top_msg = tny_msg_view_get_msg (TNY_MSG_VIEW (priv->msg_view));
3040 msg_win = modest_msg_view_window_new_for_attachment (
3041 TNY_MSG (mime_part), top_msg, account,
3042 mailbox, attachment_uid);
3043 modest_window_set_zoom (MODEST_WINDOW (msg_win),
3044 modest_window_get_zoom (MODEST_WINDOW (window)));
3045 if (modest_window_mgr_register_window (mgr, msg_win, MODEST_WINDOW (window)))
3046 gtk_widget_show_all (GTK_WIDGET (msg_win));
3048 gtk_widget_destroy (GTK_WIDGET (msg_win));
3054 g_free (attachment_uid);
3056 g_object_unref (mime_part);
3068 GnomeVFSResult result;
3070 ModestMsgViewWindow *window;
3073 static void save_mime_part_info_free (SaveMimePartInfo *info, gboolean with_struct);
3074 static gboolean idle_save_mime_part_show_result (SaveMimePartInfo *info);
3075 static gpointer save_mime_part_to_file (SaveMimePartInfo *info);
3076 static void save_mime_parts_to_file_with_checks (GtkWindow *parent, SaveMimePartInfo *info);
3079 save_mime_part_info_free (SaveMimePartInfo *info, gboolean with_struct)
3083 for (node = info->pairs; node != NULL; node = g_list_next (node)) {
3084 SaveMimePartPair *pair = (SaveMimePartPair *) node->data;
3085 g_free (pair->filename);
3086 g_object_unref (pair->part);
3087 g_slice_free (SaveMimePartPair, pair);
3089 g_list_free (info->pairs);
3092 g_object_unref (info->window);
3093 info->window = NULL;
3095 g_slice_free (SaveMimePartInfo, info);
3100 idle_save_mime_part_show_result (SaveMimePartInfo *info)
3102 /* This is a GDK lock because we are an idle callback and
3103 * modest_platform_system_banner is or does Gtk+ code */
3105 gdk_threads_enter (); /* CHECKED */
3106 if (info->result == GNOME_VFS_ERROR_CANCELLED) {
3108 } else if (info->result == GNOME_VFS_OK) {
3109 modest_platform_system_banner (NULL, NULL, _CS_SAVED);
3110 } else if (info->result == GNOME_VFS_ERROR_NO_SPACE) {
3113 /* Check if the uri belongs to the external mmc */
3114 if (g_str_has_prefix (info->uri, g_getenv (MODEST_MMC1_VOLUMEPATH_ENV)))
3115 msg = g_strdup_printf (_KR("cerm_device_memory_full"), "");
3117 msg = g_strdup (_KR("cerm_memory_card_full"));
3118 modest_platform_information_banner (NULL, NULL, msg);
3121 modest_platform_system_banner (NULL, NULL, _("mail_ib_file_operation_failed"));
3123 save_mime_part_info_free (info, FALSE);
3124 gdk_threads_leave (); /* CHECKED */
3130 save_mime_part_to_file_connect_handler (gboolean canceled,
3132 GtkWindow *parent_window,
3133 TnyAccount *account,
3134 SaveMimePartInfo *info)
3136 if (canceled || err) {
3137 if (canceled && !err) {
3138 info->result = GNOME_VFS_ERROR_CANCELLED;
3140 g_idle_add ((GSourceFunc) idle_save_mime_part_show_result, info);
3142 g_thread_create ((GThreadFunc)save_mime_part_to_file, info, FALSE, NULL);
3147 save_mime_part_to_file_connect_idle (SaveMimePartInfo *info)
3150 TnyAccount *account;
3151 ModestMsgViewWindowPrivate *priv;
3153 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (info->window);
3155 is_merge = g_str_has_prefix (priv->msg_uid, "merge:");
3158 /* Get the account */
3160 account = tny_account_store_find_account (TNY_ACCOUNT_STORE (modest_runtime_get_account_store ()),
3163 modest_platform_connect_and_perform ((ModestWindow *) info->window,
3165 TNY_ACCOUNT (account),
3166 (ModestConnectedPerformer) save_mime_part_to_file_connect_handler,
3170 g_object_unref (account);
3176 save_mime_part_to_file (SaveMimePartInfo *info)
3178 GnomeVFSHandle *handle;
3180 SaveMimePartPair *pair = (SaveMimePartPair *) info->pairs->data;
3182 if (TNY_IS_CAMEL_BS_MIME_PART (pair->part) &&
3183 !tny_camel_bs_mime_part_is_fetched (TNY_CAMEL_BS_MIME_PART (pair->part))) {
3184 gboolean check_online = TRUE;
3185 ModestMsgViewWindowPrivate *priv = NULL;
3187 /* Check if we really need to connect to save the mime part */
3188 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (info->window);
3189 if (g_str_has_prefix (priv->msg_uid, "merge:")) {
3190 check_online = FALSE;
3192 TnyAccountStore *acc_store;
3193 TnyAccount *account = NULL;
3195 acc_store = (TnyAccountStore*) modest_runtime_get_account_store ();
3196 account = tny_account_store_find_account (acc_store, priv->msg_uid);
3199 if (tny_account_get_connection_status (account) ==
3200 TNY_CONNECTION_STATUS_CONNECTED)
3201 check_online = FALSE;
3202 g_object_unref (account);
3204 check_online = !tny_device_is_online (tny_account_store_get_device (acc_store));
3209 g_idle_add ((GSourceFunc) save_mime_part_to_file_connect_idle, info);
3214 info->result = gnome_vfs_create (&handle, pair->filename, GNOME_VFS_OPEN_WRITE, FALSE, 0644);
3215 if (info->result == GNOME_VFS_OK) {
3216 GError *error = NULL;
3217 gboolean decode_in_provider;
3219 ModestAccountMgr *mgr;
3220 const gchar *account;
3221 ModestProtocol *protocol = NULL;
3223 stream = tny_vfs_stream_new (handle);
3225 decode_in_provider = FALSE;
3226 mgr = modest_runtime_get_account_mgr ();
3227 account = modest_window_get_active_account (MODEST_WINDOW (info->window));
3228 if (modest_account_mgr_account_is_multimailbox (mgr, account, &protocol)) {
3229 if (MODEST_IS_ACCOUNT_PROTOCOL (protocol)) {
3230 decode_in_provider =
3231 modest_account_protocol_decode_part_to_stream (
3232 MODEST_ACCOUNT_PROTOCOL (protocol),
3240 if (!decode_in_provider)
3241 written = tny_mime_part_decode_to_stream (pair->part, stream, &error);
3244 g_warning ("modest: could not save attachment %s: %d (%s)\n", pair->filename, error?error->code:-1, error?error->message:"Unknown error");
3246 if ((error->domain == TNY_ERROR_DOMAIN) &&
3247 (error->code == TNY_IO_ERROR_WRITE) &&
3248 (errno == ENOSPC)) {
3249 info->result = GNOME_VFS_ERROR_NO_SPACE;
3251 info->result = GNOME_VFS_ERROR_IO;
3254 g_object_unref (G_OBJECT (stream));
3256 g_warning ("Could not create save attachment %s: %s\n",
3257 pair->filename, gnome_vfs_result_to_string (info->result));
3260 /* Go on saving remaining files */
3261 info->pairs = g_list_remove_link (info->pairs, info->pairs);
3262 if (info->pairs != NULL) {
3263 save_mime_part_to_file (info);
3265 g_idle_add ((GSourceFunc) idle_save_mime_part_show_result, info);
3272 save_mime_parts_to_file_with_checks (GtkWindow *parent,
3273 SaveMimePartInfo *info)
3275 gboolean is_ok = TRUE;
3276 gint replaced_files = 0;
3277 const GList *files = info->pairs;
3278 const GList *iter, *to_replace = NULL;
3280 for (iter = files; (iter != NULL) && (replaced_files < 2); iter = g_list_next(iter)) {
3281 SaveMimePartPair *pair = iter->data;
3282 gchar *unescaped = g_uri_unescape_string (pair->filename, NULL);
3284 if (modest_utils_file_exists (unescaped)) {
3286 if (replaced_files == 1)
3291 if (replaced_files) {
3294 if (replaced_files == 1) {
3295 SaveMimePartPair *pair = to_replace->data;
3296 const gchar *basename = strrchr (pair->filename, G_DIR_SEPARATOR) + 1;
3297 gchar *escaped_basename, *message;
3299 escaped_basename = g_uri_unescape_string (basename, NULL);
3300 message = g_strdup_printf ("%s\n%s",
3302 (escaped_basename) ? escaped_basename : "");
3303 response = modest_platform_run_confirmation_dialog (parent, message);
3305 g_free (escaped_basename);
3307 response = modest_platform_run_confirmation_dialog (parent,
3308 _FM_REPLACE_MULTIPLE);
3310 if (response != GTK_RESPONSE_OK)
3315 save_mime_part_info_free (info, TRUE);
3317 g_thread_create ((GThreadFunc)save_mime_part_to_file, info, FALSE, NULL);
3322 typedef struct _SaveAttachmentsInfo {
3323 TnyList *attachments_list;
3324 ModestMsgViewWindow *window;
3325 } SaveAttachmentsInfo;
3328 save_attachments_response (GtkDialog *dialog,
3332 TnyList *mime_parts;
3334 GList *files_to_save = NULL;
3335 gchar *current_folder;
3336 SaveAttachmentsInfo *sa_info = (SaveAttachmentsInfo *) user_data;
3338 mime_parts = TNY_LIST (sa_info->attachments_list);
3340 if (arg1 != GTK_RESPONSE_OK)
3343 chooser_uri = gtk_file_chooser_get_uri (GTK_FILE_CHOOSER (dialog));
3344 current_folder = gtk_file_chooser_get_current_folder_uri (GTK_FILE_CHOOSER (dialog));
3345 if (current_folder && *current_folder != '\0') {
3347 modest_conf_set_string (modest_runtime_get_conf (), MODEST_CONF_LATEST_SAVE_ATTACHMENT_PATH,
3348 current_folder,&err);
3350 g_debug ("Error storing latest used folder: %s", err->message);
3354 g_free (current_folder);
3356 if (!modest_utils_folder_writable (chooser_uri)) {
3357 const gchar *err_msg;
3359 #ifdef MODEST_PLATFORM_MAEMO
3360 if (modest_maemo_utils_in_usb_mode ()) {
3361 err_msg = dgettext ("hildon-status-bar-usb", "usbh_ib_mmc_usb_connected");
3363 err_msg = _FM_READ_ONLY_LOCATION;
3366 err_msg = _FM_READ_ONLY_LOCATION;
3368 modest_platform_system_banner (NULL, NULL, err_msg);
3372 iter = tny_list_create_iterator (mime_parts);
3373 while (!tny_iterator_is_done (iter)) {
3374 TnyMimePart *mime_part = (TnyMimePart *) tny_iterator_get_current (iter);
3376 if ((modest_tny_mime_part_is_attachment_for_modest (mime_part)) &&
3377 !tny_mime_part_is_purged (mime_part) &&
3378 (tny_mime_part_get_filename (mime_part) != NULL)) {
3379 SaveMimePartPair *pair;
3381 pair = g_slice_new0 (SaveMimePartPair);
3383 if (tny_list_get_length (mime_parts) > 1) {
3385 gnome_vfs_escape_slashes (tny_mime_part_get_filename (mime_part));
3386 pair->filename = g_build_filename (chooser_uri, escaped, NULL);
3389 pair->filename = g_strdup (chooser_uri);
3391 pair->part = mime_part;
3392 files_to_save = g_list_prepend (files_to_save, pair);
3394 tny_iterator_next (iter);
3396 g_object_unref (iter);
3399 if (files_to_save != NULL) {
3400 SaveMimePartInfo *info = g_slice_new0 (SaveMimePartInfo);
3401 info->pairs = files_to_save;
3402 info->result = TRUE;
3403 info->uri = g_strdup (chooser_uri);
3404 info->window = g_object_ref (sa_info->window);
3405 save_mime_parts_to_file_with_checks ((GtkWindow *) dialog, info);
3407 g_free (chooser_uri);
3410 /* Free and close the dialog */
3411 g_object_unref (mime_parts);
3412 g_object_unref (sa_info->window);
3413 g_slice_free (SaveAttachmentsInfo, sa_info);
3414 gtk_widget_destroy (GTK_WIDGET (dialog));
3418 msg_is_attachment (TnyList *mime_parts)
3421 gboolean retval = FALSE;
3423 if (tny_list_get_length (mime_parts) > 1)
3426 iter = tny_list_create_iterator (mime_parts);
3428 TnyMimePart *part = TNY_MIME_PART (tny_iterator_get_current (iter));
3430 if (TNY_IS_MSG (part))
3432 g_object_unref (part);
3434 g_object_unref (iter);
3440 modest_msg_view_window_save_attachments (ModestMsgViewWindow *window,
3441 TnyList *mime_parts)
3443 ModestMsgViewWindowPrivate *priv;
3444 GtkWidget *save_dialog = NULL;
3445 gchar *conf_folder = NULL;
3446 gchar *filename = NULL;
3447 gchar *save_multiple_str = NULL;
3448 const gchar *root_folder = "file:///";
3450 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window));
3451 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
3453 if (mime_parts == NULL) {
3454 gboolean allow_msgs = FALSE;
3456 /* In Hildon 2.2 save and delete operate over all the attachments as there's no
3457 * selection available */
3458 mime_parts = modest_msg_view_get_attachments (MODEST_MSG_VIEW (priv->msg_view));
3460 /* Check if the message is composed by an unique MIME
3461 part whose content disposition is attachment. There
3462 could be messages like this:
3464 Date: Tue, 12 Jan 2010 20:40:59 +0000
3465 From: <sender@example.org>
3466 To: <recipient@example.org>
3468 Content-Type: image/jpeg
3469 Content-Disposition: attachment; filename="bug7718.jpeg"
3471 whose unique MIME part is the message itself whose
3472 content disposition is attachment
3474 if (mime_parts && msg_is_attachment (mime_parts))
3477 if (mime_parts && !modest_toolkit_utils_select_attachments (GTK_WINDOW (gtk_widget_get_toplevel (GTK_WIDGET (window))), mime_parts, allow_msgs)) {
3478 g_object_unref (mime_parts);
3482 if (mime_parts == NULL || tny_list_get_length (mime_parts) == 0) {
3484 g_object_unref (mime_parts);
3490 g_object_ref (mime_parts);
3493 /* prepare dialog */
3494 if (tny_list_get_length (mime_parts) == 1) {
3496 /* only one attachment selected */
3497 iter = tny_list_create_iterator (mime_parts);
3498 TnyMimePart *mime_part = (TnyMimePart *) tny_iterator_get_current (iter);
3499 g_object_unref (iter);
3500 if (!modest_tny_mime_part_is_msg (mime_part) &&
3501 modest_tny_mime_part_is_attachment_for_modest (mime_part) &&
3502 !tny_mime_part_is_purged (mime_part)) {
3503 filename = g_strdup (tny_mime_part_get_filename (mime_part));
3505 /* TODO: show any error? */
3506 g_warning ("%s: Tried to save a non-file attachment", __FUNCTION__);
3507 g_object_unref (mime_parts);
3510 g_object_unref (mime_part);
3512 gint num = tny_list_get_length (mime_parts);
3513 save_multiple_str = g_strdup_printf (dngettext("hildon-fm",
3514 "sfil_va_number_of_objects_attachment",
3515 "sfil_va_number_of_objects_attachments",
3519 /* Creation of hildon file chooser dialog for saving */
3520 save_dialog = modest_toolkit_factory_create_file_chooser_dialog (modest_runtime_get_toolkit_factory (),
3522 (GtkWindow *) window,
3523 GTK_FILE_CHOOSER_ACTION_SAVE);
3525 /* Get last used folder */
3526 conf_folder = modest_conf_get_string (modest_runtime_get_conf (),
3527 MODEST_CONF_LATEST_SAVE_ATTACHMENT_PATH, NULL);
3529 /* File chooser stops working if we select "file:///" as current folder */
3530 if (conf_folder && g_ascii_strcasecmp (root_folder, conf_folder) != 0) {
3531 g_free (conf_folder);
3535 if (conf_folder && conf_folder[0] != '\0') {
3536 gtk_file_chooser_set_current_folder_uri (GTK_FILE_CHOOSER (save_dialog), conf_folder);
3539 /* Set the default folder to documents folder */
3540 docs_folder = (gchar *) g_strdup(g_get_user_special_dir (G_USER_DIRECTORY_DOCUMENTS));
3543 docs_folder = g_build_filename (g_getenv (MYDOCS_ENV), DOCS_FOLDER, NULL);
3545 gtk_file_chooser_set_current_folder (GTK_FILE_CHOOSER (save_dialog), docs_folder);
3546 g_free (docs_folder);
3548 g_free (conf_folder);
3552 gtk_file_chooser_set_current_name (GTK_FILE_CHOOSER (save_dialog),
3557 /* if multiple, set multiple string */
3558 if (save_multiple_str) {
3559 g_object_set (G_OBJECT (save_dialog), "save-multiple", save_multiple_str, NULL);
3560 gtk_window_set_title (GTK_WINDOW (save_dialog), _FM_SAVE_OBJECT_FILES);
3561 g_free (save_multiple_str);
3564 /* We must run this asynchronously, because the hildon dialog
3565 performs a gtk_dialog_run by itself which leads to gdk
3567 SaveAttachmentsInfo *sa_info;
3568 sa_info = g_slice_new (SaveAttachmentsInfo);
3569 sa_info->attachments_list = mime_parts;
3570 sa_info->window = g_object_ref (window);
3571 g_signal_connect (save_dialog, "response",
3572 G_CALLBACK (save_attachments_response), sa_info);
3574 gtk_widget_show_all (save_dialog);
3578 show_remove_attachment_information (gpointer userdata)
3580 ModestMsgViewWindow *window = (ModestMsgViewWindow *) userdata;
3581 ModestMsgViewWindowPrivate *priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
3583 /* We're outside the main lock */
3584 gdk_threads_enter ();
3586 if (priv->remove_attachment_banner != NULL) {
3587 gtk_widget_destroy (priv->remove_attachment_banner);
3588 g_object_unref (priv->remove_attachment_banner);
3591 priv->remove_attachment_banner = g_object_ref (
3592 modest_platform_animation_banner (NULL, NULL, _("mcen_me_inbox_remove_attachments")));
3594 gdk_threads_leave ();
3600 modest_msg_view_window_remove_attachments (ModestMsgViewWindow *window, gboolean get_all)
3602 ModestMsgViewWindowPrivate *priv;
3603 TnyList *mime_parts = NULL, *tmp;
3604 gchar *confirmation_message;
3610 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window));
3611 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
3613 #ifdef MODEST_TOOLKIT_HILDON2
3614 /* In hildon 2.2 we ignore the get_all flag as we always get all attachments. This is
3615 * because we don't have selection
3617 mime_parts = modest_msg_view_get_attachments (MODEST_MSG_VIEW (priv->msg_view));
3619 /* Remove already purged messages from mime parts list. We use
3620 a copy of the list to remove items in the original one */
3621 tmp = tny_list_copy (mime_parts);
3622 iter = tny_list_create_iterator (tmp);
3623 while (!tny_iterator_is_done (iter)) {
3624 TnyMimePart *part = TNY_MIME_PART (tny_iterator_get_current (iter));
3625 if (tny_mime_part_is_purged (part))
3626 tny_list_remove (mime_parts, (GObject *) part);
3628 g_object_unref (part);
3629 tny_iterator_next (iter);
3631 g_object_unref (tmp);
3632 g_object_unref (iter);
3634 if (!modest_toolkit_utils_select_attachments (GTK_WINDOW (window), mime_parts, TRUE) ||
3635 tny_list_get_length (mime_parts) == 0) {
3636 g_object_unref (mime_parts);
3640 /* In gtk we get only selected attachments for the operation.
3642 mime_parts = modest_msg_view_get_selected_attachments (MODEST_MSG_VIEW (priv->msg_view));
3644 /* Remove already purged messages from mime parts list. We use
3645 a copy of the list to remove items in the original one */
3646 tmp = tny_list_copy (mime_parts);
3647 iter = tny_list_create_iterator (tmp);
3648 while (!tny_iterator_is_done (iter)) {
3649 TnyMimePart *part = TNY_MIME_PART (tny_iterator_get_current (iter));
3650 if (tny_mime_part_is_purged (part))
3651 tny_list_remove (mime_parts, (GObject *) part);
3653 g_object_unref (part);
3654 tny_iterator_next (iter);
3656 g_object_unref (tmp);
3657 g_object_unref (iter);
3659 if (tny_list_get_length (mime_parts) == 0) {
3660 g_object_unref (mime_parts);
3665 n_attachments = tny_list_get_length (mime_parts);
3666 if (n_attachments == 1) {
3670 iter = tny_list_create_iterator (mime_parts);
3671 part = (TnyMimePart *) tny_iterator_get_current (iter);
3672 g_object_unref (iter);
3673 if (modest_tny_mime_part_is_msg (part)) {
3675 header = tny_msg_get_header (TNY_MSG (part));
3676 filename = tny_header_dup_subject (header);
3677 g_object_unref (header);
3678 if (filename == NULL)
3679 filename = g_strdup (_("mail_va_no_subject"));
3681 filename = g_strdup (tny_mime_part_get_filename (TNY_MIME_PART (part)));
3683 confirmation_message = g_strdup_printf (_("mcen_nc_purge_file_text"), filename);
3685 g_object_unref (part);
3687 confirmation_message = g_strdup_printf (ngettext("mcen_nc_purge_file_text",
3688 "mcen_nc_purge_files_text",
3689 n_attachments), n_attachments);
3691 response = modest_platform_run_confirmation_dialog (GTK_WINDOW (gtk_widget_get_toplevel (GTK_WIDGET (window))),
3692 confirmation_message);
3693 g_free (confirmation_message);
3695 if (response != GTK_RESPONSE_OK) {
3696 g_object_unref (mime_parts);
3700 priv->purge_timeout = g_timeout_add (2000, show_remove_attachment_information, window);
3702 iter = tny_list_create_iterator (mime_parts);
3703 while (!tny_iterator_is_done (iter)) {
3706 part = (TnyMimePart *) tny_iterator_get_current (iter);
3707 tny_mime_part_set_purged (TNY_MIME_PART (part));
3708 g_object_unref (part);
3709 tny_iterator_next (iter);
3711 g_object_unref (iter);
3713 msg = tny_msg_view_get_msg (TNY_MSG_VIEW (priv->msg_view));
3714 tny_msg_view_clear (TNY_MSG_VIEW (priv->msg_view));
3715 tny_msg_rewrite_cache (msg);
3716 tny_msg_view_set_msg (TNY_MSG_VIEW (priv->msg_view), msg);
3717 g_object_unref (msg);
3718 update_branding (MODEST_MSG_VIEW_WINDOW (window));
3720 g_object_unref (mime_parts);
3722 if (priv->purge_timeout > 0) {
3723 g_source_remove (priv->purge_timeout);
3724 priv->purge_timeout = 0;
3727 if (priv->remove_attachment_banner) {
3728 gtk_widget_destroy (priv->remove_attachment_banner);
3729 g_object_unref (priv->remove_attachment_banner);
3730 priv->remove_attachment_banner = NULL;
3736 update_window_title (ModestMsgViewWindow *window)
3738 ModestMsgViewWindowPrivate *priv;
3740 TnyHeader *header = NULL;
3741 gchar *subject = NULL;
3743 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
3745 /* Note that if the window is closed while we're retrieving
3746 the message, this widget could de deleted */
3747 if (!priv->msg_view)
3750 msg = tny_msg_view_get_msg (TNY_MSG_VIEW (priv->msg_view));
3752 if (priv->other_body) {
3755 description = modest_tny_mime_part_get_header_value (priv->other_body, "Content-Description");
3757 g_strstrip (description);
3758 subject = description;
3760 } else if (msg != NULL) {
3761 header = tny_msg_get_header (msg);
3762 subject = tny_header_dup_subject (header);
3763 g_object_unref (header);
3764 g_object_unref (msg);
3767 if ((subject == NULL)||(subject[0] == '\0')) {
3769 subject = g_strdup (_("mail_va_no_subject"));
3772 modest_window_set_title (MODEST_WINDOW (window), subject);
3777 on_move_focus (GtkWidget *widget,
3778 GtkDirectionType direction,
3781 g_signal_stop_emission_by_name (G_OBJECT (widget), "move-focus");
3785 fetch_image_open_stream (TnyStreamCache *self, gint64 *expected_size, gchar *uri)
3787 GnomeVFSResult result;
3788 GnomeVFSHandle *handle = NULL;
3789 GnomeVFSFileInfo *info = NULL;
3792 result = gnome_vfs_open (&handle, uri, GNOME_VFS_OPEN_READ);
3793 if (result != GNOME_VFS_OK) {
3798 info = gnome_vfs_file_info_new ();
3799 result = gnome_vfs_get_file_info_from_handle (handle, info, GNOME_VFS_FILE_INFO_DEFAULT);
3800 if (result != GNOME_VFS_OK || ! (info->valid_fields & GNOME_VFS_FILE_INFO_FIELDS_SIZE)) {
3801 /* We put a "safe" default size for going to cache */
3802 *expected_size = (300*1024);
3804 *expected_size = info->size;
3806 gnome_vfs_file_info_unref (info);
3808 stream = tny_vfs_stream_new (handle);
3817 TnyStream *output_stream;
3818 GtkWidget *msg_view;
3823 on_fetch_image_timeout_refresh_view (gpointer userdata)
3825 ModestMsgViewWindowPrivate *priv;
3827 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (userdata);
3828 update_progress_hint (MODEST_MSG_VIEW_WINDOW (userdata));
3829 /* Note that priv->msg_view is set to NULL when this window is
3831 if (priv->msg_view && GTK_WIDGET_DRAWABLE (priv->msg_view)) {
3832 gtk_widget_queue_draw (GTK_WIDGET (priv->msg_view));
3834 priv->fetch_image_redraw_handler = 0;
3835 g_object_unref (userdata);
3840 on_fetch_image_idle_refresh_view (gpointer userdata)
3843 FetchImageData *fidata = (FetchImageData *) userdata;
3845 gdk_threads_enter ();
3846 if (GTK_WIDGET_DRAWABLE (fidata->msg_view)) {
3847 ModestMsgViewWindowPrivate *priv;
3849 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (fidata->window);
3850 priv->fetching_images--;
3851 if (priv->fetch_image_redraw_handler == 0) {
3852 priv->fetch_image_redraw_handler = g_timeout_add (500, on_fetch_image_timeout_refresh_view, g_object_ref (fidata->window));
3856 gdk_threads_leave ();
3858 g_object_unref (fidata->msg_view);
3859 g_object_unref (fidata->window);
3860 g_slice_free (FetchImageData, fidata);
3865 on_fetch_image_thread (gpointer userdata)
3867 FetchImageData *fidata = (FetchImageData *) userdata;
3868 TnyStreamCache *cache;
3869 TnyStream *cache_stream;
3871 cache = modest_runtime_get_images_cache ();
3873 tny_stream_cache_get_stream (cache,
3875 (TnyStreamCacheOpenStreamFetcher) fetch_image_open_stream,
3876 (gpointer) fidata->uri);
3877 g_free (fidata->cache_id);
3878 g_free (fidata->uri);
3880 if (cache_stream != NULL) {
3883 while (G_LIKELY (!tny_stream_is_eos (cache_stream))) {
3886 nb_read = tny_stream_read (cache_stream, buffer, sizeof (buffer));
3887 if (G_UNLIKELY (nb_read < 0)) {
3889 } else if (G_LIKELY (nb_read > 0)) {
3890 gssize nb_written = 0;
3892 while (G_UNLIKELY (nb_written < nb_read)) {
3895 len = tny_stream_write (fidata->output_stream, buffer + nb_written,
3896 nb_read - nb_written);
3897 if (G_UNLIKELY (len < 0))
3903 tny_stream_close (cache_stream);
3904 g_object_unref (cache_stream);
3907 tny_stream_close (fidata->output_stream);
3908 g_object_unref (fidata->output_stream);
3910 g_idle_add (on_fetch_image_idle_refresh_view, fidata);
3916 on_fetch_image (ModestMsgView *msgview,
3919 ModestMsgViewWindow *window)
3921 const gchar *current_account;
3922 ModestMsgViewWindowPrivate *priv;
3923 FetchImageData *fidata;
3925 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
3927 current_account = modest_window_get_active_account (MODEST_WINDOW (window));
3929 fidata = g_slice_new0 (FetchImageData);
3930 fidata->msg_view = g_object_ref (msgview);
3931 fidata->window = g_object_ref (window);
3932 fidata->uri = g_strdup (uri);
3933 fidata->cache_id = modest_images_cache_get_id (current_account, uri);
3934 fidata->output_stream = g_object_ref (stream);
3936 priv->fetching_images++;
3937 if (g_thread_create (on_fetch_image_thread, fidata, FALSE, NULL) == NULL) {
3938 g_object_unref (fidata->output_stream);
3939 g_free (fidata->cache_id);
3940 g_free (fidata->uri);
3941 g_object_unref (fidata->msg_view);
3942 g_slice_free (FetchImageData, fidata);
3943 tny_stream_close (stream);
3944 priv->fetching_images--;
3945 update_progress_hint (window);
3948 update_progress_hint (window);
3954 setup_menu (ModestMsgViewWindow *self)
3956 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW(self));
3958 /* Settings menu buttons */
3959 modest_window_add_to_menu (MODEST_WINDOW (self), _("mcen_me_viewer_find"), NULL,
3960 MODEST_WINDOW_MENU_CALLBACK (modest_msg_view_window_show_isearch_toolbar),
3961 MODEST_DIMMING_CALLBACK (modest_ui_dimming_rules_on_find_in_msg));
3963 modest_window_add_to_menu (MODEST_WINDOW (self),
3964 dngettext(GETTEXT_PACKAGE,
3965 "mcen_me_move_message",
3966 "mcen_me_move_messages",
3969 MODEST_WINDOW_MENU_CALLBACK (modest_ui_actions_on_move_to),
3970 MODEST_DIMMING_CALLBACK (modest_ui_dimming_rules_on_move_to));
3972 modest_window_add_to_menu (MODEST_WINDOW (self), _("mcen_me_inbox_mark_as_read"), NULL,
3973 MODEST_WINDOW_MENU_CALLBACK (modest_ui_actions_on_mark_as_read),
3974 MODEST_DIMMING_CALLBACK (modest_ui_dimming_rules_on_mark_as_read_msg_in_view));
3976 modest_window_add_to_menu (MODEST_WINDOW (self), _("mcen_me_inbox_mark_as_unread"), NULL,
3977 MODEST_WINDOW_MENU_CALLBACK (modest_ui_actions_on_mark_as_unread),
3978 MODEST_DIMMING_CALLBACK (modest_ui_dimming_rules_on_mark_as_unread_msg_in_view));
3980 modest_window_add_to_menu (MODEST_WINDOW (self), _("mcen_me_viewer_save_attachments"), NULL,
3981 MODEST_WINDOW_MENU_CALLBACK (modest_ui_actions_save_attachments),
3982 MODEST_DIMMING_CALLBACK (modest_ui_dimming_rules_on_save_attachments));
3983 modest_window_add_to_menu (MODEST_WINDOW (self), _("mcen_me_inbox_remove_attachments"), NULL,
3984 MODEST_WINDOW_MENU_CALLBACK (modest_ui_actions_remove_attachments),
3985 MODEST_DIMMING_CALLBACK (modest_ui_dimming_rules_on_remove_attachments));
3987 modest_window_add_to_menu (MODEST_WINDOW (self), _("mcen_me_new_message"), "<Control>n",
3988 MODEST_WINDOW_MENU_CALLBACK (modest_ui_actions_on_new_msg),
3989 MODEST_DIMMING_CALLBACK (modest_ui_dimming_rules_on_new_msg));
3990 modest_window_add_to_menu (MODEST_WINDOW (self), _("mcen_me_viewer_addtocontacts"), NULL,
3991 MODEST_WINDOW_MENU_CALLBACK (modest_ui_actions_add_to_contacts),
3992 MODEST_DIMMING_CALLBACK (modest_ui_dimming_rules_on_add_to_contacts));
3994 modest_window_add_to_menu (MODEST_WINDOW (self), _("mcen_ti_message_properties"), NULL,
3995 MODEST_WINDOW_MENU_CALLBACK (modest_ui_actions_on_details),
3996 MODEST_DIMMING_CALLBACK (modest_ui_dimming_rules_on_details));
4000 modest_msg_view_window_add_to_contacts (ModestMsgViewWindow *self)
4002 ModestMsgViewWindowPrivate *priv;
4003 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
4004 GSList *recipients = NULL;
4007 msg = tny_msg_view_get_msg (TNY_MSG_VIEW (priv->msg_view));
4011 header = modest_msg_view_window_get_header (self);
4014 recipients = modest_tny_msg_header_get_all_recipients_list (header);
4015 g_object_unref (header);
4017 recipients = modest_tny_msg_get_all_recipients_list (msg);
4018 g_object_unref (msg);
4022 /* Offer the user to add recipients to the address book */
4023 modest_address_book_add_address_list_with_selector (recipients, (GtkWindow *) self);
4024 g_slist_foreach (recipients, (GFunc) g_free, NULL); g_slist_free (recipients);
4029 _modest_msg_view_window_map_event (GtkWidget *widget,
4033 ModestMsgViewWindow *self = (ModestMsgViewWindow *) userdata;
4035 update_progress_hint (self);
4041 modest_msg_view_window_fetch_images (ModestMsgViewWindow *self)
4043 ModestMsgViewWindowPrivate *priv;
4044 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
4046 modest_msg_view_request_fetch_images (MODEST_MSG_VIEW (priv->msg_view));
4050 modest_msg_view_window_has_blocked_external_images (ModestMsgViewWindow *self)
4052 ModestMsgViewWindowPrivate *priv;
4053 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
4055 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self), FALSE);
4057 return modest_msg_view_has_blocked_external_images (MODEST_MSG_VIEW (priv->msg_view));
4061 modest_msg_view_window_reload (ModestMsgViewWindow *self)
4063 ModestMsgViewWindowPrivate *priv;
4064 const gchar *msg_uid;
4065 TnyHeader *header = NULL;
4066 TnyFolder *folder = NULL;
4068 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self));
4070 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
4072 header = modest_msg_view_window_get_header (MODEST_MSG_VIEW_WINDOW (self));
4076 folder = tny_header_get_folder (header);
4077 g_object_unref (header);
4082 msg_uid = modest_msg_view_window_get_message_uid (self);
4084 GtkTreeRowReference *row_reference;
4086 if (priv->row_reference && gtk_tree_row_reference_valid (priv->row_reference)) {
4087 row_reference = priv->row_reference;
4089 row_reference = NULL;
4091 if (!message_reader (self, priv, NULL, msg_uid, folder, row_reference))
4092 g_warning ("Shouldn't happen, trying to reload a message failed");
4095 g_object_unref (folder);
4099 update_branding (ModestMsgViewWindow *self)
4101 const gchar *account;
4102 const gchar *mailbox;
4103 ModestAccountMgr *mgr;
4104 ModestProtocol *protocol = NULL;
4105 gchar *service_name = NULL;
4106 const GdkPixbuf *service_icon = NULL;
4107 ModestMsgViewWindowPrivate *priv;
4109 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
4111 account = modest_window_get_active_account (MODEST_WINDOW (self));
4112 mailbox = modest_window_get_active_mailbox (MODEST_WINDOW (self));
4114 mgr = modest_runtime_get_account_mgr ();
4116 if (modest_account_mgr_account_is_multimailbox (mgr, account, &protocol)) {
4117 if (MODEST_IS_ACCOUNT_PROTOCOL (protocol)) {
4118 service_name = modest_account_protocol_get_service_name (MODEST_ACCOUNT_PROTOCOL (protocol),
4120 service_icon = modest_account_protocol_get_service_icon (MODEST_ACCOUNT_PROTOCOL (protocol),
4121 account, mailbox, MODEST_ICON_SIZE_SMALL);
4125 modest_msg_view_set_branding (MODEST_MSG_VIEW (priv->msg_view), service_name, service_icon);
4126 g_free (service_name);
4130 sync_flags (ModestMsgViewWindow *self)
4132 TnyHeader *header = NULL;
4134 header = modest_msg_view_window_get_header (self);
4136 TnyMsg *msg = modest_msg_view_window_get_message (self);
4138 header = tny_msg_get_header (msg);
4139 g_object_unref (msg);
4144 TnyFolder *folder = tny_header_get_folder (header);
4147 ModestMailOperation *mail_op;
4149 /* Sync folder, we need this to save the seen flag */
4150 mail_op = modest_mail_operation_new (NULL);
4151 modest_mail_operation_queue_add (modest_runtime_get_mail_operation_queue (),
4153 modest_mail_operation_sync_folder (mail_op, folder, FALSE, NULL, NULL);
4154 g_object_unref (mail_op);
4155 g_object_unref (folder);
4157 g_object_unref (header);
4161 #ifdef MODEST_TOOLKIT_HILDON2
4163 on_realize (GtkWidget *widget,
4166 GdkDisplay *display;
4168 unsigned long val = 1;
4170 display = gdk_drawable_get_display (widget->window);
4171 atom = gdk_x11_get_xatom_by_name_for_display (display, "_HILDON_ZOOM_KEY_ATOM");
4172 XChangeProperty (GDK_DISPLAY_XDISPLAY (display),
4173 GDK_WINDOW_XID (widget->window), atom,
4174 XA_INTEGER, 32, PropModeReplace,
4175 (unsigned char *) &val, 1);
4182 on_handle_calendar (ModestMsgView *msgview, TnyMimePart *calendar_part, GtkContainer *container, ModestMsgViewWindow *self)
4184 const gchar *account_name;
4185 ModestProtocolType proto_type;
4186 ModestProtocol *protocol;
4187 gboolean retval = FALSE;
4189 account_name = modest_window_get_active_account (MODEST_WINDOW (self));
4192 proto_type = modest_account_mgr_get_store_protocol (modest_runtime_get_account_mgr (),
4195 modest_protocol_registry_get_protocol_by_type (modest_runtime_get_protocol_registry (),
4198 if (MODEST_IS_ACCOUNT_PROTOCOL (protocol)) {
4199 retval = modest_account_protocol_handle_calendar (MODEST_ACCOUNT_PROTOCOL (protocol), MODEST_WINDOW (self),
4200 calendar_part, container);