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 #define MYDOCS_ENV "MYDOCSDIR"
79 #define DOCS_FOLDER ".documents"
81 typedef struct _ModestMsgViewWindowPrivate ModestMsgViewWindowPrivate;
82 struct _ModestMsgViewWindowPrivate {
85 GtkWidget *main_scroll;
86 GtkWidget *isearch_toolbar;
89 /* Progress observers */
90 GSList *progress_widgets;
93 GtkWidget *prev_toolitem;
94 GtkWidget *next_toolitem;
95 gboolean progress_hint;
98 /* Optimized view enabled */
99 gboolean optimized_view;
101 /* Whether this was created via the *_new_for_search_result() function. */
102 gboolean is_search_result;
104 /* Whether the message is in outbox */
107 /* A reference to the @model of the header view
108 * to allow selecting previous/next messages,
109 * if the message is currently selected in the header view.
111 const gchar *header_folder_id;
112 GtkTreeModel *header_model;
113 GtkTreeRowReference *row_reference;
114 GtkTreeRowReference *next_row_reference;
116 gulong clipboard_change_handler;
117 gulong queue_change_handler;
118 gulong account_removed_handler;
119 gulong row_changed_handler;
120 gulong row_deleted_handler;
121 gulong row_inserted_handler;
122 gulong rows_reordered_handler;
125 GtkWidget *remove_attachment_banner;
128 TnyMimePart *other_body;
133 static void modest_msg_view_window_class_init (ModestMsgViewWindowClass *klass);
134 static void modest_msg_view_window_init (ModestMsgViewWindow *obj);
135 static void modest_header_view_observer_init (ModestHeaderViewObserverIface *iface_class);
136 static void modest_msg_view_window_finalize (GObject *obj);
137 static void modest_msg_view_window_show_isearch_toolbar (GtkWidget *obj, gpointer data);
138 static void modest_msg_view_window_isearch_toolbar_close (GtkWidget *widget,
139 ModestMsgViewWindow *obj);
140 static void modest_msg_view_window_isearch_toolbar_search (GtkWidget *widget,
141 ModestMsgViewWindow *obj);
142 static void modest_msg_view_window_toggle_isearch_toolbar (GtkWidget *obj,
144 static void modest_msg_view_window_disconnect_signals (ModestWindow *self);
146 static gdouble modest_msg_view_window_get_zoom (ModestWindow *window);
147 static void modest_msg_view_window_set_zoom (ModestWindow *window,
149 static gboolean modest_msg_view_window_zoom_minus (ModestWindow *window);
150 static gboolean modest_msg_view_window_zoom_plus (ModestWindow *window);
151 static gboolean modest_msg_view_window_key_event (GtkWidget *window,
154 static void modest_msg_view_window_update_priority (ModestMsgViewWindow *window);
156 static void modest_msg_view_window_show_toolbar (ModestWindow *window,
157 gboolean show_toolbar);
159 static void modest_msg_view_window_clipboard_owner_change (GtkClipboard *clipboard,
161 ModestMsgViewWindow *window);
163 static void modest_msg_view_window_on_row_changed (GtkTreeModel *header_model,
166 ModestMsgViewWindow *window);
168 static void modest_msg_view_window_on_row_deleted (GtkTreeModel *header_model,
170 ModestMsgViewWindow *window);
172 static void modest_msg_view_window_on_row_inserted (GtkTreeModel *header_model,
173 GtkTreePath *tree_path,
174 GtkTreeIter *tree_iter,
175 ModestMsgViewWindow *window);
177 static void modest_msg_view_window_on_row_reordered (GtkTreeModel *header_model,
181 ModestMsgViewWindow *window);
183 static void modest_msg_view_window_update_model_replaced (ModestHeaderViewObserver *window,
185 const gchar *tny_folder_id);
187 static void on_queue_changed (ModestMailOperationQueue *queue,
188 ModestMailOperation *mail_op,
189 ModestMailOperationQueueNotification type,
190 ModestMsgViewWindow *self);
192 static void on_account_removed (TnyAccountStore *account_store,
196 static void on_move_focus (GtkWidget *widget,
197 GtkDirectionType direction,
200 static void view_msg_cb (ModestMailOperation *mail_op,
207 static void set_progress_hint (ModestMsgViewWindow *self,
210 static void update_window_title (ModestMsgViewWindow *window);
212 static void init_window (ModestMsgViewWindow *obj);
214 static gboolean msg_is_visible (TnyHeader *header, gboolean check_outbox);
216 static void check_dimming_rules_after_change (ModestMsgViewWindow *window);
218 static gboolean on_fetch_image (ModestMsgView *msgview,
221 ModestMsgViewWindow *window);
223 static gboolean modest_msg_view_window_scroll_child (ModestMsgViewWindow *self,
224 GtkScrollType scroll_type,
227 static gboolean message_reader (ModestMsgViewWindow *window,
228 ModestMsgViewWindowPrivate *priv,
230 const gchar *msg_uid,
232 GtkTreeRowReference *row_reference);
234 static void setup_menu (ModestMsgViewWindow *self);
235 static gboolean _modest_msg_view_window_map_event (GtkWidget *widget,
238 static void update_branding (ModestMsgViewWindow *self);
239 static void sync_flags (ModestMsgViewWindow *self);
241 /* list my signals */
248 static const GtkActionEntry msg_view_toolbar_action_entries [] = {
251 { "ToolbarMessageReply", MODEST_STOCK_REPLY, N_("mcen_me_inbox_reply"), "<CTRL>R", NULL, G_CALLBACK (modest_ui_actions_on_reply) },
252 { "ToolbarMessageReplyAll", MODEST_STOCK_REPLY_ALL, N_("mcen_me_inbox_replytoall"), NULL, NULL, G_CALLBACK (modest_ui_actions_on_reply_all) },
253 { "ToolbarMessageForward", MODEST_STOCK_FORWARD, N_("mcen_me_inbox_forward"), NULL, NULL, G_CALLBACK (modest_ui_actions_on_forward) },
254 { "ToolbarDeleteMessage", MODEST_STOCK_DELETE, N_("qgn_toolb_gene_deletebutton"), NULL, NULL, G_CALLBACK (modest_ui_actions_on_delete_message_or_folder) },
255 { "ToolbarMessageBack", MODEST_TOOLBAR_ICON_PREV, N_("qgn_toolb_gene_back"), NULL, NULL, G_CALLBACK (modest_ui_actions_on_prev) },
256 { "ToolbarMessageNext", MODEST_TOOLBAR_ICON_NEXT, N_("qgn_toolb_gene_forward"), NULL, NULL, G_CALLBACK (modest_ui_actions_on_next) },
257 { "ToolbarDownloadExternalImages", MODEST_TOOLBAR_ICON_DOWNLOAD_IMAGES, N_("mail_bd_external_images"), NULL, NULL, G_CALLBACK (modest_ui_actions_on_fetch_images) },
260 static const GtkToggleActionEntry msg_view_toggle_action_entries [] = {
261 { "FindInMessage", MODEST_TOOLBAR_ICON_FIND, N_("qgn_toolb_gene_find"), "<CTRL>F", NULL, G_CALLBACK (modest_msg_view_window_toggle_isearch_toolbar), FALSE },
264 #define MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE((o), \
265 MODEST_TYPE_MSG_VIEW_WINDOW, \
266 ModestMsgViewWindowPrivate))
268 static GtkWindowClass *parent_class = NULL;
270 /* uncomment the following if you have defined any signals */
271 static guint signals[LAST_SIGNAL] = {0};
274 modest_msg_view_window_get_type (void)
276 static GType my_type = 0;
278 static const GTypeInfo my_info = {
279 sizeof(ModestMsgViewWindowClass),
280 NULL, /* base init */
281 NULL, /* base finalize */
282 (GClassInitFunc) modest_msg_view_window_class_init,
283 NULL, /* class finalize */
284 NULL, /* class data */
285 sizeof(ModestMsgViewWindow),
287 (GInstanceInitFunc) modest_msg_view_window_init,
290 #ifdef MODEST_TOOLKIT_GTK
291 my_type = g_type_register_static (MODEST_TYPE_WINDOW,
292 "ModestMsgViewWindow",
295 my_type = g_type_register_static (MODEST_TYPE_HILDON2_WINDOW,
296 "ModestMsgViewWindow",
300 static const GInterfaceInfo modest_header_view_observer_info =
302 (GInterfaceInitFunc) modest_header_view_observer_init,
303 NULL, /* interface_finalize */
304 NULL /* interface_data */
307 g_type_add_interface_static (my_type,
308 MODEST_TYPE_HEADER_VIEW_OBSERVER,
309 &modest_header_view_observer_info);
315 save_state (ModestWindow *self)
317 modest_widget_memory_save (modest_runtime_get_conf (),
319 MODEST_CONF_MSG_VIEW_WINDOW_KEY);
323 modest_msg_view_window_scroll_child (ModestMsgViewWindow *self,
324 GtkScrollType scroll_type,
328 ModestMsgViewWindowPrivate *priv;
331 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(self);
333 switch (scroll_type) {
334 case GTK_SCROLL_STEP_UP:
337 case GTK_SCROLL_STEP_DOWN:
340 case GTK_SCROLL_PAGE_UP:
343 case GTK_SCROLL_PAGE_DOWN:
346 case GTK_SCROLL_START:
357 modest_scrollable_scroll ((ModestScrollable *) priv->main_scroll, 0, step);
359 return (gboolean) step;
363 add_scroll_binding (GtkBindingSet *binding_set,
365 GtkScrollType scroll)
367 guint keypad_keyval = keyval - GDK_Left + GDK_KP_Left;
369 gtk_binding_entry_add_signal (binding_set, keyval, 0,
371 GTK_TYPE_SCROLL_TYPE, scroll,
372 G_TYPE_BOOLEAN, FALSE);
373 gtk_binding_entry_add_signal (binding_set, keypad_keyval, 0,
375 GTK_TYPE_SCROLL_TYPE, scroll,
376 G_TYPE_BOOLEAN, FALSE);
380 modest_msg_view_window_class_init (ModestMsgViewWindowClass *klass)
382 GObjectClass *gobject_class;
383 ModestWindowClass *modest_window_class;
384 GtkBindingSet *binding_set;
386 gobject_class = (GObjectClass*) klass;
387 modest_window_class = (ModestWindowClass *) klass;
389 parent_class = g_type_class_peek_parent (klass);
390 gobject_class->finalize = modest_msg_view_window_finalize;
392 modest_window_class->set_zoom_func = modest_msg_view_window_set_zoom;
393 modest_window_class->get_zoom_func = modest_msg_view_window_get_zoom;
394 modest_window_class->zoom_plus_func = modest_msg_view_window_zoom_plus;
395 modest_window_class->zoom_minus_func = modest_msg_view_window_zoom_minus;
396 modest_window_class->show_toolbar_func = modest_msg_view_window_show_toolbar;
397 modest_window_class->disconnect_signals_func = modest_msg_view_window_disconnect_signals;
399 modest_window_class->save_state_func = save_state;
401 klass->scroll_child = modest_msg_view_window_scroll_child;
403 signals[MSG_CHANGED_SIGNAL] =
404 g_signal_new ("msg-changed",
405 G_TYPE_FROM_CLASS (gobject_class),
407 G_STRUCT_OFFSET (ModestMsgViewWindowClass, msg_changed),
409 modest_marshal_VOID__POINTER_POINTER,
410 G_TYPE_NONE, 2, G_TYPE_POINTER, G_TYPE_POINTER);
412 signals[SCROLL_CHILD_SIGNAL] =
413 g_signal_new ("scroll-child",
414 G_TYPE_FROM_CLASS (gobject_class),
415 G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
416 G_STRUCT_OFFSET (ModestMsgViewWindowClass, scroll_child),
418 modest_marshal_BOOLEAN__ENUM_BOOLEAN,
419 G_TYPE_BOOLEAN, 2, GTK_TYPE_SCROLL_TYPE, G_TYPE_BOOLEAN);
421 binding_set = gtk_binding_set_by_class (klass);
422 add_scroll_binding (binding_set, GDK_Up, GTK_SCROLL_STEP_UP);
423 add_scroll_binding (binding_set, GDK_Down, GTK_SCROLL_STEP_DOWN);
424 add_scroll_binding (binding_set, GDK_Page_Up, GTK_SCROLL_PAGE_UP);
425 add_scroll_binding (binding_set, GDK_Page_Down, GTK_SCROLL_PAGE_DOWN);
426 add_scroll_binding (binding_set, GDK_Home, GTK_SCROLL_START);
427 add_scroll_binding (binding_set, GDK_End, GTK_SCROLL_END);
429 g_type_class_add_private (gobject_class, sizeof(ModestMsgViewWindowPrivate));
433 static void modest_header_view_observer_init(
434 ModestHeaderViewObserverIface *iface_class)
436 iface_class->update_func = modest_msg_view_window_update_model_replaced;
440 modest_msg_view_window_init (ModestMsgViewWindow *obj)
442 ModestMsgViewWindowPrivate *priv;
443 ModestWindowPrivate *parent_priv = NULL;
444 GtkActionGroup *action_group = NULL;
445 GError *error = NULL;
447 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(obj);
448 parent_priv = MODEST_WINDOW_GET_PRIVATE(obj);
449 parent_priv->ui_manager = gtk_ui_manager_new();
451 action_group = gtk_action_group_new ("ModestMsgViewWindowActions");
452 gtk_action_group_set_translation_domain (action_group, GETTEXT_PACKAGE);
454 /* Add common actions */
455 gtk_action_group_add_actions (action_group,
456 msg_view_toolbar_action_entries,
457 G_N_ELEMENTS (msg_view_toolbar_action_entries),
459 gtk_action_group_add_toggle_actions (action_group,
460 msg_view_toggle_action_entries,
461 G_N_ELEMENTS (msg_view_toggle_action_entries),
464 gtk_ui_manager_insert_action_group (parent_priv->ui_manager, action_group, 0);
465 g_object_unref (action_group);
467 /* Load the UI definition */
468 gtk_ui_manager_add_ui_from_file (parent_priv->ui_manager, MODEST_UIDIR "modest-msg-view-window-ui.xml",
471 g_printerr ("modest: could not merge modest-msg-view-window-ui.xml: %s\n", error->message);
472 g_error_free (error);
477 /* Add accelerators */
478 gtk_window_add_accel_group (GTK_WINDOW (obj),
479 gtk_ui_manager_get_accel_group (parent_priv->ui_manager));
481 priv->is_search_result = FALSE;
482 priv->is_outbox = FALSE;
484 priv->msg_view = NULL;
485 priv->header_model = NULL;
486 priv->header_folder_id = NULL;
487 priv->clipboard_change_handler = 0;
488 priv->queue_change_handler = 0;
489 priv->account_removed_handler = 0;
490 priv->row_changed_handler = 0;
491 priv->row_deleted_handler = 0;
492 priv->row_inserted_handler = 0;
493 priv->rows_reordered_handler = 0;
494 priv->progress_hint = FALSE;
495 priv->fetching_images = 0;
497 priv->optimized_view = FALSE;
498 priv->purge_timeout = 0;
499 priv->remove_attachment_banner = NULL;
500 priv->msg_uid = NULL;
501 priv->other_body = NULL;
503 priv->sighandlers = NULL;
506 init_window (MODEST_MSG_VIEW_WINDOW(obj));
511 update_progress_hint (ModestMsgViewWindow *self)
513 ModestMsgViewWindowPrivate *priv;
514 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(self);
516 if (GTK_WIDGET_VISIBLE (self)) {
517 modest_window_show_progress (MODEST_WINDOW (self),
518 (priv->progress_hint || (priv->fetching_images > 0))?1:0);
523 set_progress_hint (ModestMsgViewWindow *self,
526 ModestWindowPrivate *parent_priv;
527 ModestMsgViewWindowPrivate *priv;
529 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self));
531 parent_priv = MODEST_WINDOW_GET_PRIVATE(self);
532 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(self);
534 /* Sets current progress hint */
535 priv->progress_hint = enabled;
537 update_progress_hint (self);
543 init_window (ModestMsgViewWindow *obj)
545 GtkWidget *main_vbox;
546 ModestMsgViewWindowPrivate *priv;
548 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(obj);
550 priv->msg_view = GTK_WIDGET (tny_platform_factory_new_msg_view (modest_tny_platform_factory_get_instance ()));
551 modest_msg_view_set_shadow_type (MODEST_MSG_VIEW (priv->msg_view), GTK_SHADOW_NONE);
552 main_vbox = gtk_vbox_new (FALSE, 6);
554 priv->main_scroll = modest_toolkit_factory_create_scrollable (modest_runtime_get_toolkit_factory ());
555 modest_scrollable_set_horizontal_policy (MODEST_SCROLLABLE (priv->main_scroll), GTK_POLICY_AUTOMATIC);
556 g_object_set (G_OBJECT (priv->main_scroll),
557 "movement-mode", MODEST_MOVEMENT_MODE_BOTH,
558 "horizontal-max-overshoot", 0,
560 gtk_container_add (GTK_CONTAINER (priv->main_scroll), priv->msg_view);
561 gtk_box_pack_start (GTK_BOX(main_vbox), priv->main_scroll, TRUE, TRUE, 0);
562 gtk_container_add (GTK_CONTAINER(obj), main_vbox);
564 /* NULL-ize fields if the window is destroyed */
565 g_signal_connect (priv->msg_view, "destroy", G_CALLBACK (gtk_widget_destroyed), &(priv->msg_view));
567 gtk_widget_show_all (GTK_WIDGET(main_vbox));
571 modest_msg_view_window_disconnect_signals (ModestWindow *self)
573 ModestMsgViewWindowPrivate *priv;
574 GtkWidget *header_view = NULL;
575 GtkWindow *parent_window = NULL;
577 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
579 if (gtk_clipboard_get (GDK_SELECTION_PRIMARY) &&
580 g_signal_handler_is_connected (gtk_clipboard_get (GDK_SELECTION_PRIMARY),
581 priv->clipboard_change_handler))
582 g_signal_handler_disconnect (gtk_clipboard_get (GDK_SELECTION_PRIMARY),
583 priv->clipboard_change_handler);
585 if (g_signal_handler_is_connected (G_OBJECT (modest_runtime_get_mail_operation_queue ()),
586 priv->queue_change_handler))
587 g_signal_handler_disconnect (G_OBJECT (modest_runtime_get_mail_operation_queue ()),
588 priv->queue_change_handler);
590 if (g_signal_handler_is_connected (G_OBJECT (modest_runtime_get_account_store ()),
591 priv->account_removed_handler))
592 g_signal_handler_disconnect (G_OBJECT (modest_runtime_get_account_store ()),
593 priv->account_removed_handler);
595 if (priv->header_model) {
596 if (g_signal_handler_is_connected(G_OBJECT (priv->header_model),
597 priv->row_changed_handler))
598 g_signal_handler_disconnect(G_OBJECT (priv->header_model),
599 priv->row_changed_handler);
601 if (g_signal_handler_is_connected(G_OBJECT (priv->header_model),
602 priv->row_deleted_handler))
603 g_signal_handler_disconnect(G_OBJECT (priv->header_model),
604 priv->row_deleted_handler);
606 if (g_signal_handler_is_connected(G_OBJECT (priv->header_model),
607 priv->row_inserted_handler))
608 g_signal_handler_disconnect(G_OBJECT (priv->header_model),
609 priv->row_inserted_handler);
611 if (g_signal_handler_is_connected(G_OBJECT (priv->header_model),
612 priv->rows_reordered_handler))
613 g_signal_handler_disconnect(G_OBJECT (priv->header_model),
614 priv->rows_reordered_handler);
617 modest_signal_mgr_disconnect_all_and_destroy (priv->sighandlers);
618 priv->sighandlers = NULL;
620 parent_window = gtk_window_get_transient_for (GTK_WINDOW (self));
621 if (parent_window && MODEST_IS_HEADER_WINDOW (parent_window)) {
622 header_view = GTK_WIDGET (modest_header_window_get_header_view (MODEST_HEADER_WINDOW (parent_window)));
624 modest_header_view_remove_observer(MODEST_HEADER_VIEW (header_view),
625 MODEST_HEADER_VIEW_OBSERVER(self));
631 modest_msg_view_window_finalize (GObject *obj)
633 ModestMsgViewWindowPrivate *priv;
635 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (obj);
637 /* Sanity check: shouldn't be needed, the window mgr should
638 call this function before */
639 modest_msg_view_window_disconnect_signals (MODEST_WINDOW (obj));
641 if (priv->other_body != NULL) {
642 g_object_unref (priv->other_body);
643 priv->other_body = NULL;
646 if (priv->header_model != NULL) {
647 g_object_unref (priv->header_model);
648 priv->header_model = NULL;
651 if (priv->remove_attachment_banner) {
652 gtk_widget_destroy (priv->remove_attachment_banner);
653 g_object_unref (priv->remove_attachment_banner);
654 priv->remove_attachment_banner = NULL;
657 if (priv->purge_timeout > 0) {
658 g_source_remove (priv->purge_timeout);
659 priv->purge_timeout = 0;
662 if (priv->row_reference) {
663 gtk_tree_row_reference_free (priv->row_reference);
664 priv->row_reference = NULL;
667 if (priv->next_row_reference) {
668 gtk_tree_row_reference_free (priv->next_row_reference);
669 priv->next_row_reference = NULL;
673 g_free (priv->msg_uid);
674 priv->msg_uid = NULL;
677 G_OBJECT_CLASS(parent_class)->finalize (obj);
681 select_next_valid_row (GtkTreeModel *model,
682 GtkTreeRowReference **row_reference,
686 GtkTreeIter tmp_iter;
688 GtkTreePath *next = NULL;
689 gboolean retval = FALSE, finished;
691 g_return_val_if_fail (gtk_tree_row_reference_valid (*row_reference), FALSE);
693 path = gtk_tree_row_reference_get_path (*row_reference);
694 gtk_tree_model_get_iter (model, &tmp_iter, path);
695 gtk_tree_row_reference_free (*row_reference);
696 *row_reference = NULL;
700 TnyHeader *header = NULL;
702 if (gtk_tree_model_iter_next (model, &tmp_iter)) {
703 gtk_tree_model_get (model, &tmp_iter,
704 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
708 if (msg_is_visible (header, is_outbox)) {
709 next = gtk_tree_model_get_path (model, &tmp_iter);
710 *row_reference = gtk_tree_row_reference_new (model, next);
711 gtk_tree_path_free (next);
715 g_object_unref (header);
718 } else if (cycle && gtk_tree_model_get_iter_first (model, &tmp_iter)) {
719 next = gtk_tree_model_get_path (model, &tmp_iter);
721 /* Ensure that we are not selecting the same */
722 if (gtk_tree_path_compare (path, next) != 0) {
723 gtk_tree_model_get (model, &tmp_iter,
724 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
727 if (msg_is_visible (header, is_outbox)) {
728 *row_reference = gtk_tree_row_reference_new (model, next);
732 g_object_unref (header);
736 /* If we ended up in the same message
737 then there is no valid next
741 gtk_tree_path_free (next);
743 /* If there are no more messages and we don't
744 want to start again in the first one then
745 there is no valid next message */
751 gtk_tree_path_free (path);
756 /* TODO: This should be in _init(), with the parameters as properties. */
758 modest_msg_view_window_construct (ModestMsgViewWindow *self,
759 const gchar *modest_account_name,
760 const gchar *mailbox,
761 const gchar *msg_uid)
764 ModestMsgViewWindowPrivate *priv = NULL;
765 ModestWindowPrivate *parent_priv = NULL;
766 ModestDimmingRulesGroup *toolbar_rules_group = NULL;
767 ModestDimmingRulesGroup *clipboard_rules_group = NULL;
769 obj = G_OBJECT (self);
770 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(obj);
771 parent_priv = MODEST_WINDOW_GET_PRIVATE(obj);
773 priv->msg_uid = g_strdup (msg_uid);
776 parent_priv->menubar = NULL;
778 toolbar_rules_group = modest_dimming_rules_group_new (MODEST_DIMMING_RULES_TOOLBAR, TRUE);
779 clipboard_rules_group = modest_dimming_rules_group_new (MODEST_DIMMING_RULES_CLIPBOARD, FALSE);
782 /* Add common dimming rules */
783 modest_dimming_rules_group_add_rules (toolbar_rules_group,
784 modest_msg_view_toolbar_dimming_entries,
785 G_N_ELEMENTS (modest_msg_view_toolbar_dimming_entries),
786 MODEST_WINDOW (self));
787 modest_dimming_rules_group_add_rules (clipboard_rules_group,
788 modest_msg_view_clipboard_dimming_entries,
789 G_N_ELEMENTS (modest_msg_view_clipboard_dimming_entries),
790 MODEST_WINDOW (self));
792 /* Insert dimming rules group for this window */
793 modest_ui_dimming_manager_insert_rules_group (parent_priv->ui_dimming_manager, toolbar_rules_group);
794 modest_ui_dimming_manager_insert_rules_group (parent_priv->ui_dimming_manager, clipboard_rules_group);
795 g_object_unref (toolbar_rules_group);
796 g_object_unref (clipboard_rules_group);
798 /* g_signal_connect (G_OBJECT(obj), "delete-event", G_CALLBACK(on_delete_event), obj); */
800 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);
801 g_signal_connect (G_OBJECT(priv->msg_view), "activate_link",
802 G_CALLBACK (modest_ui_actions_on_msg_link_clicked), obj);
803 g_signal_connect (G_OBJECT(priv->msg_view), "link_hover",
804 G_CALLBACK (modest_ui_actions_on_msg_link_hover), obj);
805 g_signal_connect (G_OBJECT(priv->msg_view), "attachment_clicked",
806 G_CALLBACK (modest_ui_actions_on_msg_attachment_clicked), obj);
807 g_signal_connect (G_OBJECT(priv->msg_view), "recpt_activated",
808 G_CALLBACK (modest_ui_actions_on_msg_recpt_activated), obj);
809 g_signal_connect (G_OBJECT(priv->msg_view), "show_details",
810 G_CALLBACK (modest_ui_actions_on_details), obj);
811 g_signal_connect (G_OBJECT(priv->msg_view), "link_contextual",
812 G_CALLBACK (modest_ui_actions_on_msg_link_contextual), obj);
813 g_signal_connect (G_OBJECT(priv->msg_view), "limit_error",
814 G_CALLBACK (modest_ui_actions_on_limit_error), obj);
815 g_signal_connect (G_OBJECT (priv->msg_view), "fetch_image",
816 G_CALLBACK (on_fetch_image), obj);
818 g_signal_connect (G_OBJECT (obj), "key-release-event",
819 G_CALLBACK (modest_msg_view_window_key_event),
822 g_signal_connect (G_OBJECT (obj), "key-press-event",
823 G_CALLBACK (modest_msg_view_window_key_event),
826 g_signal_connect (G_OBJECT (obj), "move-focus",
827 G_CALLBACK (on_move_focus), obj);
829 g_signal_connect (G_OBJECT (obj), "map-event",
830 G_CALLBACK (_modest_msg_view_window_map_event),
833 /* Mail Operation Queue */
834 priv->queue_change_handler = g_signal_connect (G_OBJECT (modest_runtime_get_mail_operation_queue ()),
836 G_CALLBACK (on_queue_changed),
839 /* Account manager */
840 priv->account_removed_handler = g_signal_connect (G_OBJECT (modest_runtime_get_account_store ()),
842 G_CALLBACK(on_account_removed),
845 modest_window_set_active_account (MODEST_WINDOW(obj), modest_account_name);
846 modest_window_set_active_mailbox (MODEST_WINDOW(obj), mailbox);
848 /* First add out toolbar ... */
849 modest_msg_view_window_show_toolbar (MODEST_WINDOW (obj), TRUE);
851 priv->isearch_toolbar = modest_toolkit_factory_create_isearch_toolbar (modest_runtime_get_toolkit_factory (),
853 modest_window_add_toolbar (MODEST_WINDOW (obj), GTK_TOOLBAR (priv->isearch_toolbar));
854 gtk_widget_set_no_show_all (priv->isearch_toolbar, TRUE);
855 g_signal_connect (G_OBJECT (priv->isearch_toolbar), "isearch-close",
856 G_CALLBACK (modest_msg_view_window_isearch_toolbar_close), obj);
857 g_signal_connect (G_OBJECT (priv->isearch_toolbar), "isearch-search",
858 G_CALLBACK (modest_msg_view_window_isearch_toolbar_search), obj);
859 priv->last_search = NULL;
861 /* Init the clipboard actions dim status */
862 modest_msg_view_grab_focus(MODEST_MSG_VIEW (priv->msg_view));
864 update_window_title (MODEST_MSG_VIEW_WINDOW (obj));
869 /* FIXME: parameter checks */
871 modest_msg_view_window_new_with_header_model (TnyMsg *msg,
872 const gchar *modest_account_name,
873 const gchar *mailbox,
874 const gchar *msg_uid,
876 GtkTreeRowReference *row_reference)
878 ModestMsgViewWindow *window = NULL;
879 ModestMsgViewWindowPrivate *priv = NULL;
880 TnyFolder *header_folder = NULL;
881 ModestHeaderView *header_view = NULL;
882 ModestWindowMgr *mgr = NULL;
885 modest_tny_mime_part_to_string (TNY_MIME_PART (msg), 0);
888 mgr = modest_runtime_get_window_mgr ();
889 window = MODEST_MSG_VIEW_WINDOW (modest_window_mgr_get_msg_view_window (mgr));
890 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), NULL);
892 modest_msg_view_window_construct (window, modest_account_name, mailbox, msg_uid);
894 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
896 /* Remember the message list's TreeModel so we can detect changes
897 * and change the list selection when necessary: */
898 header_folder = modest_header_view_get_folder (header_view);
900 priv->is_outbox = (modest_tny_folder_guess_folder_type (header_folder) ==
901 TNY_FOLDER_TYPE_OUTBOX);
902 priv->header_folder_id = tny_folder_get_id (header_folder);
903 g_object_unref(header_folder);
906 /* Setup row references and connect signals */
907 priv->header_model = g_object_ref (model);
909 if (row_reference && gtk_tree_row_reference_valid (row_reference)) {
910 priv->row_reference = gtk_tree_row_reference_copy (row_reference);
911 priv->next_row_reference = gtk_tree_row_reference_copy (row_reference);
912 select_next_valid_row (model, &(priv->next_row_reference), TRUE, priv->is_outbox);
914 priv->row_reference = NULL;
915 priv->next_row_reference = NULL;
918 /* Connect signals */
919 priv->row_changed_handler =
920 g_signal_connect (GTK_TREE_MODEL(model), "row-changed",
921 G_CALLBACK(modest_msg_view_window_on_row_changed),
923 priv->row_deleted_handler =
924 g_signal_connect (GTK_TREE_MODEL(model), "row-deleted",
925 G_CALLBACK(modest_msg_view_window_on_row_deleted),
927 priv->row_inserted_handler =
928 g_signal_connect (GTK_TREE_MODEL(model), "row-inserted",
929 G_CALLBACK(modest_msg_view_window_on_row_inserted),
931 priv->rows_reordered_handler =
932 g_signal_connect(GTK_TREE_MODEL(model), "rows-reordered",
933 G_CALLBACK(modest_msg_view_window_on_row_reordered),
936 if (header_view != NULL){
937 modest_header_view_add_observer(header_view,
938 MODEST_HEADER_VIEW_OBSERVER(window));
941 tny_msg_view_set_msg (TNY_MSG_VIEW (priv->msg_view), msg);
942 update_window_title (MODEST_MSG_VIEW_WINDOW (window));
943 update_branding (MODEST_MSG_VIEW_WINDOW (window));
945 /* gtk_widget_show_all (GTK_WIDGET (window)); */
946 modest_msg_view_window_update_priority (window);
947 /* Check dimming rules */
948 modest_ui_actions_check_toolbar_dimming_rules (MODEST_WINDOW (window));
949 modest_ui_actions_check_menu_dimming_rules (MODEST_WINDOW (window));
950 modest_window_check_dimming_rules_group (MODEST_WINDOW (window), MODEST_DIMMING_RULES_CLIPBOARD);
952 return MODEST_WINDOW(window);
956 modest_msg_view_window_new_from_uid (const gchar *modest_account_name,
957 const gchar *mailbox,
958 const gchar *msg_uid)
960 ModestMsgViewWindow *window = NULL;
961 ModestMsgViewWindowPrivate *priv = NULL;
962 ModestWindowMgr *mgr = NULL;
964 TnyAccount *account = NULL;
966 mgr = modest_runtime_get_window_mgr ();
967 window = MODEST_MSG_VIEW_WINDOW (modest_window_mgr_get_msg_view_window (mgr));
968 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), NULL);
970 modest_msg_view_window_construct (window, modest_account_name, mailbox, msg_uid);
972 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
974 is_merge = g_str_has_prefix (msg_uid, "merge:");
976 /* Get the account */
978 account = tny_account_store_find_account (TNY_ACCOUNT_STORE (modest_runtime_get_account_store ()),
981 if (is_merge || account) {
982 TnyFolder *folder = NULL;
984 /* Try to get the message, if it's already downloaded
985 we don't need to connect */
987 folder = modest_tny_folder_store_find_folder_from_uri (TNY_FOLDER_STORE (account), msg_uid);
989 ModestTnyAccountStore *account_store;
990 ModestTnyLocalFoldersAccount *local_folders_account;
992 account_store = modest_runtime_get_account_store ();
993 local_folders_account = MODEST_TNY_LOCAL_FOLDERS_ACCOUNT (
994 modest_tny_account_store_get_local_folders_account (account_store));
995 folder = modest_tny_local_folders_account_get_merged_outbox (local_folders_account);
996 g_object_unref (local_folders_account);
1000 gboolean device_online;
1002 device = modest_runtime_get_device();
1003 device_online = tny_device_is_online (device);
1004 if (device_online) {
1005 message_reader (window, priv, NULL, msg_uid, folder, NULL);
1007 TnyMsg *msg = tny_folder_find_msg (folder, msg_uid, NULL);
1009 tny_msg_view_set_msg (TNY_MSG_VIEW (priv->msg_view), msg);
1010 update_window_title (MODEST_MSG_VIEW_WINDOW (window));
1011 update_branding (MODEST_MSG_VIEW_WINDOW (window));
1012 g_object_unref (msg);
1013 /* Sync flags to server */
1014 sync_flags (MODEST_MSG_VIEW_WINDOW (window));
1016 message_reader (window, priv, NULL, msg_uid, folder, NULL);
1019 g_object_unref (folder);
1024 /* Check dimming rules */
1025 modest_ui_actions_check_toolbar_dimming_rules (MODEST_WINDOW (window));
1026 modest_ui_actions_check_menu_dimming_rules (MODEST_WINDOW (window));
1027 modest_window_check_dimming_rules_group (MODEST_WINDOW (window), MODEST_DIMMING_RULES_CLIPBOARD);
1029 return MODEST_WINDOW(window);
1033 modest_msg_view_window_new_from_header_view (ModestHeaderView *header_view,
1034 const gchar *modest_account_name,
1035 const gchar *mailbox,
1036 const gchar *msg_uid,
1037 GtkTreeRowReference *row_reference)
1039 ModestMsgViewWindow *window = NULL;
1040 ModestMsgViewWindowPrivate *priv = NULL;
1041 TnyFolder *header_folder = NULL;
1042 ModestWindowMgr *mgr = NULL;
1046 mgr = modest_runtime_get_window_mgr ();
1047 window = MODEST_MSG_VIEW_WINDOW (modest_window_mgr_get_msg_view_window (mgr));
1048 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), NULL);
1050 modest_msg_view_window_construct (window, modest_account_name, mailbox, msg_uid);
1052 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1054 /* Remember the message list's TreeModel so we can detect changes
1055 * and change the list selection when necessary: */
1057 if (header_view != NULL){
1058 header_folder = modest_header_view_get_folder(header_view);
1059 /* This could happen if the header folder was
1060 unseleted before opening this msg window (for
1061 example if the user selects an account in the
1062 folder view of the main window */
1063 if (header_folder) {
1064 priv->is_outbox = (modest_tny_folder_guess_folder_type (header_folder) ==
1065 TNY_FOLDER_TYPE_OUTBOX);
1066 priv->header_folder_id = tny_folder_get_id(header_folder);
1067 g_object_unref(header_folder);
1071 /* Setup row references and connect signals */
1072 priv->header_model = gtk_tree_view_get_model (GTK_TREE_VIEW (header_view));
1073 g_object_ref (priv->header_model);
1075 if (row_reference && gtk_tree_row_reference_valid (row_reference)) {
1076 priv->row_reference = gtk_tree_row_reference_copy (row_reference);
1077 priv->next_row_reference = gtk_tree_row_reference_copy (row_reference);
1078 select_next_valid_row (priv->header_model, &(priv->next_row_reference), TRUE, priv->is_outbox);
1080 priv->row_reference = NULL;
1081 priv->next_row_reference = NULL;
1084 /* Connect signals */
1085 priv->row_changed_handler =
1086 g_signal_connect (GTK_TREE_MODEL(priv->header_model), "row-changed",
1087 G_CALLBACK(modest_msg_view_window_on_row_changed),
1089 priv->row_deleted_handler =
1090 g_signal_connect (GTK_TREE_MODEL(priv->header_model), "row-deleted",
1091 G_CALLBACK(modest_msg_view_window_on_row_deleted),
1093 priv->row_inserted_handler =
1094 g_signal_connect (GTK_TREE_MODEL(priv->header_model), "row-inserted",
1095 G_CALLBACK(modest_msg_view_window_on_row_inserted),
1097 priv->rows_reordered_handler =
1098 g_signal_connect(GTK_TREE_MODEL(priv->header_model), "rows-reordered",
1099 G_CALLBACK(modest_msg_view_window_on_row_reordered),
1102 if (header_view != NULL){
1103 modest_header_view_add_observer(header_view,
1104 MODEST_HEADER_VIEW_OBSERVER(window));
1107 tny_msg_view_set_msg (TNY_MSG_VIEW (priv->msg_view), NULL);
1108 update_branding (MODEST_MSG_VIEW_WINDOW (window));
1110 if (priv->row_reference) {
1111 path = gtk_tree_row_reference_get_path (priv->row_reference);
1112 if (gtk_tree_model_get_iter (priv->header_model, &iter, path)) {
1114 gtk_tree_model_get (priv->header_model, &iter,
1115 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
1117 message_reader (window, priv, header, NULL, NULL, priv->row_reference);
1118 g_object_unref (header);
1120 gtk_tree_path_free (path);
1122 /* Check dimming rules */
1123 modest_ui_actions_check_toolbar_dimming_rules (MODEST_WINDOW (window));
1124 modest_ui_actions_check_menu_dimming_rules (MODEST_WINDOW (window));
1125 modest_window_check_dimming_rules_group (MODEST_WINDOW (window), MODEST_DIMMING_RULES_CLIPBOARD);
1127 return MODEST_WINDOW(window);
1131 modest_msg_view_window_new_for_search_result (TnyMsg *msg,
1132 const gchar *modest_account_name,
1133 const gchar *mailbox,
1134 const gchar *msg_uid)
1136 ModestMsgViewWindow *window = NULL;
1137 ModestMsgViewWindowPrivate *priv = NULL;
1138 ModestWindowMgr *mgr = NULL;
1140 mgr = modest_runtime_get_window_mgr ();
1141 window = MODEST_MSG_VIEW_WINDOW (modest_window_mgr_get_msg_view_window (mgr));
1142 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), NULL);
1143 modest_msg_view_window_construct (window, modest_account_name, mailbox, msg_uid);
1145 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1147 /* Remember that this is a search result,
1148 * so we can disable some UI appropriately: */
1149 priv->is_search_result = TRUE;
1151 tny_msg_view_set_msg (TNY_MSG_VIEW (priv->msg_view), msg);
1152 update_branding (MODEST_MSG_VIEW_WINDOW (window));
1154 update_window_title (window);
1155 /* gtk_widget_show_all (GTK_WIDGET (window));*/
1156 modest_msg_view_window_update_priority (window);
1158 /* Check dimming rules */
1159 modest_ui_actions_check_toolbar_dimming_rules (MODEST_WINDOW (window));
1160 modest_ui_actions_check_menu_dimming_rules (MODEST_WINDOW (window));
1161 modest_window_check_dimming_rules_group (MODEST_WINDOW (window), MODEST_DIMMING_RULES_CLIPBOARD);
1163 return MODEST_WINDOW(window);
1167 modest_msg_view_window_is_other_body (ModestMsgViewWindow *self)
1169 ModestMsgViewWindowPrivate *priv = NULL;
1171 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self), FALSE);
1172 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
1174 return (priv->other_body != NULL);
1178 modest_msg_view_window_new_with_other_body (TnyMsg *msg,
1179 TnyMimePart *other_body,
1180 const gchar *modest_account_name,
1181 const gchar *mailbox,
1182 const gchar *msg_uid)
1184 GObject *obj = NULL;
1185 ModestMsgViewWindowPrivate *priv;
1186 ModestWindowMgr *mgr = NULL;
1188 g_return_val_if_fail (msg, NULL);
1189 mgr = modest_runtime_get_window_mgr ();
1190 obj = G_OBJECT (modest_window_mgr_get_msg_view_window (mgr));
1191 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (obj);
1192 modest_msg_view_window_construct (MODEST_MSG_VIEW_WINDOW (obj),
1193 modest_account_name, mailbox, msg_uid);
1196 priv->other_body = g_object_ref (other_body);
1197 modest_msg_view_set_msg_with_other_body (MODEST_MSG_VIEW (priv->msg_view), msg, other_body);
1199 tny_msg_view_set_msg (TNY_MSG_VIEW (priv->msg_view), msg);
1201 update_window_title (MODEST_MSG_VIEW_WINDOW (obj));
1202 update_branding (MODEST_MSG_VIEW_WINDOW (obj));
1204 /* gtk_widget_show_all (GTK_WIDGET (obj)); */
1206 /* Check dimming rules */
1207 modest_ui_actions_check_toolbar_dimming_rules (MODEST_WINDOW (obj));
1208 modest_ui_actions_check_menu_dimming_rules (MODEST_WINDOW (obj));
1209 modest_window_check_dimming_rules_group (MODEST_WINDOW (obj), MODEST_DIMMING_RULES_CLIPBOARD);
1211 return MODEST_WINDOW(obj);
1215 modest_msg_view_window_new_for_attachment (TnyMsg *msg,
1216 const gchar *modest_account_name,
1217 const gchar *mailbox,
1218 const gchar *msg_uid)
1220 return modest_msg_view_window_new_with_other_body (msg, NULL, modest_account_name, mailbox, msg_uid);
1224 modest_msg_view_window_on_row_changed (GtkTreeModel *header_model,
1227 ModestMsgViewWindow *window)
1229 check_dimming_rules_after_change (window);
1233 modest_msg_view_window_on_row_deleted(GtkTreeModel *header_model,
1235 ModestMsgViewWindow *window)
1237 check_dimming_rules_after_change (window);
1239 /* The window could have dissapeared */
1242 check_dimming_rules_after_change (ModestMsgViewWindow *window)
1244 modest_ui_actions_check_menu_dimming_rules (MODEST_WINDOW (window));
1245 modest_ui_actions_check_toolbar_dimming_rules (MODEST_WINDOW (window));
1249 /* On insertions we check if the folder still has the message we are
1250 * showing or do not. If do not, we do nothing. Which means we are still
1251 * not attached to any header folder and thus next/prev buttons are
1252 * still dimmed. Once the message that is shown by msg-view is found, the
1253 * new model of header-view will be attached and the references will be set.
1254 * On each further insertions dimming rules will be checked. However
1255 * this requires extra CPU time at least works.
1256 * (An message might be deleted from TnyFolder and thus will not be
1257 * inserted into the model again for example if it is removed by the
1258 * imap server and the header view is refreshed.)
1261 modest_msg_view_window_on_row_inserted (GtkTreeModel *model,
1262 GtkTreePath *tree_path,
1263 GtkTreeIter *tree_iter,
1264 ModestMsgViewWindow *window)
1266 ModestMsgViewWindowPrivate *priv = NULL;
1267 TnyHeader *header = NULL;
1269 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window));
1270 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1272 g_assert (model == priv->header_model);
1274 /* Check if the newly inserted message is the same we are actually
1275 * showing. IF not, we should remain detached from the header model
1276 * and thus prev and next toolbar buttons should remain dimmed. */
1277 gtk_tree_model_get (model, tree_iter,
1278 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
1281 if (TNY_IS_HEADER (header)) {
1284 uid = modest_tny_folder_get_header_unique_id (header);
1285 if (!g_str_equal(priv->msg_uid, uid)) {
1286 check_dimming_rules_after_change (window);
1288 g_object_unref (G_OBJECT(header));
1292 g_object_unref(G_OBJECT(header));
1295 if (priv->row_reference) {
1296 gtk_tree_row_reference_free (priv->row_reference);
1299 /* Setup row_reference for the actual msg. */
1300 priv->row_reference = gtk_tree_row_reference_new (priv->header_model, tree_path);
1301 if (priv->row_reference == NULL) {
1302 g_warning("%s: No reference for msg header item.", __FUNCTION__);
1306 /* Now set up next_row_reference. */
1307 if (priv->next_row_reference) {
1308 gtk_tree_row_reference_free (priv->next_row_reference);
1311 priv->next_row_reference = gtk_tree_row_reference_copy (priv->row_reference);
1312 select_next_valid_row (priv->header_model,
1313 &(priv->next_row_reference), FALSE, priv->is_outbox);
1315 /* Connect the remaining callbacks to become able to detect
1316 * changes in header-view. */
1317 priv->row_changed_handler =
1318 g_signal_connect (priv->header_model, "row-changed",
1319 G_CALLBACK (modest_msg_view_window_on_row_changed),
1321 priv->row_deleted_handler =
1322 g_signal_connect (priv->header_model, "row-deleted",
1323 G_CALLBACK (modest_msg_view_window_on_row_deleted),
1325 priv->rows_reordered_handler =
1326 g_signal_connect (priv->header_model, "rows-reordered",
1327 G_CALLBACK (modest_msg_view_window_on_row_reordered),
1330 check_dimming_rules_after_change (window);
1334 modest_msg_view_window_on_row_reordered (GtkTreeModel *header_model,
1338 ModestMsgViewWindow *window)
1340 ModestMsgViewWindowPrivate *priv = NULL;
1341 gboolean already_changed = FALSE;
1343 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(window);
1345 /* If the current row was reordered select the proper next
1346 valid row. The same if the next row reference changes */
1347 if (!priv->row_reference ||
1348 !gtk_tree_row_reference_valid (priv->row_reference))
1351 if (priv->next_row_reference &&
1352 gtk_tree_row_reference_valid (priv->next_row_reference)) {
1353 GtkTreePath *cur, *next;
1354 /* Check that the order is still the correct one */
1355 cur = gtk_tree_row_reference_get_path (priv->row_reference);
1356 next = gtk_tree_row_reference_get_path (priv->next_row_reference);
1357 gtk_tree_path_next (cur);
1358 if (gtk_tree_path_compare (cur, next) != 0) {
1359 gtk_tree_row_reference_free (priv->next_row_reference);
1360 priv->next_row_reference = gtk_tree_row_reference_copy (priv->row_reference);
1361 select_next_valid_row (header_model, &(priv->next_row_reference), FALSE, priv->is_outbox);
1362 already_changed = TRUE;
1364 gtk_tree_path_free (cur);
1365 gtk_tree_path_free (next);
1367 if (priv->next_row_reference)
1368 gtk_tree_row_reference_free (priv->next_row_reference);
1369 /* Update next row reference */
1370 priv->next_row_reference = gtk_tree_row_reference_copy (priv->row_reference);
1371 select_next_valid_row (header_model, &(priv->next_row_reference), FALSE, priv->is_outbox);
1372 already_changed = TRUE;
1375 check_dimming_rules_after_change (window);
1378 /* The modest_msg_view_window_update_model_replaced implements update
1379 * function for ModestHeaderViewObserver. Checks whether the TnyFolder
1380 * actually belongs to the header-view is the same as the TnyFolder of
1381 * the message of msg-view or not. If they are different, there is
1382 * nothing to do. If they are the same, then the model has replaced and
1383 * the reference in msg-view shall be replaced from the old model to
1384 * the new model. In this case the view will be detached from it's
1385 * header folder. From this point the next/prev buttons are dimmed.
1388 modest_msg_view_window_update_model_replaced (ModestHeaderViewObserver *observer,
1389 GtkTreeModel *model,
1390 const gchar *tny_folder_id)
1392 ModestMsgViewWindowPrivate *priv = NULL;
1393 ModestMsgViewWindow *window = NULL;
1395 g_assert(MODEST_IS_HEADER_VIEW_OBSERVER(observer));
1396 g_assert(MODEST_IS_MSG_VIEW_WINDOW(observer));
1398 window = MODEST_MSG_VIEW_WINDOW(observer);
1399 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(window);
1401 /* If there is an other folder in the header-view then we do
1402 * not care about it's model (msg list). Else if the
1403 * header-view shows the folder the msg shown by us is in, we
1404 * shall replace our model reference and make some check. */
1405 if(model == NULL || tny_folder_id == NULL ||
1406 (priv->header_folder_id && !g_str_equal(tny_folder_id, priv->header_folder_id)))
1409 /* Model is changed(replaced), so we should forget the old
1410 * one. Because there might be other references and there
1411 * might be some change on the model even if we unreferenced
1412 * it, we need to disconnect our signals here. */
1413 if (priv->header_model) {
1414 if (g_signal_handler_is_connected(G_OBJECT (priv->header_model),
1415 priv->row_changed_handler))
1416 g_signal_handler_disconnect(G_OBJECT (priv->header_model),
1417 priv->row_changed_handler);
1418 if (g_signal_handler_is_connected(G_OBJECT (priv->header_model),
1419 priv->row_deleted_handler))
1420 g_signal_handler_disconnect(G_OBJECT (priv->header_model),
1421 priv->row_deleted_handler);
1422 if (g_signal_handler_is_connected(G_OBJECT (priv->header_model),
1423 priv->row_inserted_handler))
1424 g_signal_handler_disconnect(G_OBJECT (priv->header_model),
1425 priv->row_inserted_handler);
1426 if (g_signal_handler_is_connected(G_OBJECT (priv->header_model),
1427 priv->rows_reordered_handler))
1428 g_signal_handler_disconnect(G_OBJECT (priv->header_model),
1429 priv->rows_reordered_handler);
1432 if (priv->row_reference)
1433 gtk_tree_row_reference_free (priv->row_reference);
1434 if (priv->next_row_reference)
1435 gtk_tree_row_reference_free (priv->next_row_reference);
1436 g_object_unref(priv->header_model);
1439 priv->row_changed_handler = 0;
1440 priv->row_deleted_handler = 0;
1441 priv->row_inserted_handler = 0;
1442 priv->rows_reordered_handler = 0;
1443 priv->next_row_reference = NULL;
1444 priv->row_reference = NULL;
1445 priv->header_model = NULL;
1448 priv->header_model = g_object_ref (model);
1450 /* Also we must connect to the new model for row insertions.
1451 * Only for insertions now. We will need other ones only after
1452 * the msg is show by msg-view is added to the new model. */
1453 priv->row_inserted_handler =
1454 g_signal_connect (priv->header_model, "row-inserted",
1455 G_CALLBACK(modest_msg_view_window_on_row_inserted),
1458 modest_ui_actions_check_menu_dimming_rules(MODEST_WINDOW(window));
1459 modest_ui_actions_check_toolbar_dimming_rules(MODEST_WINDOW(window));
1463 modest_msg_view_window_toolbar_on_transfer_mode (ModestMsgViewWindow *self)
1465 ModestMsgViewWindowPrivate *priv= NULL;
1467 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self), FALSE);
1468 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
1470 return priv->progress_hint;
1474 modest_msg_view_window_get_header (ModestMsgViewWindow *self)
1476 ModestMsgViewWindowPrivate *priv= NULL;
1478 TnyHeader *header = NULL;
1479 GtkTreePath *path = NULL;
1482 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self), NULL);
1483 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
1485 /* If the message was not obtained from a treemodel,
1486 * for instance if it was opened directly by the search UI:
1488 if (priv->header_model == NULL ||
1489 priv->row_reference == NULL ||
1490 !gtk_tree_row_reference_valid (priv->row_reference)) {
1491 msg = modest_msg_view_window_get_message (self);
1493 header = tny_msg_get_header (msg);
1494 g_object_unref (msg);
1499 /* Get iter of the currently selected message in the header view: */
1500 path = gtk_tree_row_reference_get_path (priv->row_reference);
1501 g_return_val_if_fail (path != NULL, NULL);
1502 gtk_tree_model_get_iter (priv->header_model,
1506 /* Get current message header */
1507 gtk_tree_model_get (priv->header_model, &iter,
1508 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
1511 gtk_tree_path_free (path);
1516 modest_msg_view_window_get_message (ModestMsgViewWindow *self)
1518 ModestMsgViewWindowPrivate *priv;
1520 g_return_val_if_fail (self, NULL);
1522 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(self);
1524 return tny_msg_view_get_msg (TNY_MSG_VIEW (priv->msg_view));
1528 modest_msg_view_window_get_message_uid (ModestMsgViewWindow *self)
1530 ModestMsgViewWindowPrivate *priv;
1532 g_return_val_if_fail (self, NULL);
1534 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
1536 return (const gchar*) priv->msg_uid;
1539 /* Used for the Ctrl+F accelerator */
1541 modest_msg_view_window_toggle_isearch_toolbar (GtkWidget *obj,
1544 ModestMsgViewWindow *window = MODEST_MSG_VIEW_WINDOW (data);
1545 ModestMsgViewWindowPrivate *priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1547 if (GTK_WIDGET_VISIBLE (priv->isearch_toolbar)) {
1548 modest_msg_view_window_isearch_toolbar_close (obj, data);
1550 modest_msg_view_window_show_isearch_toolbar (obj, data);
1554 /* Handler for menu option */
1556 modest_msg_view_window_show_isearch_toolbar (GtkWidget *obj,
1559 ModestMsgViewWindow *window = MODEST_MSG_VIEW_WINDOW (data);
1560 ModestMsgViewWindowPrivate *priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1562 gtk_widget_show (priv->isearch_toolbar);
1563 modest_isearch_toolbar_highlight_entry (MODEST_ISEARCH_TOOLBAR (priv->isearch_toolbar), TRUE);
1566 /* Handler for click on the "X" close button in isearch toolbar */
1568 modest_msg_view_window_isearch_toolbar_close (GtkWidget *widget,
1569 ModestMsgViewWindow *obj)
1571 ModestMsgViewWindowPrivate *priv;
1573 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (obj);
1576 gtk_widget_hide (priv->isearch_toolbar);
1577 modest_msg_view_grab_focus (MODEST_MSG_VIEW (priv->msg_view));
1581 modest_msg_view_window_isearch_toolbar_search (GtkWidget *widget,
1582 ModestMsgViewWindow *obj)
1584 const gchar *current_search;
1585 ModestMsgViewWindowPrivate *priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (obj);
1587 if (modest_mime_part_view_is_empty (MODEST_MIME_PART_VIEW (priv->msg_view))) {
1588 modest_platform_system_banner (NULL, NULL, _("mail_ib_nothing_to_find"));
1592 current_search = modest_isearch_toolbar_get_search (MODEST_ISEARCH_TOOLBAR (widget));
1594 if ((current_search == NULL) || (strcmp (current_search, "") == 0)) {
1595 modest_platform_system_banner (NULL, NULL, _CS("ecdg_ib_find_rep_enter_text"));
1599 if ((priv->last_search == NULL) || (strcmp (priv->last_search, current_search) != 0)) {
1601 g_free (priv->last_search);
1602 priv->last_search = g_strdup (current_search);
1603 result = modest_isearch_view_search (MODEST_ISEARCH_VIEW (priv->msg_view),
1606 modest_platform_system_banner (NULL, NULL,
1607 _HL("ckct_ib_find_no_matches"));
1608 g_free (priv->last_search);
1609 priv->last_search = NULL;
1611 modest_isearch_toolbar_highlight_entry (MODEST_ISEARCH_TOOLBAR (priv->isearch_toolbar), TRUE);
1614 if (!modest_isearch_view_search_next (MODEST_ISEARCH_VIEW (priv->msg_view))) {
1615 modest_platform_system_banner (NULL, NULL,
1616 _HL("ckct_ib_find_search_complete"));
1617 g_free (priv->last_search);
1618 priv->last_search = NULL;
1620 modest_isearch_toolbar_highlight_entry (MODEST_ISEARCH_TOOLBAR (priv->isearch_toolbar), TRUE);
1627 modest_msg_view_window_set_zoom (ModestWindow *window,
1630 ModestMsgViewWindowPrivate *priv;
1631 ModestWindowPrivate *parent_priv;
1633 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window));
1635 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1636 parent_priv = MODEST_WINDOW_GET_PRIVATE (window);
1637 modest_zoomable_set_zoom (MODEST_ZOOMABLE (priv->msg_view), zoom);
1642 modest_msg_view_window_get_zoom (ModestWindow *window)
1644 ModestMsgViewWindowPrivate *priv;
1646 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), 1.0);
1648 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1649 return modest_zoomable_get_zoom (MODEST_ZOOMABLE (priv->msg_view));
1653 modest_msg_view_window_zoom_plus (ModestWindow *window)
1656 ModestMsgViewWindowPrivate *priv;
1660 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), 1.0);
1661 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1663 zoom_level = modest_zoomable_get_zoom (MODEST_ZOOMABLE (priv->msg_view));
1665 if (zoom_level >= 2.0) {
1666 modest_platform_system_banner (NULL, NULL,
1667 _CS("ckct_ib_max_zoom_level_reached"));
1669 } else if (zoom_level >= 1.5) {
1671 } else if (zoom_level >= 1.2) {
1673 } else if (zoom_level >= 1.0) {
1675 } else if (zoom_level >= 0.8) {
1677 } else if (zoom_level >= 0.5) {
1683 /* set zoom level */
1684 int_zoom = (gint) rint (zoom_level*100.0+0.1);
1685 banner_text = g_strdup_printf (_HL("wdgt_ib_zoom"), int_zoom);
1686 modest_platform_information_banner (GTK_WIDGET (window), NULL, banner_text);
1687 g_free (banner_text);
1688 modest_zoomable_set_zoom (MODEST_ZOOMABLE (priv->msg_view), zoom_level);
1694 modest_msg_view_window_zoom_minus (ModestWindow *window)
1697 ModestMsgViewWindowPrivate *priv;
1701 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), 1.0);
1702 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1704 zoom_level = modest_zoomable_get_zoom (MODEST_ZOOMABLE (priv->msg_view));
1706 if (zoom_level <= 0.5) {
1707 modest_platform_system_banner (NULL, NULL,
1708 _CS("ckct_ib_min_zoom_level_reached"));
1710 } else if (zoom_level <= 0.8) {
1712 } else if (zoom_level <= 1.0) {
1714 } else if (zoom_level <= 1.2) {
1716 } else if (zoom_level <= 1.5) {
1718 } else if (zoom_level <= 2.0) {
1724 /* set zoom level */
1725 int_zoom = (gint) rint (zoom_level*100.0+0.1);
1726 banner_text = g_strdup_printf (_HL("wdgt_ib_zoom"), int_zoom);
1727 modest_platform_information_banner (GTK_WIDGET (window), NULL, banner_text);
1728 g_free (banner_text);
1729 modest_zoomable_set_zoom (MODEST_ZOOMABLE (priv->msg_view), zoom_level);
1735 modest_msg_view_window_key_event (GtkWidget *window,
1741 focus = gtk_window_get_focus (GTK_WINDOW (window));
1743 /* for the isearch toolbar case */
1744 if (focus && GTK_IS_ENTRY (focus)) {
1745 if (event->keyval == GDK_BackSpace) {
1747 copy = gdk_event_copy ((GdkEvent *) event);
1748 gtk_widget_event (focus, copy);
1749 gdk_event_free (copy);
1759 modest_msg_view_window_last_message_selected (ModestMsgViewWindow *window)
1762 ModestMsgViewWindowPrivate *priv;
1763 GtkTreeIter tmp_iter;
1764 gboolean is_last_selected;
1766 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), TRUE);
1767 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1769 /*if no model (so no rows at all), then virtually we are the last*/
1770 if (!priv->header_model || !priv->row_reference)
1773 if (!gtk_tree_row_reference_valid (priv->row_reference))
1776 path = gtk_tree_row_reference_get_path (priv->row_reference);
1780 is_last_selected = TRUE;
1781 while (is_last_selected) {
1783 gtk_tree_path_next (path);
1784 if (!gtk_tree_model_get_iter (priv->header_model, &tmp_iter, path))
1786 gtk_tree_model_get (priv->header_model, &tmp_iter,
1787 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
1790 if (msg_is_visible (header, priv->is_outbox))
1791 is_last_selected = FALSE;
1792 g_object_unref(G_OBJECT(header));
1795 gtk_tree_path_free (path);
1796 return is_last_selected;
1800 modest_msg_view_window_has_headers_model (ModestMsgViewWindow *window)
1802 ModestMsgViewWindowPrivate *priv;
1804 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), TRUE);
1805 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1807 return priv->header_model != NULL;
1811 modest_msg_view_window_is_search_result (ModestMsgViewWindow *window)
1813 ModestMsgViewWindowPrivate *priv;
1815 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), TRUE);
1816 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1818 return priv->is_search_result;
1822 msg_is_visible (TnyHeader *header, gboolean check_outbox)
1824 if ((tny_header_get_flags(header) & TNY_HEADER_FLAG_DELETED))
1826 if (!check_outbox) {
1829 ModestTnySendQueueStatus status;
1830 status = modest_tny_all_send_queues_get_msg_status (header);
1831 return ((status != MODEST_TNY_SEND_QUEUE_FAILED) &&
1832 (status != MODEST_TNY_SEND_QUEUE_SENDING));
1837 modest_msg_view_window_first_message_selected (ModestMsgViewWindow *window)
1840 ModestMsgViewWindowPrivate *priv;
1841 gboolean is_first_selected;
1842 GtkTreeIter tmp_iter;
1844 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), TRUE);
1845 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1847 /*if no model (so no rows at all), then virtually we are the first*/
1848 if (!priv->header_model || !priv->row_reference)
1851 if (!gtk_tree_row_reference_valid (priv->row_reference))
1854 path = gtk_tree_row_reference_get_path (priv->row_reference);
1858 is_first_selected = TRUE;
1859 while (is_first_selected) {
1861 if(!gtk_tree_path_prev (path))
1863 /* Here the 'if' is needless for logic, but let make sure
1864 * iter is valid for gtk_tree_model_get. */
1865 if (!gtk_tree_model_get_iter (priv->header_model, &tmp_iter, path))
1867 gtk_tree_model_get (priv->header_model, &tmp_iter,
1868 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
1871 if (msg_is_visible (header, priv->is_outbox))
1872 is_first_selected = FALSE;
1873 g_object_unref(G_OBJECT(header));
1876 gtk_tree_path_free (path);
1877 return is_first_selected;
1884 GtkTreeRowReference *row_reference;
1888 message_reader_performer (gboolean canceled,
1890 GtkWindow *parent_window,
1891 TnyAccount *account,
1894 ModestMailOperation *mail_op = NULL;
1895 MsgReaderInfo *info;
1897 info = (MsgReaderInfo *) user_data;
1898 if (canceled || err) {
1899 update_window_title (MODEST_MSG_VIEW_WINDOW (parent_window));
1903 /* Register the header - it'll be unregistered in the callback */
1905 modest_window_mgr_register_header (modest_runtime_get_window_mgr (), info->header, NULL);
1907 /* New mail operation */
1908 mail_op = modest_mail_operation_new_with_error_handling (G_OBJECT(parent_window),
1909 modest_ui_actions_disk_operations_error_handler,
1912 modest_mail_operation_queue_add (modest_runtime_get_mail_operation_queue (), mail_op);
1914 modest_mail_operation_get_msg (mail_op, info->header, TRUE, view_msg_cb, info->row_reference);
1916 modest_mail_operation_find_msg (mail_op, info->folder, info->msg_uid, TRUE, view_msg_cb, NULL);
1917 g_object_unref (mail_op);
1919 /* Update dimming rules */
1920 modest_ui_actions_check_toolbar_dimming_rules (MODEST_WINDOW (parent_window));
1921 modest_ui_actions_check_menu_dimming_rules (MODEST_WINDOW (parent_window));
1924 /* Frees. The row_reference will be freed by the view_msg_cb callback */
1925 g_free (info->msg_uid);
1927 g_object_unref (info->folder);
1929 g_object_unref (info->header);
1930 g_slice_free (MsgReaderInfo, info);
1935 * Reads the message whose summary item is @header. It takes care of
1936 * several things, among others:
1938 * If the message was not previously downloaded then ask the user
1939 * before downloading. If there is no connection launch the connection
1940 * dialog. Update toolbar dimming rules.
1942 * Returns: TRUE if the mail operation was started, otherwise if the
1943 * user do not want to download the message, or if the user do not
1944 * want to connect, then the operation is not issued
1947 message_reader (ModestMsgViewWindow *window,
1948 ModestMsgViewWindowPrivate *priv,
1950 const gchar *msg_uid,
1952 GtkTreeRowReference *row_reference)
1954 ModestWindowMgr *mgr;
1955 TnyAccount *account = NULL;
1956 MsgReaderInfo *info;
1958 /* We set the header from model while we're loading */
1959 tny_header_view_set_header (TNY_HEADER_VIEW (priv->msg_view), header);
1960 modest_window_set_title (MODEST_WINDOW (window), _CS("ckdg_pb_updating"));
1966 g_object_ref (folder);
1968 mgr = modest_runtime_get_window_mgr ();
1969 /* Msg download completed */
1970 if (!header || !(tny_header_get_flags (header) & TNY_HEADER_FLAG_CACHED)) {
1972 /* Ask the user if he wants to download the message if
1974 if (!tny_device_is_online (modest_runtime_get_device())) {
1975 GtkResponseType response;
1977 response = modest_platform_run_confirmation_dialog (GTK_WINDOW (window),
1978 _("mcen_nc_get_msg"));
1979 if (response == GTK_RESPONSE_CANCEL) {
1980 update_window_title (window);
1985 folder = tny_header_get_folder (header);
1987 info = g_slice_new (MsgReaderInfo);
1988 info->msg_uid = g_strdup (msg_uid);
1990 info->header = g_object_ref (header);
1992 info->header = NULL;
1994 info->folder = g_object_ref (folder);
1996 info->folder = NULL;
1997 if (row_reference) {
1998 info->row_reference = gtk_tree_row_reference_copy (row_reference);
2000 info->row_reference = NULL;
2003 /* Offer the connection dialog if necessary */
2004 modest_platform_connect_if_remote_and_perform ((GtkWindow *) window,
2006 TNY_FOLDER_STORE (folder),
2007 message_reader_performer,
2010 g_object_unref (folder);
2016 folder = tny_header_get_folder (header);
2019 account = tny_folder_get_account (folder);
2021 info = g_slice_new (MsgReaderInfo);
2022 info->msg_uid = g_strdup (msg_uid);
2024 info->folder = g_object_ref (folder);
2026 info->folder = NULL;
2028 info->header = g_object_ref (header);
2030 info->header = NULL;
2032 info->row_reference = gtk_tree_row_reference_copy (row_reference);
2034 info->row_reference = NULL;
2036 message_reader_performer (FALSE, NULL, (GtkWindow *) window, account, info);
2038 g_object_unref (account);
2040 g_object_unref (folder);
2046 modest_msg_view_window_select_next_message (ModestMsgViewWindow *window)
2048 ModestMsgViewWindowPrivate *priv;
2049 GtkTreePath *path= NULL;
2050 GtkTreeIter tmp_iter;
2052 gboolean retval = TRUE;
2053 GtkTreeRowReference *row_reference = NULL;
2055 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), FALSE);
2056 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
2058 if (!priv->row_reference)
2061 /* Update the next row reference if it's not valid. This could
2062 happen if for example the header which it was pointing to,
2063 was deleted. The best place to do it is in the row-deleted
2064 handler but the tinymail model do not work like the glib
2065 tree models and reports the deletion when the row is still
2067 if (!gtk_tree_row_reference_valid (priv->next_row_reference)) {
2068 if (priv->next_row_reference) {
2069 gtk_tree_row_reference_free (priv->next_row_reference);
2071 if (gtk_tree_row_reference_valid (priv->row_reference)) {
2072 priv->next_row_reference = gtk_tree_row_reference_copy (priv->row_reference);
2073 select_next_valid_row (priv->header_model, &(priv->next_row_reference), FALSE, priv->is_outbox);
2075 priv->next_row_reference = NULL;
2078 if (priv->next_row_reference)
2079 path = gtk_tree_row_reference_get_path (priv->next_row_reference);
2083 row_reference = gtk_tree_row_reference_copy (priv->next_row_reference);
2085 gtk_tree_model_get_iter (priv->header_model,
2088 gtk_tree_path_free (path);
2090 gtk_tree_model_get (priv->header_model, &tmp_iter,
2091 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
2094 /* Read the message & show it */
2095 if (!message_reader (window, priv, header, NULL, NULL, row_reference)) {
2098 gtk_tree_row_reference_free (row_reference);
2101 g_object_unref (header);
2107 modest_msg_view_window_select_previous_message (ModestMsgViewWindow *window)
2109 ModestMsgViewWindowPrivate *priv = NULL;
2111 gboolean finished = FALSE;
2112 gboolean retval = FALSE;
2114 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), FALSE);
2115 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
2117 if (priv->row_reference && !gtk_tree_row_reference_valid (priv->row_reference)) {
2118 gtk_tree_row_reference_free (priv->row_reference);
2119 priv->row_reference = NULL;
2122 /* Return inmediatly if there is no header model */
2123 if (!priv->header_model || !priv->row_reference)
2126 path = gtk_tree_row_reference_get_path (priv->row_reference);
2127 while (!finished && gtk_tree_path_prev (path)) {
2131 gtk_tree_model_get_iter (priv->header_model, &iter, path);
2132 gtk_tree_model_get (priv->header_model, &iter,
2133 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
2137 if (msg_is_visible (header, priv->is_outbox)) {
2138 GtkTreeRowReference *row_reference;
2139 row_reference = gtk_tree_row_reference_new (priv->header_model, path);
2140 /* Read the message & show it */
2141 retval = message_reader (window, priv, header, NULL, NULL, row_reference);
2142 gtk_tree_row_reference_free (row_reference);
2146 g_object_unref (header);
2150 gtk_tree_path_free (path);
2155 view_msg_cb (ModestMailOperation *mail_op,
2162 ModestMsgViewWindow *self = NULL;
2163 ModestMsgViewWindowPrivate *priv = NULL;
2164 GtkTreeRowReference *row_reference = NULL;
2166 /* Unregister the header (it was registered before creating the mail operation) */
2167 modest_window_mgr_unregister_header (modest_runtime_get_window_mgr (), header);
2169 row_reference = (GtkTreeRowReference *) user_data;
2172 gtk_tree_row_reference_free (row_reference);
2173 self = (ModestMsgViewWindow *) modest_mail_operation_get_source (mail_op);
2175 /* Restore window title */
2176 update_window_title (self);
2177 modest_ui_actions_on_close_window (NULL, MODEST_WINDOW (self));
2178 g_object_unref (self);
2183 /* If there was any error */
2184 if (!modest_ui_actions_msg_retrieval_check (mail_op, header, msg)) {
2186 gtk_tree_row_reference_free (row_reference);
2187 self = (ModestMsgViewWindow *) modest_mail_operation_get_source (mail_op);
2189 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
2190 /* First we check if the parent is a folder window */
2191 if (priv->msg_uid && !modest_window_mgr_get_folder_window (MODEST_WINDOW_MGR (modest_runtime_get_window_mgr ()))) {
2193 TnyAccount *account = NULL;
2194 GtkWidget *header_window = NULL;
2196 is_merge = g_str_has_prefix (priv->msg_uid, "merge:");
2198 /* Get the account */
2200 account = tny_account_store_find_account (TNY_ACCOUNT_STORE (modest_runtime_get_account_store ()),
2203 if (is_merge || account) {
2204 TnyFolder *folder = NULL;
2206 /* Try to get the message, if it's already downloaded
2207 we don't need to connect */
2209 folder = modest_tny_folder_store_find_folder_from_uri (TNY_FOLDER_STORE (account),
2212 ModestTnyAccountStore *account_store;
2213 ModestTnyLocalFoldersAccount *local_folders_account;
2215 account_store = modest_runtime_get_account_store ();
2216 local_folders_account = MODEST_TNY_LOCAL_FOLDERS_ACCOUNT (
2217 modest_tny_account_store_get_local_folders_account (account_store));
2218 folder = modest_tny_local_folders_account_get_merged_outbox (local_folders_account);
2219 g_object_unref (local_folders_account);
2221 if (account) g_object_unref (account);
2224 header_window = (GtkWidget *)
2225 modest_header_window_new (
2227 modest_window_get_active_account (MODEST_WINDOW (self)),
2228 modest_window_get_active_mailbox (MODEST_WINDOW (self)));
2229 if (!modest_window_mgr_register_window (modest_runtime_get_window_mgr (),
2230 MODEST_WINDOW (header_window),
2232 gtk_widget_destroy (GTK_WIDGET (header_window));
2234 gtk_widget_show_all (GTK_WIDGET (header_window));
2236 g_object_unref (folder);
2242 /* Restore window title */
2243 update_window_title (self);
2244 modest_ui_actions_on_close_window (NULL, MODEST_WINDOW (self));
2245 g_object_unref (self);
2250 /* Get the window */
2251 self = (ModestMsgViewWindow *) modest_mail_operation_get_source (mail_op);
2252 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self));
2253 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
2255 /* Update the row reference */
2256 if (priv->row_reference != NULL) {
2257 gtk_tree_row_reference_free (priv->row_reference);
2258 priv->row_reference = (row_reference && gtk_tree_row_reference_valid (row_reference))?gtk_tree_row_reference_copy (row_reference):NULL;
2259 if (priv->next_row_reference != NULL) {
2260 gtk_tree_row_reference_free (priv->next_row_reference);
2262 if (priv->row_reference) {
2263 priv->next_row_reference = gtk_tree_row_reference_copy (priv->row_reference);
2264 select_next_valid_row (priv->header_model, &(priv->next_row_reference), TRUE, priv->is_outbox);
2266 priv->next_row_reference = NULL;
2270 /* Mark header as read */
2271 if (!(tny_header_get_flags (header) & TNY_HEADER_FLAG_SEEN))
2272 tny_header_set_flag (header, TNY_HEADER_FLAG_SEEN);
2274 /* Set new message */
2275 if (priv->msg_view != NULL && TNY_IS_MSG_VIEW (priv->msg_view)) {
2276 tny_msg_view_set_msg (TNY_MSG_VIEW (priv->msg_view), msg);
2277 modest_msg_view_window_update_priority (self);
2278 update_window_title (MODEST_MSG_VIEW_WINDOW (self));
2279 update_branding (MODEST_MSG_VIEW_WINDOW (self));
2280 modest_msg_view_grab_focus (MODEST_MSG_VIEW (priv->msg_view));
2283 /* Set the new message uid of the window */
2284 if (priv->msg_uid) {
2285 g_free (priv->msg_uid);
2286 priv->msg_uid = modest_tny_folder_get_header_unique_id (header);
2289 /* Notify the observers */
2290 g_signal_emit (G_OBJECT (self), signals[MSG_CHANGED_SIGNAL],
2291 0, priv->header_model, priv->row_reference);
2293 /* Sync the flags if the message is not opened from a header
2294 model, i.e, if it's opened from a notification */
2295 if (!priv->header_model)
2299 g_object_unref (self);
2301 gtk_tree_row_reference_free (row_reference);
2305 modest_msg_view_window_get_folder_type (ModestMsgViewWindow *window)
2307 ModestMsgViewWindowPrivate *priv;
2309 TnyFolderType folder_type;
2311 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
2313 folder_type = TNY_FOLDER_TYPE_UNKNOWN;
2315 msg = tny_msg_view_get_msg (TNY_MSG_VIEW (priv->msg_view));
2319 folder = tny_msg_get_folder (msg);
2321 folder_type = modest_tny_folder_guess_folder_type (folder);
2322 g_object_unref (folder);
2324 g_object_unref (msg);
2332 modest_msg_view_window_update_priority (ModestMsgViewWindow *window)
2334 ModestMsgViewWindowPrivate *priv;
2335 TnyHeader *header = NULL;
2336 TnyHeaderFlags flags = 0;
2338 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
2340 if (priv->header_model && priv->row_reference && gtk_tree_row_reference_valid (priv->row_reference)) {
2342 GtkTreePath *path = NULL;
2344 path = gtk_tree_row_reference_get_path (priv->row_reference);
2345 g_return_if_fail (path != NULL);
2346 gtk_tree_model_get_iter (priv->header_model,
2348 gtk_tree_row_reference_get_path (priv->row_reference));
2350 gtk_tree_model_get (priv->header_model, &iter, TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
2352 gtk_tree_path_free (path);
2355 msg = tny_msg_view_get_msg (TNY_MSG_VIEW (priv->msg_view));
2357 header = tny_msg_get_header (msg);
2358 g_object_unref (msg);
2363 flags = tny_header_get_flags (header);
2364 g_object_unref(G_OBJECT(header));
2367 modest_msg_view_set_priority (MODEST_MSG_VIEW(priv->msg_view), flags);
2372 toolbar_resize (ModestMsgViewWindow *self)
2374 ModestMsgViewWindowPrivate *priv = NULL;
2375 ModestWindowPrivate *parent_priv = NULL;
2377 gint static_button_size;
2378 ModestWindowMgr *mgr;
2380 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self));
2381 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
2382 parent_priv = MODEST_WINDOW_GET_PRIVATE(self);
2384 mgr = modest_runtime_get_window_mgr ();
2385 static_button_size = modest_window_mgr_get_fullscreen_mode (mgr)?120:120;
2387 if (parent_priv->toolbar) {
2388 /* Set expandable and homogeneous tool buttons */
2389 widget = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/ToolbarMessageReply");
2390 gtk_tool_item_set_expand (GTK_TOOL_ITEM (widget), TRUE);
2391 gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (widget), TRUE);
2392 widget = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/ToolbarMessageReplyAll");
2393 gtk_tool_item_set_expand (GTK_TOOL_ITEM (widget), TRUE);
2394 gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (widget), TRUE);
2395 widget = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/ToolbarMessageForward");
2396 gtk_tool_item_set_expand (GTK_TOOL_ITEM (widget), TRUE);
2397 gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (widget), TRUE);
2398 widget = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/ToolbarDeleteMessage");
2399 gtk_tool_item_set_expand (GTK_TOOL_ITEM (widget), TRUE);
2400 gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (widget), TRUE);
2401 widget = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/ToolbarDownloadExternalImages");
2402 gtk_tool_item_set_expand (GTK_TOOL_ITEM (widget), TRUE);
2403 gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (widget), TRUE);
2404 gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (priv->next_toolitem), TRUE);
2405 gtk_tool_item_set_expand (GTK_TOOL_ITEM (priv->next_toolitem), TRUE);
2406 gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (priv->prev_toolitem), TRUE);
2407 gtk_tool_item_set_expand (GTK_TOOL_ITEM (priv->prev_toolitem), TRUE);
2412 modest_msg_view_window_show_toolbar (ModestWindow *self,
2413 gboolean show_toolbar)
2415 ModestMsgViewWindowPrivate *priv = NULL;
2416 ModestWindowPrivate *parent_priv;
2418 parent_priv = MODEST_WINDOW_GET_PRIVATE(self);
2419 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(self);
2421 /* Set optimized view status */
2422 priv->optimized_view = !show_toolbar;
2424 if (!parent_priv->toolbar) {
2425 parent_priv->toolbar = gtk_ui_manager_get_widget (parent_priv->ui_manager,
2428 /* We don't use HILDON_ICON_SIZE_FINGER in order to avoid the ifdef here */
2429 gtk_toolbar_set_icon_size (GTK_TOOLBAR (parent_priv->toolbar), gtk_icon_size_from_name ("hildon-finger"));
2430 gtk_widget_set_no_show_all (parent_priv->toolbar, TRUE);
2432 priv->next_toolitem = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/ToolbarMessageNext");
2433 priv->prev_toolitem = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/ToolbarMessageBack");
2434 toolbar_resize (MODEST_MSG_VIEW_WINDOW (self));
2436 modest_window_add_toolbar (MODEST_WINDOW (self),
2437 GTK_TOOLBAR (parent_priv->toolbar));
2442 /* Quick hack: this prevents toolbar icons "dance" when progress bar show status is changed */
2443 /* TODO: resize mode migth be GTK_RESIZE_QUEUE, in order to avoid unneccesary shows */
2444 gtk_container_set_resize_mode (GTK_CONTAINER(parent_priv->toolbar), GTK_RESIZE_IMMEDIATE);
2446 gtk_widget_show (GTK_WIDGET (parent_priv->toolbar));
2447 if (modest_msg_view_window_transfer_mode_enabled (MODEST_MSG_VIEW_WINDOW (self)))
2448 set_progress_hint (MODEST_MSG_VIEW_WINDOW (self), TRUE);
2450 set_progress_hint (MODEST_MSG_VIEW_WINDOW (self), FALSE);
2453 gtk_widget_set_no_show_all (parent_priv->toolbar, TRUE);
2454 gtk_widget_hide (GTK_WIDGET (parent_priv->toolbar));
2459 modest_msg_view_window_clipboard_owner_change (GtkClipboard *clipboard,
2461 ModestMsgViewWindow *window)
2463 if (!GTK_WIDGET_VISIBLE (window))
2466 modest_window_check_dimming_rules_group (MODEST_WINDOW (window), MODEST_DIMMING_RULES_CLIPBOARD);
2470 modest_msg_view_window_transfer_mode_enabled (ModestMsgViewWindow *self)
2472 ModestMsgViewWindowPrivate *priv;
2474 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self), FALSE);
2475 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(self);
2477 return priv->progress_hint;
2481 observers_empty (ModestMsgViewWindow *self)
2484 ModestMsgViewWindowPrivate *priv;
2485 gboolean is_empty = TRUE;
2486 guint pending_ops = 0;
2488 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(self);
2489 tmp = priv->progress_widgets;
2491 /* Check all observers */
2492 while (tmp && is_empty) {
2493 pending_ops = modest_progress_object_num_pending_operations (MODEST_PROGRESS_OBJECT(tmp->data));
2494 is_empty = pending_ops == 0;
2496 tmp = g_slist_next(tmp);
2503 on_account_removed (TnyAccountStore *account_store,
2504 TnyAccount *account,
2507 /* Do nothing if it's a transport account, because we only
2508 show the messages of a store account */
2509 if (tny_account_get_account_type(account) == TNY_ACCOUNT_TYPE_STORE) {
2510 const gchar *parent_acc = NULL;
2511 const gchar *our_acc = NULL;
2513 our_acc = modest_window_get_active_account (MODEST_WINDOW (user_data));
2514 parent_acc = modest_tny_account_get_parent_modest_account_name_for_server_account (account);
2516 /* Close this window if I'm showing a message of the removed account */
2517 if (our_acc && parent_acc && strcmp (parent_acc, our_acc) == 0)
2518 modest_ui_actions_on_close_window (NULL, MODEST_WINDOW (user_data));
2523 on_mail_operation_started (ModestMailOperation *mail_op,
2526 ModestMsgViewWindow *self;
2527 ModestMailOperationTypeOperation op_type;
2529 ModestMsgViewWindowPrivate *priv;
2530 GObject *source = NULL;
2532 self = MODEST_MSG_VIEW_WINDOW (user_data);
2533 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
2534 op_type = modest_mail_operation_get_type_operation (mail_op);
2535 tmp = priv->progress_widgets;
2536 source = modest_mail_operation_get_source(mail_op);
2537 if (G_OBJECT (self) == source) {
2538 if (op_type == MODEST_MAIL_OPERATION_TYPE_RECEIVE ||
2539 op_type == MODEST_MAIL_OPERATION_TYPE_OPEN ||
2540 op_type == MODEST_MAIL_OPERATION_TYPE_DELETE) {
2541 set_progress_hint (self, TRUE);
2543 modest_progress_object_add_operation (
2544 MODEST_PROGRESS_OBJECT (tmp->data),
2546 tmp = g_slist_next (tmp);
2550 g_object_unref (source);
2552 /* Update dimming rules */
2553 check_dimming_rules_after_change (self);
2557 on_mail_operation_finished (ModestMailOperation *mail_op,
2560 ModestMsgViewWindow *self;
2561 ModestMailOperationTypeOperation op_type;
2563 ModestMsgViewWindowPrivate *priv;
2565 self = MODEST_MSG_VIEW_WINDOW (user_data);
2566 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
2567 op_type = modest_mail_operation_get_type_operation (mail_op);
2568 tmp = priv->progress_widgets;
2570 if (op_type == MODEST_MAIL_OPERATION_TYPE_RECEIVE ||
2571 op_type == MODEST_MAIL_OPERATION_TYPE_OPEN ||
2572 op_type == MODEST_MAIL_OPERATION_TYPE_DELETE) {
2574 modest_progress_object_remove_operation (MODEST_PROGRESS_OBJECT (tmp->data),
2576 tmp = g_slist_next (tmp);
2579 /* If no more operations are being observed, NORMAL mode is enabled again */
2580 if (observers_empty (self)) {
2581 set_progress_hint (self, FALSE);
2585 /* Update dimming rules. We have to do this right here
2586 and not in view_msg_cb because at that point the
2587 transfer mode is still enabled so the dimming rule
2588 won't let the user delete the message that has been
2589 readed for example */
2590 check_dimming_rules_after_change (self);
2594 on_queue_changed (ModestMailOperationQueue *queue,
2595 ModestMailOperation *mail_op,
2596 ModestMailOperationQueueNotification type,
2597 ModestMsgViewWindow *self)
2599 ModestMsgViewWindowPrivate *priv;
2601 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
2603 /* If this operations was created by another window, do nothing */
2604 if (!modest_mail_operation_is_mine (mail_op, G_OBJECT(self)))
2607 if (type == MODEST_MAIL_OPERATION_QUEUE_OPERATION_ADDED) {
2608 priv->sighandlers = modest_signal_mgr_connect (priv->sighandlers,
2610 "operation-started",
2611 G_CALLBACK (on_mail_operation_started),
2613 priv->sighandlers = modest_signal_mgr_connect (priv->sighandlers,
2615 "operation-finished",
2616 G_CALLBACK (on_mail_operation_finished),
2618 } else if (type == MODEST_MAIL_OPERATION_QUEUE_OPERATION_REMOVED) {
2619 priv->sighandlers = modest_signal_mgr_disconnect (priv->sighandlers,
2621 "operation-started");
2622 priv->sighandlers = modest_signal_mgr_disconnect (priv->sighandlers,
2624 "operation-finished");
2629 modest_msg_view_window_get_attachments (ModestMsgViewWindow *win)
2631 ModestMsgViewWindowPrivate *priv;
2632 TnyList *selected_attachments = NULL;
2634 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (win), NULL);
2635 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (win);
2637 /* In Hildon 2.2 as there's no selection we assume we have all attachments selected */
2638 selected_attachments = modest_msg_view_get_attachments (MODEST_MSG_VIEW (priv->msg_view));
2640 return selected_attachments;
2644 ModestMsgViewWindow *self;
2646 gchar *attachment_uid;
2647 } DecodeAsyncHelper;
2650 on_decode_to_stream_async_handler (TnyMimePart *mime_part,
2656 DecodeAsyncHelper *helper = (DecodeAsyncHelper *) user_data;
2657 const gchar *content_type;
2659 if (cancelled || err) {
2662 if ((err->domain == TNY_ERROR_DOMAIN) &&
2663 (err->code == TNY_IO_ERROR_WRITE) &&
2664 (errno == ENOSPC)) {
2665 msg = g_strdup_printf (_KR("cerm_device_memory_full"), "");
2667 msg = g_strdup (_("mail_ib_file_operation_failed"));
2669 modest_platform_information_banner (NULL, NULL, msg);
2675 /* It could happen that the window was closed. So we
2676 assume it is a cancelation */
2677 if (!GTK_WIDGET_VISIBLE (helper->self))
2680 /* Remove the progress hint */
2681 set_progress_hint (helper->self, FALSE);
2683 content_type = tny_mime_part_get_content_type (mime_part);
2684 if (g_str_has_prefix (content_type, "message/rfc822")) {
2685 ModestWindowMgr *mgr;
2686 ModestWindow *msg_win = NULL;
2689 const gchar *mailbox;
2690 TnyStream *file_stream;
2693 fd = g_open (helper->file_path, O_RDONLY, 0644);
2695 file_stream = tny_fs_stream_new (fd);
2697 mgr = modest_runtime_get_window_mgr ();
2699 account = g_strdup (modest_window_get_active_account (MODEST_WINDOW (helper->self)));
2700 mailbox = modest_window_get_active_mailbox (MODEST_WINDOW (helper->self));
2703 account = modest_account_mgr_get_default_account (modest_runtime_get_account_mgr ());
2705 msg = tny_camel_msg_new ();
2706 tny_camel_msg_parse (msg, file_stream);
2707 msg_win = modest_msg_view_window_new_for_attachment (TNY_MSG (msg), account, mailbox, helper->attachment_uid);
2708 modest_window_set_zoom (MODEST_WINDOW (msg_win),
2709 modest_window_get_zoom (MODEST_WINDOW (helper->self)));
2710 if (modest_window_mgr_register_window (mgr, msg_win, MODEST_WINDOW (helper->self)))
2711 gtk_widget_show_all (GTK_WIDGET (msg_win));
2713 gtk_widget_destroy (GTK_WIDGET (msg_win));
2714 g_object_unref (msg);
2715 g_object_unref (file_stream);
2717 modest_platform_information_banner (NULL, NULL, _("mail_ib_file_operation_failed"));
2722 /* make the file read-only */
2723 g_chmod(helper->file_path, 0444);
2725 /* Activate the file */
2726 modest_platform_activate_file (helper->file_path, tny_mime_part_get_content_type (mime_part));
2731 g_object_unref (helper->self);
2732 g_free (helper->file_path);
2733 g_free (helper->attachment_uid);
2734 g_slice_free (DecodeAsyncHelper, helper);
2738 modest_msg_view_window_view_attachment (ModestMsgViewWindow *window,
2739 TnyMimePart *mime_part)
2741 ModestMsgViewWindowPrivate *priv;
2742 const gchar *msg_uid;
2743 gchar *attachment_uid = NULL;
2744 gint attachment_index = 0;
2745 TnyList *attachments;
2747 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window));
2748 g_return_if_fail (TNY_IS_MIME_PART (mime_part) || (mime_part == NULL));
2749 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
2751 msg_uid = modest_msg_view_window_get_message_uid (MODEST_MSG_VIEW_WINDOW (window));
2752 attachments = modest_msg_view_get_attachments (MODEST_MSG_VIEW (priv->msg_view));
2753 attachment_index = modest_list_index (attachments, (GObject *) mime_part);
2754 g_object_unref (attachments);
2756 if (msg_uid && attachment_index >= 0) {
2757 attachment_uid = g_strdup_printf ("%s/%d", msg_uid, attachment_index);
2760 if (mime_part == NULL) {
2761 gboolean error = FALSE;
2762 TnyList *selected_attachments = modest_msg_view_get_selected_attachments (MODEST_MSG_VIEW (priv->msg_view));
2763 if (selected_attachments == NULL || tny_list_get_length (selected_attachments) == 0) {
2765 } else if (tny_list_get_length (selected_attachments) > 1) {
2766 modest_platform_system_banner (NULL, NULL, _("mcen_ib_unable_to_display_more"));
2770 iter = tny_list_create_iterator (selected_attachments);
2771 mime_part = (TnyMimePart *) tny_iterator_get_current (iter);
2772 g_object_unref (iter);
2774 if (selected_attachments)
2775 g_object_unref (selected_attachments);
2780 g_object_ref (mime_part);
2783 if (tny_mime_part_is_purged (mime_part))
2786 if (!modest_tny_mime_part_is_msg (mime_part) && tny_mime_part_get_filename (mime_part)) {
2787 gchar *filepath = NULL;
2788 const gchar *att_filename = tny_mime_part_get_filename (mime_part);
2789 gboolean show_error_banner = FALSE;
2790 TnyFsStream *temp_stream = NULL;
2791 temp_stream = modest_utils_create_temp_stream (att_filename, attachment_uid,
2794 if (temp_stream != NULL) {
2795 ModestAccountMgr *mgr;
2796 DecodeAsyncHelper *helper;
2797 gboolean decode_in_provider;
2798 ModestProtocol *protocol;
2799 const gchar *account;
2801 /* Activate progress hint */
2802 set_progress_hint (window, TRUE);
2804 helper = g_slice_new0 (DecodeAsyncHelper);
2805 helper->self = g_object_ref (window);
2806 helper->file_path = g_strdup (filepath);
2807 helper->attachment_uid = g_strdup (attachment_uid);
2809 decode_in_provider = FALSE;
2810 mgr = modest_runtime_get_account_mgr ();
2811 account = modest_window_get_active_account (MODEST_WINDOW (window));
2812 if (modest_account_mgr_account_is_multimailbox (mgr, account, &protocol)) {
2813 if (MODEST_IS_ACCOUNT_PROTOCOL (protocol)) {
2815 uri = g_strconcat ("file://", filepath, NULL);
2816 decode_in_provider =
2817 modest_account_protocol_decode_part_to_stream_async (
2818 MODEST_ACCOUNT_PROTOCOL (protocol),
2821 TNY_STREAM (temp_stream),
2822 on_decode_to_stream_async_handler,
2829 if (!decode_in_provider)
2830 tny_mime_part_decode_to_stream_async (mime_part, TNY_STREAM (temp_stream),
2831 on_decode_to_stream_async_handler,
2834 g_object_unref (temp_stream);
2835 /* NOTE: files in the temporary area will be automatically
2836 * cleaned after some time if they are no longer in use */
2839 const gchar *content_type;
2840 /* the file may already exist but it isn't writable,
2841 * let's try to open it anyway */
2842 content_type = tny_mime_part_get_content_type (mime_part);
2843 modest_platform_activate_file (filepath, content_type);
2845 g_warning ("%s: modest_utils_create_temp_stream failed", __FUNCTION__);
2846 show_error_banner = TRUE;
2851 if (show_error_banner)
2852 modest_platform_information_banner (NULL, NULL, _("mail_ib_file_operation_failed"));
2853 } else if (!modest_tny_mime_part_is_msg (mime_part)) {
2854 ModestWindowMgr *mgr;
2855 ModestWindow *msg_win = NULL;
2856 TnyMsg *current_msg;
2860 current_msg = modest_msg_view_window_get_message (MODEST_MSG_VIEW_WINDOW (window));
2861 mgr = modest_runtime_get_window_mgr ();
2862 header = tny_msg_get_header (TNY_MSG (current_msg));
2863 found = modest_window_mgr_find_registered_message_uid (mgr,
2868 g_debug ("window for this body is already being created");
2871 /* it's not found, so create a new window for it */
2872 modest_window_mgr_register_header (mgr, header, attachment_uid); /* register the uid before building the window */
2873 gchar *account = g_strdup (modest_window_get_active_account (MODEST_WINDOW (window)));
2874 const gchar *mailbox = modest_window_get_active_mailbox (MODEST_WINDOW (window));
2876 account = modest_account_mgr_get_default_account (modest_runtime_get_account_mgr ());
2878 msg_win = modest_msg_view_window_new_with_other_body (TNY_MSG (current_msg), TNY_MIME_PART (mime_part),
2879 account, mailbox, attachment_uid);
2881 modest_window_set_zoom (MODEST_WINDOW (msg_win),
2882 modest_window_get_zoom (MODEST_WINDOW (window)));
2883 if (modest_window_mgr_register_window (mgr, msg_win, MODEST_WINDOW (window)))
2884 gtk_widget_show_all (GTK_WIDGET (msg_win));
2886 gtk_widget_destroy (GTK_WIDGET (msg_win));
2888 g_object_unref (current_msg);
2890 /* message attachment */
2891 TnyHeader *header = NULL;
2892 ModestWindowMgr *mgr;
2893 ModestWindow *msg_win = NULL;
2896 header = tny_msg_get_header (TNY_MSG (mime_part));
2897 mgr = modest_runtime_get_window_mgr ();
2898 found = modest_window_mgr_find_registered_header (mgr, header, &msg_win);
2901 /* if it's found, but there is no msg_win, it's probably in the process of being created;
2902 * thus, we don't do anything */
2903 g_debug ("window for is already being created");
2905 /* it's not found, so create a new window for it */
2906 modest_window_mgr_register_header (mgr, header, attachment_uid); /* register the uid before building the window */
2907 gchar *account = g_strdup (modest_window_get_active_account (MODEST_WINDOW (window)));
2908 const gchar *mailbox = modest_window_get_active_mailbox (MODEST_WINDOW (window));
2910 account = modest_account_mgr_get_default_account (modest_runtime_get_account_mgr ());
2911 msg_win = modest_msg_view_window_new_for_attachment (TNY_MSG (mime_part), account,
2912 mailbox, attachment_uid);
2913 modest_window_set_zoom (MODEST_WINDOW (msg_win),
2914 modest_window_get_zoom (MODEST_WINDOW (window)));
2915 if (modest_window_mgr_register_window (mgr, msg_win, MODEST_WINDOW (window)))
2916 gtk_widget_show_all (GTK_WIDGET (msg_win));
2918 gtk_widget_destroy (GTK_WIDGET (msg_win));
2924 g_free (attachment_uid);
2926 g_object_unref (mime_part);
2938 GnomeVFSResult result;
2940 ModestMsgViewWindow *window;
2943 static void save_mime_part_info_free (SaveMimePartInfo *info, gboolean with_struct);
2944 static gboolean idle_save_mime_part_show_result (SaveMimePartInfo *info);
2945 static gpointer save_mime_part_to_file (SaveMimePartInfo *info);
2946 static void save_mime_parts_to_file_with_checks (GtkWindow *parent, SaveMimePartInfo *info);
2949 save_mime_part_info_free (SaveMimePartInfo *info, gboolean with_struct)
2953 for (node = info->pairs; node != NULL; node = g_list_next (node)) {
2954 SaveMimePartPair *pair = (SaveMimePartPair *) node->data;
2955 g_free (pair->filename);
2956 g_object_unref (pair->part);
2957 g_slice_free (SaveMimePartPair, pair);
2959 g_list_free (info->pairs);
2962 g_object_unref (info->window);
2963 info->window = NULL;
2965 g_slice_free (SaveMimePartInfo, info);
2970 idle_save_mime_part_show_result (SaveMimePartInfo *info)
2972 /* This is a GDK lock because we are an idle callback and
2973 * modest_platform_system_banner is or does Gtk+ code */
2975 gdk_threads_enter (); /* CHECKED */
2976 if (info->result == GNOME_VFS_OK) {
2977 modest_platform_system_banner (NULL, NULL, _CS("sfil_ib_saved"));
2978 } else if (info->result == GNOME_VFS_ERROR_NO_SPACE) {
2981 /* Check if the uri belongs to the external mmc */
2982 if (g_str_has_prefix (info->uri, g_getenv (MODEST_MMC1_VOLUMEPATH_ENV)))
2983 msg = g_strdup_printf (_KR("cerm_device_memory_full"), "");
2985 msg = g_strdup (_KR("cerm_memory_card_full"));
2986 modest_platform_information_banner (NULL, NULL, msg);
2989 modest_platform_system_banner (NULL, NULL, _("mail_ib_file_operation_failed"));
2991 save_mime_part_info_free (info, FALSE);
2992 gdk_threads_leave (); /* CHECKED */
2998 save_mime_part_to_file (SaveMimePartInfo *info)
3000 GnomeVFSHandle *handle;
3002 SaveMimePartPair *pair = (SaveMimePartPair *) info->pairs->data;
3004 info->result = gnome_vfs_create (&handle, pair->filename, GNOME_VFS_OPEN_WRITE, FALSE, 0644);
3005 if (info->result == GNOME_VFS_OK) {
3006 GError *error = NULL;
3007 gboolean decode_in_provider;
3009 ModestAccountMgr *mgr;
3010 const gchar *account;
3011 ModestProtocol *protocol = NULL;
3013 stream = tny_vfs_stream_new (handle);
3015 decode_in_provider = FALSE;
3016 mgr = modest_runtime_get_account_mgr ();
3017 account = modest_window_get_active_account (MODEST_WINDOW (info->window));
3018 if (modest_account_mgr_account_is_multimailbox (mgr, account, &protocol)) {
3019 if (MODEST_IS_ACCOUNT_PROTOCOL (protocol)) {
3020 decode_in_provider =
3021 modest_account_protocol_decode_part_to_stream (
3022 MODEST_ACCOUNT_PROTOCOL (protocol),
3030 if (!decode_in_provider)
3031 written = tny_mime_part_decode_to_stream (pair->part, stream, &error);
3034 g_warning ("modest: could not save attachment %s: %d (%s)\n", pair->filename, error?error->code:-1, error?error->message:"Unknown error");
3036 if ((error->domain == TNY_ERROR_DOMAIN) &&
3037 (error->code == TNY_IO_ERROR_WRITE) &&
3038 (errno == ENOSPC)) {
3039 info->result = GNOME_VFS_ERROR_NO_SPACE;
3041 info->result = GNOME_VFS_ERROR_IO;
3044 g_object_unref (G_OBJECT (stream));
3046 g_warning ("Could not create save attachment %s: %s\n",
3047 pair->filename, gnome_vfs_result_to_string (info->result));
3050 /* Go on saving remaining files */
3051 info->pairs = g_list_remove_link (info->pairs, info->pairs);
3052 if (info->pairs != NULL) {
3053 save_mime_part_to_file (info);
3055 g_idle_add ((GSourceFunc) idle_save_mime_part_show_result, info);
3062 save_mime_parts_to_file_with_checks (GtkWindow *parent,
3063 SaveMimePartInfo *info)
3065 gboolean is_ok = TRUE;
3066 gint replaced_files = 0;
3067 const GList *files = info->pairs;
3068 const GList *iter, *to_replace = NULL;
3070 for (iter = files; (iter != NULL) && (replaced_files < 2); iter = g_list_next(iter)) {
3071 SaveMimePartPair *pair = iter->data;
3072 gchar *unescaped = g_uri_unescape_string (pair->filename, NULL);
3074 if (modest_utils_file_exists (unescaped)) {
3076 if (replaced_files == 1)
3081 if (replaced_files) {
3084 if (replaced_files == 1) {
3085 SaveMimePartPair *pair = to_replace->data;
3086 const gchar *basename = strrchr (pair->filename, G_DIR_SEPARATOR) + 1;
3087 gchar *escaped_basename, *message;
3089 escaped_basename = g_uri_unescape_string (basename, NULL);
3090 message = g_strdup_printf ("%s\n%s",
3091 _FM("docm_nc_replace_file"),
3092 (escaped_basename) ? escaped_basename : "");
3093 response = modest_platform_run_confirmation_dialog (parent, message);
3095 g_free (escaped_basename);
3097 response = modest_platform_run_confirmation_dialog (parent,
3098 _FM("docm_nc_replace_multiple"));
3100 if (response != GTK_RESPONSE_OK)
3105 save_mime_part_info_free (info, TRUE);
3107 g_thread_create ((GThreadFunc)save_mime_part_to_file, info, FALSE, NULL);
3112 typedef struct _SaveAttachmentsInfo {
3113 TnyList *attachments_list;
3114 ModestMsgViewWindow *window;
3115 } SaveAttachmentsInfo;
3118 save_attachments_response (GtkDialog *dialog,
3122 TnyList *mime_parts;
3124 GList *files_to_save = NULL;
3125 gchar *current_folder;
3126 SaveAttachmentsInfo *sa_info = (SaveAttachmentsInfo *) user_data;
3128 mime_parts = TNY_LIST (sa_info->attachments_list);
3130 if (arg1 != GTK_RESPONSE_OK)
3133 chooser_uri = gtk_file_chooser_get_uri (GTK_FILE_CHOOSER (dialog));
3134 current_folder = gtk_file_chooser_get_current_folder_uri (GTK_FILE_CHOOSER (dialog));
3135 if (current_folder && *current_folder != '\0') {
3137 modest_conf_set_string (modest_runtime_get_conf (), MODEST_CONF_LATEST_SAVE_ATTACHMENT_PATH,
3138 current_folder,&err);
3140 g_debug ("Error storing latest used folder: %s", err->message);
3144 g_free (current_folder);
3146 if (!modest_utils_folder_writable (chooser_uri)) {
3147 const gchar *err_msg;
3149 #ifdef MODEST_PLATFORM_MAEMO
3150 if (modest_maemo_utils_in_usb_mode ()) {
3151 err_msg = dgettext ("hildon-status-bar-usb", "usbh_ib_mmc_usb_connected");
3153 err_msg = _FM("sfil_ib_readonly_location");
3156 err_msg = _FM("sfil_ib_readonly_location");
3158 modest_platform_system_banner (NULL, NULL, err_msg);
3162 iter = tny_list_create_iterator (mime_parts);
3163 while (!tny_iterator_is_done (iter)) {
3164 TnyMimePart *mime_part = (TnyMimePart *) tny_iterator_get_current (iter);
3166 if ((modest_tny_mime_part_is_attachment_for_modest (mime_part)) &&
3167 !tny_mime_part_is_purged (mime_part) &&
3168 (tny_mime_part_get_filename (mime_part) != NULL)) {
3169 SaveMimePartPair *pair;
3171 pair = g_slice_new0 (SaveMimePartPair);
3173 if (tny_list_get_length (mime_parts) > 1) {
3175 gnome_vfs_escape_slashes (tny_mime_part_get_filename (mime_part));
3176 pair->filename = g_build_filename (chooser_uri, escaped, NULL);
3179 pair->filename = g_strdup (chooser_uri);
3181 pair->part = mime_part;
3182 files_to_save = g_list_prepend (files_to_save, pair);
3184 tny_iterator_next (iter);
3186 g_object_unref (iter);
3189 if (files_to_save != NULL) {
3190 SaveMimePartInfo *info = g_slice_new0 (SaveMimePartInfo);
3191 info->pairs = files_to_save;
3192 info->result = TRUE;
3193 info->uri = g_strdup (chooser_uri);
3194 info->window = g_object_ref (sa_info->window);
3195 save_mime_parts_to_file_with_checks ((GtkWindow *) dialog, info);
3197 g_free (chooser_uri);
3200 /* Free and close the dialog */
3201 g_object_unref (mime_parts);
3202 g_object_unref (sa_info->window);
3203 g_slice_free (SaveAttachmentsInfo, sa_info);
3204 gtk_widget_destroy (GTK_WIDGET (dialog));
3208 modest_msg_view_window_save_attachments (ModestMsgViewWindow *window,
3209 TnyList *mime_parts)
3211 ModestMsgViewWindowPrivate *priv;
3212 GtkWidget *save_dialog = NULL;
3213 gchar *conf_folder = NULL;
3214 gchar *filename = NULL;
3215 gchar *save_multiple_str = NULL;
3216 const gchar *root_folder = "file:///";
3218 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window));
3219 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
3221 if (mime_parts == NULL) {
3222 /* In Hildon 2.2 save and delete operate over all the attachments as there's no
3223 * selection available */
3224 mime_parts = modest_msg_view_get_attachments (MODEST_MSG_VIEW (priv->msg_view));
3225 if (mime_parts && !modest_toolkit_utils_select_attachments (GTK_WINDOW (window), mime_parts, FALSE)) {
3226 g_object_unref (mime_parts);
3229 if (mime_parts == NULL || tny_list_get_length (mime_parts) == 0) {
3231 g_object_unref (mime_parts);
3237 g_object_ref (mime_parts);
3240 /* prepare dialog */
3241 if (tny_list_get_length (mime_parts) == 1) {
3243 /* only one attachment selected */
3244 iter = tny_list_create_iterator (mime_parts);
3245 TnyMimePart *mime_part = (TnyMimePart *) tny_iterator_get_current (iter);
3246 g_object_unref (iter);
3247 if (!modest_tny_mime_part_is_msg (mime_part) &&
3248 modest_tny_mime_part_is_attachment_for_modest (mime_part) &&
3249 !tny_mime_part_is_purged (mime_part)) {
3250 filename = g_strdup (tny_mime_part_get_filename (mime_part));
3252 /* TODO: show any error? */
3253 g_warning ("%s: Tried to save a non-file attachment", __FUNCTION__);
3254 g_object_unref (mime_parts);
3257 g_object_unref (mime_part);
3259 gint num = tny_list_get_length (mime_parts);
3260 save_multiple_str = g_strdup_printf (dngettext("hildon-fm",
3261 "sfil_va_number_of_objects_attachment",
3262 "sfil_va_number_of_objects_attachments",
3266 /* Creation of hildon file chooser dialog for saving */
3267 save_dialog = modest_toolkit_factory_create_file_chooser_dialog (modest_runtime_get_toolkit_factory (),
3269 (GtkWindow *) window,
3270 GTK_FILE_CHOOSER_ACTION_SAVE);
3272 /* Get last used folder */
3273 conf_folder = modest_conf_get_string (modest_runtime_get_conf (),
3274 MODEST_CONF_LATEST_SAVE_ATTACHMENT_PATH, NULL);
3276 /* File chooser stops working if we select "file:///" as current folder */
3277 if (conf_folder && g_ascii_strcasecmp (root_folder, conf_folder) != 0) {
3278 g_free (conf_folder);
3282 if (conf_folder && conf_folder[0] != '\0') {
3283 gtk_file_chooser_set_current_folder_uri (GTK_FILE_CHOOSER (save_dialog), conf_folder);
3286 /* Set the default folder to documents folder */
3287 docs_folder = (gchar *) g_strdup(g_get_user_special_dir (G_USER_DIRECTORY_DOCUMENTS));
3290 docs_folder = g_build_filename (g_getenv (MYDOCS_ENV), DOCS_FOLDER, NULL);
3292 gtk_file_chooser_set_current_folder (GTK_FILE_CHOOSER (save_dialog), docs_folder);
3293 g_free (docs_folder);
3295 g_free (conf_folder);
3299 gtk_file_chooser_set_current_name (GTK_FILE_CHOOSER (save_dialog),
3304 /* if multiple, set multiple string */
3305 if (save_multiple_str) {
3306 g_object_set (G_OBJECT (save_dialog), "save-multiple", save_multiple_str, NULL);
3307 gtk_window_set_title (GTK_WINDOW (save_dialog), _FM("sfil_ti_save_objects_files"));
3308 g_free (save_multiple_str);
3311 /* We must run this asynchronously, because the hildon dialog
3312 performs a gtk_dialog_run by itself which leads to gdk
3314 SaveAttachmentsInfo *sa_info;
3315 sa_info = g_slice_new (SaveAttachmentsInfo);
3316 sa_info->attachments_list = mime_parts;
3317 sa_info->window = g_object_ref (window);
3318 g_signal_connect (save_dialog, "response",
3319 G_CALLBACK (save_attachments_response), sa_info);
3321 gtk_widget_show_all (save_dialog);
3325 show_remove_attachment_information (gpointer userdata)
3327 ModestMsgViewWindow *window = (ModestMsgViewWindow *) userdata;
3328 ModestMsgViewWindowPrivate *priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
3330 /* We're outside the main lock */
3331 gdk_threads_enter ();
3333 if (priv->remove_attachment_banner != NULL) {
3334 gtk_widget_destroy (priv->remove_attachment_banner);
3335 g_object_unref (priv->remove_attachment_banner);
3338 priv->remove_attachment_banner = g_object_ref (
3339 modest_platform_animation_banner (NULL, NULL, _("mcen_me_inbox_remove_attachments")));
3341 gdk_threads_leave ();
3347 modest_msg_view_window_remove_attachments (ModestMsgViewWindow *window, gboolean get_all)
3349 ModestMsgViewWindowPrivate *priv;
3350 TnyList *mime_parts = NULL, *tmp;
3351 gchar *confirmation_message;
3357 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window));
3358 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
3360 /* In hildon 2.2 we ignore the get_all flag as we always get all attachments. This is
3361 * because we don't have selection
3363 mime_parts = modest_msg_view_get_attachments (MODEST_MSG_VIEW (priv->msg_view));
3365 /* Remove already purged messages from mime parts list. We use
3366 a copy of the list to remove items in the original one */
3367 tmp = tny_list_copy (mime_parts);
3368 iter = tny_list_create_iterator (tmp);
3369 while (!tny_iterator_is_done (iter)) {
3370 TnyMimePart *part = TNY_MIME_PART (tny_iterator_get_current (iter));
3371 if (tny_mime_part_is_purged (part))
3372 tny_list_remove (mime_parts, (GObject *) part);
3374 g_object_unref (part);
3375 tny_iterator_next (iter);
3377 g_object_unref (tmp);
3378 g_object_unref (iter);
3380 if (!modest_toolkit_utils_select_attachments (GTK_WINDOW (window), mime_parts, TRUE) ||
3381 tny_list_get_length (mime_parts) == 0) {
3382 g_object_unref (mime_parts);
3386 n_attachments = tny_list_get_length (mime_parts);
3387 if (n_attachments == 1) {
3391 iter = tny_list_create_iterator (mime_parts);
3392 part = (TnyMimePart *) tny_iterator_get_current (iter);
3393 g_object_unref (iter);
3394 if (modest_tny_mime_part_is_msg (part)) {
3396 header = tny_msg_get_header (TNY_MSG (part));
3397 filename = tny_header_dup_subject (header);
3398 g_object_unref (header);
3399 if (filename == NULL)
3400 filename = g_strdup (_("mail_va_no_subject"));
3402 filename = g_strdup (tny_mime_part_get_filename (TNY_MIME_PART (part)));
3404 confirmation_message = g_strdup_printf (_("mcen_nc_purge_file_text"), filename);
3406 g_object_unref (part);
3408 confirmation_message = g_strdup_printf (ngettext("mcen_nc_purge_file_text",
3409 "mcen_nc_purge_files_text",
3410 n_attachments), n_attachments);
3412 response = modest_platform_run_confirmation_dialog (GTK_WINDOW (window),
3413 confirmation_message);
3414 g_free (confirmation_message);
3416 if (response != GTK_RESPONSE_OK) {
3417 g_object_unref (mime_parts);
3421 priv->purge_timeout = g_timeout_add (2000, show_remove_attachment_information, window);
3423 iter = tny_list_create_iterator (mime_parts);
3424 while (!tny_iterator_is_done (iter)) {
3427 part = (TnyMimePart *) tny_iterator_get_current (iter);
3428 tny_mime_part_set_purged (TNY_MIME_PART (part));
3429 g_object_unref (part);
3430 tny_iterator_next (iter);
3432 g_object_unref (iter);
3434 msg = tny_msg_view_get_msg (TNY_MSG_VIEW (priv->msg_view));
3435 tny_msg_view_clear (TNY_MSG_VIEW (priv->msg_view));
3436 tny_msg_rewrite_cache (msg);
3437 tny_msg_view_set_msg (TNY_MSG_VIEW (priv->msg_view), msg);
3438 g_object_unref (msg);
3439 update_branding (MODEST_MSG_VIEW_WINDOW (window));
3441 g_object_unref (mime_parts);
3443 if (priv->purge_timeout > 0) {
3444 g_source_remove (priv->purge_timeout);
3445 priv->purge_timeout = 0;
3448 if (priv->remove_attachment_banner) {
3449 gtk_widget_destroy (priv->remove_attachment_banner);
3450 g_object_unref (priv->remove_attachment_banner);
3451 priv->remove_attachment_banner = NULL;
3457 update_window_title (ModestMsgViewWindow *window)
3459 ModestMsgViewWindowPrivate *priv;
3461 TnyHeader *header = NULL;
3462 gchar *subject = NULL;
3464 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
3466 /* Note that if the window is closed while we're retrieving
3467 the message, this widget could de deleted */
3468 if (!priv->msg_view)
3471 msg = tny_msg_view_get_msg (TNY_MSG_VIEW (priv->msg_view));
3473 if (priv->other_body) {
3476 description = modest_tny_mime_part_get_header_value (priv->other_body, "Content-Description");
3478 g_strstrip (description);
3479 subject = description;
3481 } else if (msg != NULL) {
3482 header = tny_msg_get_header (msg);
3483 subject = tny_header_dup_subject (header);
3484 g_object_unref (header);
3485 g_object_unref (msg);
3488 if ((subject == NULL)||(subject[0] == '\0')) {
3490 subject = g_strdup (_("mail_va_no_subject"));
3493 modest_window_set_title (MODEST_WINDOW (window), subject);
3498 on_move_focus (GtkWidget *widget,
3499 GtkDirectionType direction,
3502 g_signal_stop_emission_by_name (G_OBJECT (widget), "move-focus");
3506 fetch_image_open_stream (TnyStreamCache *self, gint64 *expected_size, gchar *uri)
3508 GnomeVFSResult result;
3509 GnomeVFSHandle *handle = NULL;
3510 GnomeVFSFileInfo *info = NULL;
3513 result = gnome_vfs_open (&handle, uri, GNOME_VFS_OPEN_READ);
3514 if (result != GNOME_VFS_OK) {
3519 info = gnome_vfs_file_info_new ();
3520 result = gnome_vfs_get_file_info_from_handle (handle, info, GNOME_VFS_FILE_INFO_DEFAULT);
3521 if (result != GNOME_VFS_OK || ! (info->valid_fields & GNOME_VFS_FILE_INFO_FIELDS_SIZE)) {
3522 /* We put a "safe" default size for going to cache */
3523 *expected_size = (300*1024);
3525 *expected_size = info->size;
3527 gnome_vfs_file_info_unref (info);
3529 stream = tny_vfs_stream_new (handle);
3538 TnyStream *output_stream;
3539 GtkWidget *msg_view;
3544 on_fetch_image_idle_refresh_view (gpointer userdata)
3547 FetchImageData *fidata = (FetchImageData *) userdata;
3549 gdk_threads_enter ();
3550 if (GTK_WIDGET_DRAWABLE (fidata->msg_view)) {
3551 ModestMsgViewWindowPrivate *priv;
3553 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (fidata->window);
3554 priv->fetching_images--;
3555 gtk_widget_queue_draw (fidata->msg_view);
3556 update_progress_hint (MODEST_MSG_VIEW_WINDOW (fidata->window));
3558 gdk_threads_leave ();
3560 g_object_unref (fidata->msg_view);
3561 g_object_unref (fidata->window);
3562 g_slice_free (FetchImageData, fidata);
3567 on_fetch_image_thread (gpointer userdata)
3569 FetchImageData *fidata = (FetchImageData *) userdata;
3570 TnyStreamCache *cache;
3571 TnyStream *cache_stream;
3573 cache = modest_runtime_get_images_cache ();
3575 tny_stream_cache_get_stream (cache,
3577 (TnyStreamCacheOpenStreamFetcher) fetch_image_open_stream,
3578 (gpointer) fidata->uri);
3579 g_free (fidata->cache_id);
3580 g_free (fidata->uri);
3582 if (cache_stream != NULL) {
3585 while (G_LIKELY (!tny_stream_is_eos (cache_stream))) {
3588 nb_read = tny_stream_read (cache_stream, buffer, sizeof (buffer));
3589 if (G_UNLIKELY (nb_read < 0)) {
3591 } else if (G_LIKELY (nb_read > 0)) {
3592 gssize nb_written = 0;
3594 while (G_UNLIKELY (nb_written < nb_read)) {
3597 len = tny_stream_write (fidata->output_stream, buffer + nb_written,
3598 nb_read - nb_written);
3599 if (G_UNLIKELY (len < 0))
3605 tny_stream_close (cache_stream);
3606 g_object_unref (cache_stream);
3609 tny_stream_close (fidata->output_stream);
3610 g_object_unref (fidata->output_stream);
3612 g_idle_add (on_fetch_image_idle_refresh_view, fidata);
3618 on_fetch_image (ModestMsgView *msgview,
3621 ModestMsgViewWindow *window)
3623 const gchar *current_account;
3624 ModestMsgViewWindowPrivate *priv;
3625 FetchImageData *fidata;
3627 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
3629 current_account = modest_window_get_active_account (MODEST_WINDOW (window));
3631 fidata = g_slice_new0 (FetchImageData);
3632 fidata->msg_view = g_object_ref (msgview);
3633 fidata->window = g_object_ref (window);
3634 fidata->uri = g_strdup (uri);
3635 fidata->cache_id = modest_images_cache_get_id (current_account, uri);
3636 fidata->output_stream = g_object_ref (stream);
3638 priv->fetching_images++;
3639 if (g_thread_create (on_fetch_image_thread, fidata, FALSE, NULL) == NULL) {
3640 g_object_unref (fidata->output_stream);
3641 g_free (fidata->cache_id);
3642 g_free (fidata->uri);
3643 g_object_unref (fidata->msg_view);
3644 g_slice_free (FetchImageData, fidata);
3645 tny_stream_close (stream);
3646 priv->fetching_images--;
3647 update_progress_hint (window);
3650 update_progress_hint (window);
3656 setup_menu (ModestMsgViewWindow *self)
3658 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW(self));
3660 /* Settings menu buttons */
3661 modest_window_add_to_menu (MODEST_WINDOW (self), _("mcen_me_viewer_find"), NULL,
3662 MODEST_WINDOW_MENU_CALLBACK (modest_msg_view_window_show_isearch_toolbar),
3663 MODEST_DIMMING_CALLBACK (modest_ui_dimming_rules_on_find_in_msg));
3665 modest_window_add_to_menu (MODEST_WINDOW (self),
3666 dngettext(GETTEXT_PACKAGE,
3667 "mcen_me_move_message",
3668 "mcen_me_move_messages",
3671 MODEST_WINDOW_MENU_CALLBACK (modest_ui_actions_on_move_to),
3672 MODEST_DIMMING_CALLBACK (modest_ui_dimming_rules_on_move_to));
3674 modest_window_add_to_menu (MODEST_WINDOW (self), _("mcen_me_inbox_mark_as_read"), NULL,
3675 MODEST_WINDOW_MENU_CALLBACK (modest_ui_actions_on_mark_as_read),
3676 MODEST_DIMMING_CALLBACK (modest_ui_dimming_rules_on_mark_as_read_msg_in_view));
3678 modest_window_add_to_menu (MODEST_WINDOW (self), _("mcen_me_inbox_mark_as_unread"), NULL,
3679 MODEST_WINDOW_MENU_CALLBACK (modest_ui_actions_on_mark_as_unread),
3680 MODEST_DIMMING_CALLBACK (modest_ui_dimming_rules_on_mark_as_unread_msg_in_view));
3682 modest_window_add_to_menu (MODEST_WINDOW (self), _("mcen_me_viewer_save_attachments"), NULL,
3683 MODEST_WINDOW_MENU_CALLBACK (modest_ui_actions_save_attachments),
3684 MODEST_DIMMING_CALLBACK (modest_ui_dimming_rules_on_save_attachments));
3685 modest_window_add_to_menu (MODEST_WINDOW (self), _("mcen_me_inbox_remove_attachments"), NULL,
3686 MODEST_WINDOW_MENU_CALLBACK (modest_ui_actions_remove_attachments),
3687 MODEST_DIMMING_CALLBACK (modest_ui_dimming_rules_on_remove_attachments));
3689 modest_window_add_to_menu (MODEST_WINDOW (self), _("mcen_me_new_message"), "<Control>n",
3690 MODEST_WINDOW_MENU_CALLBACK (modest_ui_actions_on_new_msg),
3691 MODEST_DIMMING_CALLBACK (modest_ui_dimming_rules_on_new_msg));
3692 modest_window_add_to_menu (MODEST_WINDOW (self), _("mcen_me_viewer_addtocontacts"), NULL,
3693 MODEST_WINDOW_MENU_CALLBACK (modest_ui_actions_add_to_contacts),
3694 MODEST_DIMMING_CALLBACK (modest_ui_dimming_rules_on_add_to_contacts));
3696 modest_window_add_to_menu (MODEST_WINDOW (self), _("mcen_ti_message_properties"), NULL,
3697 MODEST_WINDOW_MENU_CALLBACK (modest_ui_actions_on_details),
3698 MODEST_DIMMING_CALLBACK (modest_ui_dimming_rules_on_details));
3702 modest_msg_view_window_add_to_contacts (ModestMsgViewWindow *self)
3704 ModestMsgViewWindowPrivate *priv;
3705 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
3706 GSList *recipients = NULL;
3709 msg = tny_msg_view_get_msg (TNY_MSG_VIEW (priv->msg_view));
3713 header = modest_msg_view_window_get_header (self);
3716 recipients = modest_tny_msg_header_get_all_recipients_list (header);
3717 g_object_unref (header);
3719 recipients = modest_tny_msg_get_all_recipients_list (msg);
3720 g_object_unref (msg);
3724 /* Offer the user to add recipients to the address book */
3725 modest_address_book_add_address_list_with_selector (recipients, (GtkWindow *) self);
3726 g_slist_foreach (recipients, (GFunc) g_free, NULL); g_slist_free (recipients);
3731 _modest_msg_view_window_map_event (GtkWidget *widget,
3735 ModestMsgViewWindow *self = (ModestMsgViewWindow *) userdata;
3737 update_progress_hint (self);
3743 modest_msg_view_window_fetch_images (ModestMsgViewWindow *self)
3745 ModestMsgViewWindowPrivate *priv;
3746 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
3748 modest_msg_view_request_fetch_images (MODEST_MSG_VIEW (priv->msg_view));
3752 modest_msg_view_window_has_blocked_external_images (ModestMsgViewWindow *self)
3754 ModestMsgViewWindowPrivate *priv;
3755 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
3757 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self), FALSE);
3759 return modest_msg_view_has_blocked_external_images (MODEST_MSG_VIEW (priv->msg_view));
3763 modest_msg_view_window_reload (ModestMsgViewWindow *self)
3765 ModestMsgViewWindowPrivate *priv;
3766 const gchar *msg_uid;
3767 TnyHeader *header = NULL;
3768 TnyFolder *folder = NULL;
3770 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self));
3772 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
3774 header = modest_msg_view_window_get_header (MODEST_MSG_VIEW_WINDOW (self));
3778 folder = tny_header_get_folder (header);
3779 g_object_unref (header);
3784 msg_uid = modest_msg_view_window_get_message_uid (self);
3786 GtkTreeRowReference *row_reference;
3788 if (priv->row_reference && gtk_tree_row_reference_valid (priv->row_reference)) {
3789 row_reference = priv->row_reference;
3791 row_reference = NULL;
3793 if (!message_reader (self, priv, NULL, msg_uid, folder, row_reference))
3794 g_warning ("Shouldn't happen, trying to reload a message failed");
3797 g_object_unref (folder);
3801 update_branding (ModestMsgViewWindow *self)
3803 const gchar *account;
3804 const gchar *mailbox;
3805 ModestAccountMgr *mgr;
3806 ModestProtocol *protocol = NULL;
3807 gchar *service_name = NULL;
3808 const GdkPixbuf *service_icon = NULL;
3809 ModestMsgViewWindowPrivate *priv;
3811 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
3813 account = modest_window_get_active_account (MODEST_WINDOW (self));
3814 mailbox = modest_window_get_active_mailbox (MODEST_WINDOW (self));
3816 mgr = modest_runtime_get_account_mgr ();
3818 if (modest_account_mgr_account_is_multimailbox (mgr, account, &protocol)) {
3819 if (MODEST_IS_ACCOUNT_PROTOCOL (protocol)) {
3820 service_name = modest_account_protocol_get_service_name (MODEST_ACCOUNT_PROTOCOL (protocol),
3822 service_icon = modest_account_protocol_get_service_icon (MODEST_ACCOUNT_PROTOCOL (protocol),
3823 account, mailbox, MODEST_ICON_SIZE_SMALL);
3827 modest_msg_view_set_branding (MODEST_MSG_VIEW (priv->msg_view), service_name, service_icon);
3828 g_free (service_name);
3832 sync_flags (ModestMsgViewWindow *self)
3834 TnyHeader *header = NULL;
3836 header = modest_msg_view_window_get_header (self);
3838 TnyMsg *msg = modest_msg_view_window_get_message (self);
3840 header = tny_msg_get_header (msg);
3841 g_object_unref (msg);
3846 TnyFolder *folder = tny_header_get_folder (header);
3849 ModestMailOperation *mail_op;
3851 /* Sync folder, we need this to save the seen flag */
3852 mail_op = modest_mail_operation_new (NULL);
3853 modest_mail_operation_queue_add (modest_runtime_get_mail_operation_queue (),
3855 modest_mail_operation_sync_folder (mail_op, folder, FALSE, NULL, NULL);
3856 g_object_unref (mail_op);
3857 g_object_unref (folder);
3859 g_object_unref (header);