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));
1900 modest_ui_actions_on_close_window (NULL, MODEST_WINDOW (parent_window));
1904 /* Register the header - it'll be unregistered in the callback */
1906 modest_window_mgr_register_header (modest_runtime_get_window_mgr (), info->header, NULL);
1908 /* New mail operation */
1909 mail_op = modest_mail_operation_new_with_error_handling (G_OBJECT(parent_window),
1910 modest_ui_actions_disk_operations_error_handler,
1913 modest_mail_operation_queue_add (modest_runtime_get_mail_operation_queue (), mail_op);
1915 modest_mail_operation_get_msg (mail_op, info->header, TRUE, view_msg_cb, info->row_reference);
1917 modest_mail_operation_find_msg (mail_op, info->folder, info->msg_uid, TRUE, view_msg_cb, NULL);
1918 g_object_unref (mail_op);
1920 /* Update dimming rules */
1921 modest_ui_actions_check_toolbar_dimming_rules (MODEST_WINDOW (parent_window));
1922 modest_ui_actions_check_menu_dimming_rules (MODEST_WINDOW (parent_window));
1925 /* Frees. The row_reference will be freed by the view_msg_cb callback */
1926 g_free (info->msg_uid);
1928 g_object_unref (info->folder);
1930 g_object_unref (info->header);
1931 g_slice_free (MsgReaderInfo, info);
1936 * Reads the message whose summary item is @header. It takes care of
1937 * several things, among others:
1939 * If the message was not previously downloaded then ask the user
1940 * before downloading. If there is no connection launch the connection
1941 * dialog. Update toolbar dimming rules.
1943 * Returns: TRUE if the mail operation was started, otherwise if the
1944 * user do not want to download the message, or if the user do not
1945 * want to connect, then the operation is not issued
1948 message_reader (ModestMsgViewWindow *window,
1949 ModestMsgViewWindowPrivate *priv,
1951 const gchar *msg_uid,
1953 GtkTreeRowReference *row_reference)
1955 ModestWindowMgr *mgr;
1956 TnyAccount *account = NULL;
1957 MsgReaderInfo *info;
1959 /* We set the header from model while we're loading */
1960 tny_header_view_set_header (TNY_HEADER_VIEW (priv->msg_view), header);
1961 modest_window_set_title (MODEST_WINDOW (window), _CS("ckdg_pb_updating"));
1967 g_object_ref (folder);
1969 mgr = modest_runtime_get_window_mgr ();
1970 /* Msg download completed */
1971 if (!header || !(tny_header_get_flags (header) & TNY_HEADER_FLAG_CACHED)) {
1973 /* Ask the user if he wants to download the message if
1975 if (!tny_device_is_online (modest_runtime_get_device())) {
1976 GtkResponseType response;
1978 response = modest_platform_run_confirmation_dialog (GTK_WINDOW (window),
1979 _("mcen_nc_get_msg"));
1980 if (response == GTK_RESPONSE_CANCEL) {
1981 update_window_title (window);
1986 folder = tny_header_get_folder (header);
1988 info = g_slice_new (MsgReaderInfo);
1989 info->msg_uid = g_strdup (msg_uid);
1991 info->header = g_object_ref (header);
1993 info->header = NULL;
1995 info->folder = g_object_ref (folder);
1997 info->folder = NULL;
1998 if (row_reference) {
1999 info->row_reference = gtk_tree_row_reference_copy (row_reference);
2001 info->row_reference = NULL;
2004 /* Offer the connection dialog if necessary */
2005 modest_platform_connect_if_remote_and_perform ((GtkWindow *) window,
2007 TNY_FOLDER_STORE (folder),
2008 message_reader_performer,
2011 g_object_unref (folder);
2017 folder = tny_header_get_folder (header);
2020 account = tny_folder_get_account (folder);
2022 info = g_slice_new (MsgReaderInfo);
2023 info->msg_uid = g_strdup (msg_uid);
2025 info->folder = g_object_ref (folder);
2027 info->folder = NULL;
2029 info->header = g_object_ref (header);
2031 info->header = NULL;
2033 info->row_reference = gtk_tree_row_reference_copy (row_reference);
2035 info->row_reference = NULL;
2037 message_reader_performer (FALSE, NULL, (GtkWindow *) window, account, info);
2039 g_object_unref (account);
2041 g_object_unref (folder);
2047 modest_msg_view_window_select_next_message (ModestMsgViewWindow *window)
2049 ModestMsgViewWindowPrivate *priv;
2050 GtkTreePath *path= NULL;
2051 GtkTreeIter tmp_iter;
2053 gboolean retval = TRUE;
2054 GtkTreeRowReference *row_reference = NULL;
2056 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), FALSE);
2057 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
2059 if (!priv->row_reference)
2062 /* Update the next row reference if it's not valid. This could
2063 happen if for example the header which it was pointing to,
2064 was deleted. The best place to do it is in the row-deleted
2065 handler but the tinymail model do not work like the glib
2066 tree models and reports the deletion when the row is still
2068 if (!gtk_tree_row_reference_valid (priv->next_row_reference)) {
2069 if (priv->next_row_reference) {
2070 gtk_tree_row_reference_free (priv->next_row_reference);
2072 if (gtk_tree_row_reference_valid (priv->row_reference)) {
2073 priv->next_row_reference = gtk_tree_row_reference_copy (priv->row_reference);
2074 select_next_valid_row (priv->header_model, &(priv->next_row_reference), FALSE, priv->is_outbox);
2076 priv->next_row_reference = NULL;
2079 if (priv->next_row_reference)
2080 path = gtk_tree_row_reference_get_path (priv->next_row_reference);
2084 row_reference = gtk_tree_row_reference_copy (priv->next_row_reference);
2086 gtk_tree_model_get_iter (priv->header_model,
2089 gtk_tree_path_free (path);
2091 gtk_tree_model_get (priv->header_model, &tmp_iter,
2092 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
2095 /* Read the message & show it */
2096 if (!message_reader (window, priv, header, NULL, NULL, row_reference)) {
2099 gtk_tree_row_reference_free (row_reference);
2102 g_object_unref (header);
2108 modest_msg_view_window_select_previous_message (ModestMsgViewWindow *window)
2110 ModestMsgViewWindowPrivate *priv = NULL;
2112 gboolean finished = FALSE;
2113 gboolean retval = FALSE;
2115 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), FALSE);
2116 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
2118 if (priv->row_reference && !gtk_tree_row_reference_valid (priv->row_reference)) {
2119 gtk_tree_row_reference_free (priv->row_reference);
2120 priv->row_reference = NULL;
2123 /* Return inmediatly if there is no header model */
2124 if (!priv->header_model || !priv->row_reference)
2127 path = gtk_tree_row_reference_get_path (priv->row_reference);
2128 while (!finished && gtk_tree_path_prev (path)) {
2132 gtk_tree_model_get_iter (priv->header_model, &iter, path);
2133 gtk_tree_model_get (priv->header_model, &iter,
2134 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
2138 if (msg_is_visible (header, priv->is_outbox)) {
2139 GtkTreeRowReference *row_reference;
2140 row_reference = gtk_tree_row_reference_new (priv->header_model, path);
2141 /* Read the message & show it */
2142 retval = message_reader (window, priv, header, NULL, NULL, row_reference);
2143 gtk_tree_row_reference_free (row_reference);
2147 g_object_unref (header);
2151 gtk_tree_path_free (path);
2156 view_msg_cb (ModestMailOperation *mail_op,
2163 ModestMsgViewWindow *self = NULL;
2164 ModestMsgViewWindowPrivate *priv = NULL;
2165 GtkTreeRowReference *row_reference = NULL;
2167 /* Unregister the header (it was registered before creating the mail operation) */
2168 modest_window_mgr_unregister_header (modest_runtime_get_window_mgr (), header);
2170 row_reference = (GtkTreeRowReference *) user_data;
2173 gtk_tree_row_reference_free (row_reference);
2174 self = (ModestMsgViewWindow *) modest_mail_operation_get_source (mail_op);
2176 /* Restore window title */
2177 update_window_title (self);
2178 modest_ui_actions_on_close_window (NULL, MODEST_WINDOW (self));
2179 g_object_unref (self);
2184 /* If there was any error */
2185 if (!modest_ui_actions_msg_retrieval_check (mail_op, header, msg)) {
2187 gtk_tree_row_reference_free (row_reference);
2188 self = (ModestMsgViewWindow *) modest_mail_operation_get_source (mail_op);
2190 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
2191 /* First we check if the parent is a folder window */
2192 if (priv->msg_uid && !modest_window_mgr_get_folder_window (MODEST_WINDOW_MGR (modest_runtime_get_window_mgr ()))) {
2194 TnyAccount *account = NULL;
2195 GtkWidget *header_window = NULL;
2197 is_merge = g_str_has_prefix (priv->msg_uid, "merge:");
2199 /* Get the account */
2201 account = tny_account_store_find_account (TNY_ACCOUNT_STORE (modest_runtime_get_account_store ()),
2204 if (is_merge || account) {
2205 TnyFolder *folder = NULL;
2207 /* Try to get the message, if it's already downloaded
2208 we don't need to connect */
2210 folder = modest_tny_folder_store_find_folder_from_uri (TNY_FOLDER_STORE (account),
2213 ModestTnyAccountStore *account_store;
2214 ModestTnyLocalFoldersAccount *local_folders_account;
2216 account_store = modest_runtime_get_account_store ();
2217 local_folders_account = MODEST_TNY_LOCAL_FOLDERS_ACCOUNT (
2218 modest_tny_account_store_get_local_folders_account (account_store));
2219 folder = modest_tny_local_folders_account_get_merged_outbox (local_folders_account);
2220 g_object_unref (local_folders_account);
2222 if (account) g_object_unref (account);
2225 header_window = (GtkWidget *)
2226 modest_header_window_new (
2228 modest_window_get_active_account (MODEST_WINDOW (self)),
2229 modest_window_get_active_mailbox (MODEST_WINDOW (self)));
2230 if (!modest_window_mgr_register_window (modest_runtime_get_window_mgr (),
2231 MODEST_WINDOW (header_window),
2233 gtk_widget_destroy (GTK_WIDGET (header_window));
2235 gtk_widget_show_all (GTK_WIDGET (header_window));
2237 g_object_unref (folder);
2243 /* Restore window title */
2244 update_window_title (self);
2245 modest_ui_actions_on_close_window (NULL, MODEST_WINDOW (self));
2246 g_object_unref (self);
2251 /* Get the window */
2252 self = (ModestMsgViewWindow *) modest_mail_operation_get_source (mail_op);
2253 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self));
2254 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
2256 /* Update the row reference */
2257 if (priv->row_reference != NULL) {
2258 gtk_tree_row_reference_free (priv->row_reference);
2259 priv->row_reference = (row_reference && gtk_tree_row_reference_valid (row_reference))?gtk_tree_row_reference_copy (row_reference):NULL;
2260 if (priv->next_row_reference != NULL) {
2261 gtk_tree_row_reference_free (priv->next_row_reference);
2263 if (priv->row_reference) {
2264 priv->next_row_reference = gtk_tree_row_reference_copy (priv->row_reference);
2265 select_next_valid_row (priv->header_model, &(priv->next_row_reference), TRUE, priv->is_outbox);
2267 priv->next_row_reference = NULL;
2271 /* Mark header as read */
2272 if (!(tny_header_get_flags (header) & TNY_HEADER_FLAG_SEEN))
2273 tny_header_set_flag (header, TNY_HEADER_FLAG_SEEN);
2275 /* Set new message */
2276 if (priv->msg_view != NULL && TNY_IS_MSG_VIEW (priv->msg_view)) {
2277 tny_msg_view_set_msg (TNY_MSG_VIEW (priv->msg_view), msg);
2278 modest_msg_view_window_update_priority (self);
2279 update_window_title (MODEST_MSG_VIEW_WINDOW (self));
2280 update_branding (MODEST_MSG_VIEW_WINDOW (self));
2281 modest_msg_view_grab_focus (MODEST_MSG_VIEW (priv->msg_view));
2284 /* Set the new message uid of the window */
2285 if (priv->msg_uid) {
2286 g_free (priv->msg_uid);
2287 priv->msg_uid = modest_tny_folder_get_header_unique_id (header);
2290 /* Notify the observers */
2291 g_signal_emit (G_OBJECT (self), signals[MSG_CHANGED_SIGNAL],
2292 0, priv->header_model, priv->row_reference);
2294 /* Sync the flags if the message is not opened from a header
2295 model, i.e, if it's opened from a notification */
2296 if (!priv->header_model)
2300 g_object_unref (self);
2302 gtk_tree_row_reference_free (row_reference);
2306 modest_msg_view_window_get_folder_type (ModestMsgViewWindow *window)
2308 ModestMsgViewWindowPrivate *priv;
2310 TnyFolderType folder_type;
2312 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
2314 folder_type = TNY_FOLDER_TYPE_UNKNOWN;
2316 msg = tny_msg_view_get_msg (TNY_MSG_VIEW (priv->msg_view));
2320 folder = tny_msg_get_folder (msg);
2322 folder_type = modest_tny_folder_guess_folder_type (folder);
2323 g_object_unref (folder);
2325 g_object_unref (msg);
2333 modest_msg_view_window_update_priority (ModestMsgViewWindow *window)
2335 ModestMsgViewWindowPrivate *priv;
2336 TnyHeader *header = NULL;
2337 TnyHeaderFlags flags = 0;
2339 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
2341 if (priv->header_model && priv->row_reference && gtk_tree_row_reference_valid (priv->row_reference)) {
2343 GtkTreePath *path = NULL;
2345 path = gtk_tree_row_reference_get_path (priv->row_reference);
2346 g_return_if_fail (path != NULL);
2347 gtk_tree_model_get_iter (priv->header_model,
2349 gtk_tree_row_reference_get_path (priv->row_reference));
2351 gtk_tree_model_get (priv->header_model, &iter, TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
2353 gtk_tree_path_free (path);
2356 msg = tny_msg_view_get_msg (TNY_MSG_VIEW (priv->msg_view));
2358 header = tny_msg_get_header (msg);
2359 g_object_unref (msg);
2364 flags = tny_header_get_flags (header);
2365 g_object_unref(G_OBJECT(header));
2368 modest_msg_view_set_priority (MODEST_MSG_VIEW(priv->msg_view), flags);
2373 toolbar_resize (ModestMsgViewWindow *self)
2375 ModestMsgViewWindowPrivate *priv = NULL;
2376 ModestWindowPrivate *parent_priv = NULL;
2378 gint static_button_size;
2379 ModestWindowMgr *mgr;
2381 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self));
2382 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
2383 parent_priv = MODEST_WINDOW_GET_PRIVATE(self);
2385 mgr = modest_runtime_get_window_mgr ();
2386 static_button_size = modest_window_mgr_get_fullscreen_mode (mgr)?120:120;
2388 if (parent_priv->toolbar) {
2389 /* Set expandable and homogeneous tool buttons */
2390 widget = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/ToolbarMessageReply");
2391 gtk_tool_item_set_expand (GTK_TOOL_ITEM (widget), TRUE);
2392 gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (widget), TRUE);
2393 widget = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/ToolbarMessageReplyAll");
2394 gtk_tool_item_set_expand (GTK_TOOL_ITEM (widget), TRUE);
2395 gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (widget), TRUE);
2396 widget = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/ToolbarMessageForward");
2397 gtk_tool_item_set_expand (GTK_TOOL_ITEM (widget), TRUE);
2398 gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (widget), TRUE);
2399 widget = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/ToolbarDeleteMessage");
2400 gtk_tool_item_set_expand (GTK_TOOL_ITEM (widget), TRUE);
2401 gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (widget), TRUE);
2402 widget = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/ToolbarDownloadExternalImages");
2403 gtk_tool_item_set_expand (GTK_TOOL_ITEM (widget), TRUE);
2404 gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (widget), TRUE);
2405 gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (priv->next_toolitem), TRUE);
2406 gtk_tool_item_set_expand (GTK_TOOL_ITEM (priv->next_toolitem), TRUE);
2407 gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (priv->prev_toolitem), TRUE);
2408 gtk_tool_item_set_expand (GTK_TOOL_ITEM (priv->prev_toolitem), TRUE);
2413 modest_msg_view_window_show_toolbar (ModestWindow *self,
2414 gboolean show_toolbar)
2416 ModestMsgViewWindowPrivate *priv = NULL;
2417 ModestWindowPrivate *parent_priv;
2419 parent_priv = MODEST_WINDOW_GET_PRIVATE(self);
2420 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(self);
2422 /* Set optimized view status */
2423 priv->optimized_view = !show_toolbar;
2425 if (!parent_priv->toolbar) {
2426 parent_priv->toolbar = gtk_ui_manager_get_widget (parent_priv->ui_manager,
2429 /* We don't use HILDON_ICON_SIZE_FINGER in order to avoid the ifdef here */
2430 gtk_toolbar_set_icon_size (GTK_TOOLBAR (parent_priv->toolbar), gtk_icon_size_from_name ("hildon-finger"));
2431 gtk_widget_set_no_show_all (parent_priv->toolbar, TRUE);
2433 priv->next_toolitem = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/ToolbarMessageNext");
2434 priv->prev_toolitem = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/ToolbarMessageBack");
2435 toolbar_resize (MODEST_MSG_VIEW_WINDOW (self));
2437 modest_window_add_toolbar (MODEST_WINDOW (self),
2438 GTK_TOOLBAR (parent_priv->toolbar));
2443 /* Quick hack: this prevents toolbar icons "dance" when progress bar show status is changed */
2444 /* TODO: resize mode migth be GTK_RESIZE_QUEUE, in order to avoid unneccesary shows */
2445 gtk_container_set_resize_mode (GTK_CONTAINER(parent_priv->toolbar), GTK_RESIZE_IMMEDIATE);
2447 gtk_widget_show (GTK_WIDGET (parent_priv->toolbar));
2448 if (modest_msg_view_window_transfer_mode_enabled (MODEST_MSG_VIEW_WINDOW (self)))
2449 set_progress_hint (MODEST_MSG_VIEW_WINDOW (self), TRUE);
2451 set_progress_hint (MODEST_MSG_VIEW_WINDOW (self), FALSE);
2454 gtk_widget_set_no_show_all (parent_priv->toolbar, TRUE);
2455 gtk_widget_hide (GTK_WIDGET (parent_priv->toolbar));
2460 modest_msg_view_window_clipboard_owner_change (GtkClipboard *clipboard,
2462 ModestMsgViewWindow *window)
2464 if (!GTK_WIDGET_VISIBLE (window))
2467 modest_window_check_dimming_rules_group (MODEST_WINDOW (window), MODEST_DIMMING_RULES_CLIPBOARD);
2471 modest_msg_view_window_transfer_mode_enabled (ModestMsgViewWindow *self)
2473 ModestMsgViewWindowPrivate *priv;
2475 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self), FALSE);
2476 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(self);
2478 return priv->progress_hint;
2482 observers_empty (ModestMsgViewWindow *self)
2485 ModestMsgViewWindowPrivate *priv;
2486 gboolean is_empty = TRUE;
2487 guint pending_ops = 0;
2489 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(self);
2490 tmp = priv->progress_widgets;
2492 /* Check all observers */
2493 while (tmp && is_empty) {
2494 pending_ops = modest_progress_object_num_pending_operations (MODEST_PROGRESS_OBJECT(tmp->data));
2495 is_empty = pending_ops == 0;
2497 tmp = g_slist_next(tmp);
2504 on_account_removed (TnyAccountStore *account_store,
2505 TnyAccount *account,
2508 /* Do nothing if it's a transport account, because we only
2509 show the messages of a store account */
2510 if (tny_account_get_account_type(account) == TNY_ACCOUNT_TYPE_STORE) {
2511 const gchar *parent_acc = NULL;
2512 const gchar *our_acc = NULL;
2514 our_acc = modest_window_get_active_account (MODEST_WINDOW (user_data));
2515 parent_acc = modest_tny_account_get_parent_modest_account_name_for_server_account (account);
2517 /* Close this window if I'm showing a message of the removed account */
2518 if (our_acc && parent_acc && strcmp (parent_acc, our_acc) == 0)
2519 modest_ui_actions_on_close_window (NULL, MODEST_WINDOW (user_data));
2524 on_mail_operation_started (ModestMailOperation *mail_op,
2527 ModestMsgViewWindow *self;
2528 ModestMailOperationTypeOperation op_type;
2530 ModestMsgViewWindowPrivate *priv;
2531 GObject *source = NULL;
2533 self = MODEST_MSG_VIEW_WINDOW (user_data);
2534 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
2535 op_type = modest_mail_operation_get_type_operation (mail_op);
2536 tmp = priv->progress_widgets;
2537 source = modest_mail_operation_get_source(mail_op);
2538 if (G_OBJECT (self) == source) {
2539 if (op_type == MODEST_MAIL_OPERATION_TYPE_RECEIVE ||
2540 op_type == MODEST_MAIL_OPERATION_TYPE_OPEN ||
2541 op_type == MODEST_MAIL_OPERATION_TYPE_DELETE) {
2542 set_progress_hint (self, TRUE);
2544 modest_progress_object_add_operation (
2545 MODEST_PROGRESS_OBJECT (tmp->data),
2547 tmp = g_slist_next (tmp);
2551 g_object_unref (source);
2553 /* Update dimming rules */
2554 check_dimming_rules_after_change (self);
2558 on_mail_operation_finished (ModestMailOperation *mail_op,
2561 ModestMsgViewWindow *self;
2562 ModestMailOperationTypeOperation op_type;
2564 ModestMsgViewWindowPrivate *priv;
2566 self = MODEST_MSG_VIEW_WINDOW (user_data);
2567 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
2568 op_type = modest_mail_operation_get_type_operation (mail_op);
2569 tmp = priv->progress_widgets;
2571 if (op_type == MODEST_MAIL_OPERATION_TYPE_RECEIVE ||
2572 op_type == MODEST_MAIL_OPERATION_TYPE_OPEN ||
2573 op_type == MODEST_MAIL_OPERATION_TYPE_DELETE) {
2575 modest_progress_object_remove_operation (MODEST_PROGRESS_OBJECT (tmp->data),
2577 tmp = g_slist_next (tmp);
2580 /* If no more operations are being observed, NORMAL mode is enabled again */
2581 if (observers_empty (self)) {
2582 set_progress_hint (self, FALSE);
2586 /* Update dimming rules. We have to do this right here
2587 and not in view_msg_cb because at that point the
2588 transfer mode is still enabled so the dimming rule
2589 won't let the user delete the message that has been
2590 readed for example */
2591 check_dimming_rules_after_change (self);
2595 on_queue_changed (ModestMailOperationQueue *queue,
2596 ModestMailOperation *mail_op,
2597 ModestMailOperationQueueNotification type,
2598 ModestMsgViewWindow *self)
2600 ModestMsgViewWindowPrivate *priv;
2602 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
2604 /* If this operations was created by another window, do nothing */
2605 if (!modest_mail_operation_is_mine (mail_op, G_OBJECT(self)))
2608 if (type == MODEST_MAIL_OPERATION_QUEUE_OPERATION_ADDED) {
2609 priv->sighandlers = modest_signal_mgr_connect (priv->sighandlers,
2611 "operation-started",
2612 G_CALLBACK (on_mail_operation_started),
2614 priv->sighandlers = modest_signal_mgr_connect (priv->sighandlers,
2616 "operation-finished",
2617 G_CALLBACK (on_mail_operation_finished),
2619 } else if (type == MODEST_MAIL_OPERATION_QUEUE_OPERATION_REMOVED) {
2620 priv->sighandlers = modest_signal_mgr_disconnect (priv->sighandlers,
2622 "operation-started");
2623 priv->sighandlers = modest_signal_mgr_disconnect (priv->sighandlers,
2625 "operation-finished");
2630 modest_msg_view_window_get_attachments (ModestMsgViewWindow *win)
2632 ModestMsgViewWindowPrivate *priv;
2633 TnyList *selected_attachments = NULL;
2635 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (win), NULL);
2636 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (win);
2638 /* In Hildon 2.2 as there's no selection we assume we have all attachments selected */
2639 selected_attachments = modest_msg_view_get_attachments (MODEST_MSG_VIEW (priv->msg_view));
2641 return selected_attachments;
2645 ModestMsgViewWindow *self;
2647 gchar *attachment_uid;
2648 } DecodeAsyncHelper;
2651 on_decode_to_stream_async_handler (TnyMimePart *mime_part,
2657 DecodeAsyncHelper *helper = (DecodeAsyncHelper *) user_data;
2658 const gchar *content_type;
2660 if (cancelled || err) {
2663 if ((err->domain == TNY_ERROR_DOMAIN) &&
2664 (err->code == TNY_IO_ERROR_WRITE) &&
2665 (errno == ENOSPC)) {
2666 msg = g_strdup_printf (_KR("cerm_device_memory_full"), "");
2668 msg = g_strdup (_("mail_ib_file_operation_failed"));
2670 modest_platform_information_banner (NULL, NULL, msg);
2676 /* It could happen that the window was closed. So we
2677 assume it is a cancelation */
2678 if (!GTK_WIDGET_VISIBLE (helper->self))
2681 /* Remove the progress hint */
2682 set_progress_hint (helper->self, FALSE);
2684 content_type = tny_mime_part_get_content_type (mime_part);
2685 if (g_str_has_prefix (content_type, "message/rfc822")) {
2686 ModestWindowMgr *mgr;
2687 ModestWindow *msg_win = NULL;
2690 const gchar *mailbox;
2691 TnyStream *file_stream;
2694 fd = g_open (helper->file_path, O_RDONLY, 0644);
2696 file_stream = tny_fs_stream_new (fd);
2698 mgr = modest_runtime_get_window_mgr ();
2700 account = g_strdup (modest_window_get_active_account (MODEST_WINDOW (helper->self)));
2701 mailbox = modest_window_get_active_mailbox (MODEST_WINDOW (helper->self));
2704 account = modest_account_mgr_get_default_account (modest_runtime_get_account_mgr ());
2706 msg = tny_camel_msg_new ();
2707 tny_camel_msg_parse (msg, file_stream);
2708 msg_win = modest_msg_view_window_new_for_attachment (TNY_MSG (msg), account, mailbox, helper->attachment_uid);
2709 modest_window_set_zoom (MODEST_WINDOW (msg_win),
2710 modest_window_get_zoom (MODEST_WINDOW (helper->self)));
2711 if (modest_window_mgr_register_window (mgr, msg_win, MODEST_WINDOW (helper->self)))
2712 gtk_widget_show_all (GTK_WIDGET (msg_win));
2714 gtk_widget_destroy (GTK_WIDGET (msg_win));
2715 g_object_unref (msg);
2716 g_object_unref (file_stream);
2718 modest_platform_information_banner (NULL, NULL, _("mail_ib_file_operation_failed"));
2723 /* make the file read-only */
2724 g_chmod(helper->file_path, 0444);
2726 /* Activate the file */
2727 modest_platform_activate_file (helper->file_path, tny_mime_part_get_content_type (mime_part));
2732 g_object_unref (helper->self);
2733 g_free (helper->file_path);
2734 g_free (helper->attachment_uid);
2735 g_slice_free (DecodeAsyncHelper, helper);
2739 modest_msg_view_window_view_attachment (ModestMsgViewWindow *window,
2740 TnyMimePart *mime_part)
2742 ModestMsgViewWindowPrivate *priv;
2743 const gchar *msg_uid;
2744 gchar *attachment_uid = NULL;
2745 gint attachment_index = 0;
2746 TnyList *attachments;
2748 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window));
2749 g_return_if_fail (TNY_IS_MIME_PART (mime_part) || (mime_part == NULL));
2750 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
2752 msg_uid = modest_msg_view_window_get_message_uid (MODEST_MSG_VIEW_WINDOW (window));
2753 attachments = modest_msg_view_get_attachments (MODEST_MSG_VIEW (priv->msg_view));
2754 attachment_index = modest_list_index (attachments, (GObject *) mime_part);
2755 g_object_unref (attachments);
2757 if (msg_uid && attachment_index >= 0) {
2758 attachment_uid = g_strdup_printf ("%s/%d", msg_uid, attachment_index);
2761 if (mime_part == NULL) {
2762 gboolean error = FALSE;
2763 TnyList *selected_attachments = modest_msg_view_get_selected_attachments (MODEST_MSG_VIEW (priv->msg_view));
2764 if (selected_attachments == NULL || tny_list_get_length (selected_attachments) == 0) {
2766 } else if (tny_list_get_length (selected_attachments) > 1) {
2767 modest_platform_system_banner (NULL, NULL, _("mcen_ib_unable_to_display_more"));
2771 iter = tny_list_create_iterator (selected_attachments);
2772 mime_part = (TnyMimePart *) tny_iterator_get_current (iter);
2773 g_object_unref (iter);
2775 if (selected_attachments)
2776 g_object_unref (selected_attachments);
2781 g_object_ref (mime_part);
2784 if (tny_mime_part_is_purged (mime_part))
2787 if (!modest_tny_mime_part_is_msg (mime_part) && tny_mime_part_get_filename (mime_part)) {
2788 gchar *filepath = NULL;
2789 const gchar *att_filename = tny_mime_part_get_filename (mime_part);
2790 gboolean show_error_banner = FALSE;
2791 TnyFsStream *temp_stream = NULL;
2792 temp_stream = modest_utils_create_temp_stream (att_filename, attachment_uid,
2795 if (temp_stream != NULL) {
2796 ModestAccountMgr *mgr;
2797 DecodeAsyncHelper *helper;
2798 gboolean decode_in_provider;
2799 ModestProtocol *protocol;
2800 const gchar *account;
2802 /* Activate progress hint */
2803 set_progress_hint (window, TRUE);
2805 helper = g_slice_new0 (DecodeAsyncHelper);
2806 helper->self = g_object_ref (window);
2807 helper->file_path = g_strdup (filepath);
2808 helper->attachment_uid = g_strdup (attachment_uid);
2810 decode_in_provider = FALSE;
2811 mgr = modest_runtime_get_account_mgr ();
2812 account = modest_window_get_active_account (MODEST_WINDOW (window));
2813 if (modest_account_mgr_account_is_multimailbox (mgr, account, &protocol)) {
2814 if (MODEST_IS_ACCOUNT_PROTOCOL (protocol)) {
2816 uri = g_strconcat ("file://", filepath, NULL);
2817 decode_in_provider =
2818 modest_account_protocol_decode_part_to_stream_async (
2819 MODEST_ACCOUNT_PROTOCOL (protocol),
2822 TNY_STREAM (temp_stream),
2823 on_decode_to_stream_async_handler,
2830 if (!decode_in_provider)
2831 tny_mime_part_decode_to_stream_async (mime_part, TNY_STREAM (temp_stream),
2832 on_decode_to_stream_async_handler,
2835 g_object_unref (temp_stream);
2836 /* NOTE: files in the temporary area will be automatically
2837 * cleaned after some time if they are no longer in use */
2840 const gchar *content_type;
2841 /* the file may already exist but it isn't writable,
2842 * let's try to open it anyway */
2843 content_type = tny_mime_part_get_content_type (mime_part);
2844 modest_platform_activate_file (filepath, content_type);
2846 g_warning ("%s: modest_utils_create_temp_stream failed", __FUNCTION__);
2847 show_error_banner = TRUE;
2852 if (show_error_banner)
2853 modest_platform_information_banner (NULL, NULL, _("mail_ib_file_operation_failed"));
2854 } else if (!modest_tny_mime_part_is_msg (mime_part)) {
2855 ModestWindowMgr *mgr;
2856 ModestWindow *msg_win = NULL;
2857 TnyMsg *current_msg;
2861 current_msg = modest_msg_view_window_get_message (MODEST_MSG_VIEW_WINDOW (window));
2862 mgr = modest_runtime_get_window_mgr ();
2863 header = tny_msg_get_header (TNY_MSG (current_msg));
2864 found = modest_window_mgr_find_registered_message_uid (mgr,
2869 g_debug ("window for this body is already being created");
2872 /* it's not found, so create a new window for it */
2873 modest_window_mgr_register_header (mgr, header, attachment_uid); /* register the uid before building the window */
2874 gchar *account = g_strdup (modest_window_get_active_account (MODEST_WINDOW (window)));
2875 const gchar *mailbox = modest_window_get_active_mailbox (MODEST_WINDOW (window));
2877 account = modest_account_mgr_get_default_account (modest_runtime_get_account_mgr ());
2879 msg_win = modest_msg_view_window_new_with_other_body (TNY_MSG (current_msg), TNY_MIME_PART (mime_part),
2880 account, mailbox, attachment_uid);
2882 modest_window_set_zoom (MODEST_WINDOW (msg_win),
2883 modest_window_get_zoom (MODEST_WINDOW (window)));
2884 if (modest_window_mgr_register_window (mgr, msg_win, MODEST_WINDOW (window)))
2885 gtk_widget_show_all (GTK_WIDGET (msg_win));
2887 gtk_widget_destroy (GTK_WIDGET (msg_win));
2889 g_object_unref (current_msg);
2891 /* message attachment */
2892 TnyHeader *header = NULL;
2893 ModestWindowMgr *mgr;
2894 ModestWindow *msg_win = NULL;
2897 header = tny_msg_get_header (TNY_MSG (mime_part));
2898 mgr = modest_runtime_get_window_mgr ();
2899 found = modest_window_mgr_find_registered_header (mgr, header, &msg_win);
2902 /* if it's found, but there is no msg_win, it's probably in the process of being created;
2903 * thus, we don't do anything */
2904 g_debug ("window for is already being created");
2906 /* it's not found, so create a new window for it */
2907 modest_window_mgr_register_header (mgr, header, attachment_uid); /* register the uid before building the window */
2908 gchar *account = g_strdup (modest_window_get_active_account (MODEST_WINDOW (window)));
2909 const gchar *mailbox = modest_window_get_active_mailbox (MODEST_WINDOW (window));
2911 account = modest_account_mgr_get_default_account (modest_runtime_get_account_mgr ());
2912 msg_win = modest_msg_view_window_new_for_attachment (TNY_MSG (mime_part), account,
2913 mailbox, attachment_uid);
2914 modest_window_set_zoom (MODEST_WINDOW (msg_win),
2915 modest_window_get_zoom (MODEST_WINDOW (window)));
2916 if (modest_window_mgr_register_window (mgr, msg_win, MODEST_WINDOW (window)))
2917 gtk_widget_show_all (GTK_WIDGET (msg_win));
2919 gtk_widget_destroy (GTK_WIDGET (msg_win));
2925 g_free (attachment_uid);
2927 g_object_unref (mime_part);
2939 GnomeVFSResult result;
2941 ModestMsgViewWindow *window;
2944 static void save_mime_part_info_free (SaveMimePartInfo *info, gboolean with_struct);
2945 static gboolean idle_save_mime_part_show_result (SaveMimePartInfo *info);
2946 static gpointer save_mime_part_to_file (SaveMimePartInfo *info);
2947 static void save_mime_parts_to_file_with_checks (GtkWindow *parent, SaveMimePartInfo *info);
2950 save_mime_part_info_free (SaveMimePartInfo *info, gboolean with_struct)
2954 for (node = info->pairs; node != NULL; node = g_list_next (node)) {
2955 SaveMimePartPair *pair = (SaveMimePartPair *) node->data;
2956 g_free (pair->filename);
2957 g_object_unref (pair->part);
2958 g_slice_free (SaveMimePartPair, pair);
2960 g_list_free (info->pairs);
2963 g_object_unref (info->window);
2964 info->window = NULL;
2966 g_slice_free (SaveMimePartInfo, info);
2971 idle_save_mime_part_show_result (SaveMimePartInfo *info)
2973 /* This is a GDK lock because we are an idle callback and
2974 * modest_platform_system_banner is or does Gtk+ code */
2976 gdk_threads_enter (); /* CHECKED */
2977 if (info->result == GNOME_VFS_OK) {
2978 modest_platform_system_banner (NULL, NULL, _CS("sfil_ib_saved"));
2979 } else if (info->result == GNOME_VFS_ERROR_NO_SPACE) {
2982 /* Check if the uri belongs to the external mmc */
2983 if (g_str_has_prefix (info->uri, g_getenv (MODEST_MMC1_VOLUMEPATH_ENV)))
2984 msg = g_strdup_printf (_KR("cerm_device_memory_full"), "");
2986 msg = g_strdup (_KR("cerm_memory_card_full"));
2987 modest_platform_information_banner (NULL, NULL, msg);
2990 modest_platform_system_banner (NULL, NULL, _("mail_ib_file_operation_failed"));
2992 save_mime_part_info_free (info, FALSE);
2993 gdk_threads_leave (); /* CHECKED */
2999 save_mime_part_to_file (SaveMimePartInfo *info)
3001 GnomeVFSHandle *handle;
3003 SaveMimePartPair *pair = (SaveMimePartPair *) info->pairs->data;
3005 info->result = gnome_vfs_create (&handle, pair->filename, GNOME_VFS_OPEN_WRITE, FALSE, 0644);
3006 if (info->result == GNOME_VFS_OK) {
3007 GError *error = NULL;
3008 gboolean decode_in_provider;
3010 ModestAccountMgr *mgr;
3011 const gchar *account;
3012 ModestProtocol *protocol = NULL;
3014 stream = tny_vfs_stream_new (handle);
3016 decode_in_provider = FALSE;
3017 mgr = modest_runtime_get_account_mgr ();
3018 account = modest_window_get_active_account (MODEST_WINDOW (info->window));
3019 if (modest_account_mgr_account_is_multimailbox (mgr, account, &protocol)) {
3020 if (MODEST_IS_ACCOUNT_PROTOCOL (protocol)) {
3021 decode_in_provider =
3022 modest_account_protocol_decode_part_to_stream (
3023 MODEST_ACCOUNT_PROTOCOL (protocol),
3031 if (!decode_in_provider)
3032 written = tny_mime_part_decode_to_stream (pair->part, stream, &error);
3035 g_warning ("modest: could not save attachment %s: %d (%s)\n", pair->filename, error?error->code:-1, error?error->message:"Unknown error");
3037 if ((error->domain == TNY_ERROR_DOMAIN) &&
3038 (error->code == TNY_IO_ERROR_WRITE) &&
3039 (errno == ENOSPC)) {
3040 info->result = GNOME_VFS_ERROR_NO_SPACE;
3042 info->result = GNOME_VFS_ERROR_IO;
3045 g_object_unref (G_OBJECT (stream));
3047 g_warning ("Could not create save attachment %s: %s\n",
3048 pair->filename, gnome_vfs_result_to_string (info->result));
3051 /* Go on saving remaining files */
3052 info->pairs = g_list_remove_link (info->pairs, info->pairs);
3053 if (info->pairs != NULL) {
3054 save_mime_part_to_file (info);
3056 g_idle_add ((GSourceFunc) idle_save_mime_part_show_result, info);
3063 save_mime_parts_to_file_with_checks (GtkWindow *parent,
3064 SaveMimePartInfo *info)
3066 gboolean is_ok = TRUE;
3067 gint replaced_files = 0;
3068 const GList *files = info->pairs;
3069 const GList *iter, *to_replace = NULL;
3071 for (iter = files; (iter != NULL) && (replaced_files < 2); iter = g_list_next(iter)) {
3072 SaveMimePartPair *pair = iter->data;
3073 gchar *unescaped = g_uri_unescape_string (pair->filename, NULL);
3075 if (modest_utils_file_exists (unescaped)) {
3077 if (replaced_files == 1)
3082 if (replaced_files) {
3085 if (replaced_files == 1) {
3086 SaveMimePartPair *pair = to_replace->data;
3087 const gchar *basename = strrchr (pair->filename, G_DIR_SEPARATOR) + 1;
3088 gchar *escaped_basename, *message;
3090 escaped_basename = g_uri_unescape_string (basename, NULL);
3091 message = g_strdup_printf ("%s\n%s",
3092 _FM("docm_nc_replace_file"),
3093 (escaped_basename) ? escaped_basename : "");
3094 response = modest_platform_run_confirmation_dialog (parent, message);
3096 g_free (escaped_basename);
3098 response = modest_platform_run_confirmation_dialog (parent,
3099 _FM("docm_nc_replace_multiple"));
3101 if (response != GTK_RESPONSE_OK)
3106 save_mime_part_info_free (info, TRUE);
3108 g_thread_create ((GThreadFunc)save_mime_part_to_file, info, FALSE, NULL);
3113 typedef struct _SaveAttachmentsInfo {
3114 TnyList *attachments_list;
3115 ModestMsgViewWindow *window;
3116 } SaveAttachmentsInfo;
3119 save_attachments_response (GtkDialog *dialog,
3123 TnyList *mime_parts;
3125 GList *files_to_save = NULL;
3126 gchar *current_folder;
3127 SaveAttachmentsInfo *sa_info = (SaveAttachmentsInfo *) user_data;
3129 mime_parts = TNY_LIST (sa_info->attachments_list);
3131 if (arg1 != GTK_RESPONSE_OK)
3134 chooser_uri = gtk_file_chooser_get_uri (GTK_FILE_CHOOSER (dialog));
3135 current_folder = gtk_file_chooser_get_current_folder_uri (GTK_FILE_CHOOSER (dialog));
3136 if (current_folder && *current_folder != '\0') {
3138 modest_conf_set_string (modest_runtime_get_conf (), MODEST_CONF_LATEST_SAVE_ATTACHMENT_PATH,
3139 current_folder,&err);
3141 g_debug ("Error storing latest used folder: %s", err->message);
3145 g_free (current_folder);
3147 if (!modest_utils_folder_writable (chooser_uri)) {
3148 const gchar *err_msg;
3150 #ifdef MODEST_PLATFORM_MAEMO
3151 if (modest_maemo_utils_in_usb_mode ()) {
3152 err_msg = dgettext ("hildon-status-bar-usb", "usbh_ib_mmc_usb_connected");
3154 err_msg = _FM("sfil_ib_readonly_location");
3157 err_msg = _FM("sfil_ib_readonly_location");
3159 modest_platform_system_banner (NULL, NULL, err_msg);
3163 iter = tny_list_create_iterator (mime_parts);
3164 while (!tny_iterator_is_done (iter)) {
3165 TnyMimePart *mime_part = (TnyMimePart *) tny_iterator_get_current (iter);
3167 if ((modest_tny_mime_part_is_attachment_for_modest (mime_part)) &&
3168 !tny_mime_part_is_purged (mime_part) &&
3169 (tny_mime_part_get_filename (mime_part) != NULL)) {
3170 SaveMimePartPair *pair;
3172 pair = g_slice_new0 (SaveMimePartPair);
3174 if (tny_list_get_length (mime_parts) > 1) {
3176 gnome_vfs_escape_slashes (tny_mime_part_get_filename (mime_part));
3177 pair->filename = g_build_filename (chooser_uri, escaped, NULL);
3180 pair->filename = g_strdup (chooser_uri);
3182 pair->part = mime_part;
3183 files_to_save = g_list_prepend (files_to_save, pair);
3185 tny_iterator_next (iter);
3187 g_object_unref (iter);
3190 if (files_to_save != NULL) {
3191 SaveMimePartInfo *info = g_slice_new0 (SaveMimePartInfo);
3192 info->pairs = files_to_save;
3193 info->result = TRUE;
3194 info->uri = g_strdup (chooser_uri);
3195 info->window = g_object_ref (sa_info->window);
3196 save_mime_parts_to_file_with_checks ((GtkWindow *) dialog, info);
3198 g_free (chooser_uri);
3201 /* Free and close the dialog */
3202 g_object_unref (mime_parts);
3203 g_object_unref (sa_info->window);
3204 g_slice_free (SaveAttachmentsInfo, sa_info);
3205 gtk_widget_destroy (GTK_WIDGET (dialog));
3209 modest_msg_view_window_save_attachments (ModestMsgViewWindow *window,
3210 TnyList *mime_parts)
3212 ModestMsgViewWindowPrivate *priv;
3213 GtkWidget *save_dialog = NULL;
3214 gchar *conf_folder = NULL;
3215 gchar *filename = NULL;
3216 gchar *save_multiple_str = NULL;
3217 const gchar *root_folder = "file:///";
3219 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window));
3220 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
3222 if (mime_parts == NULL) {
3223 /* In Hildon 2.2 save and delete operate over all the attachments as there's no
3224 * selection available */
3225 mime_parts = modest_msg_view_get_attachments (MODEST_MSG_VIEW (priv->msg_view));
3226 if (mime_parts && !modest_toolkit_utils_select_attachments (GTK_WINDOW (window), mime_parts, FALSE)) {
3227 g_object_unref (mime_parts);
3230 if (mime_parts == NULL || tny_list_get_length (mime_parts) == 0) {
3232 g_object_unref (mime_parts);
3238 g_object_ref (mime_parts);
3241 /* prepare dialog */
3242 if (tny_list_get_length (mime_parts) == 1) {
3244 /* only one attachment selected */
3245 iter = tny_list_create_iterator (mime_parts);
3246 TnyMimePart *mime_part = (TnyMimePart *) tny_iterator_get_current (iter);
3247 g_object_unref (iter);
3248 if (!modest_tny_mime_part_is_msg (mime_part) &&
3249 modest_tny_mime_part_is_attachment_for_modest (mime_part) &&
3250 !tny_mime_part_is_purged (mime_part)) {
3251 filename = g_strdup (tny_mime_part_get_filename (mime_part));
3253 /* TODO: show any error? */
3254 g_warning ("%s: Tried to save a non-file attachment", __FUNCTION__);
3255 g_object_unref (mime_parts);
3258 g_object_unref (mime_part);
3260 gint num = tny_list_get_length (mime_parts);
3261 save_multiple_str = g_strdup_printf (dngettext("hildon-fm",
3262 "sfil_va_number_of_objects_attachment",
3263 "sfil_va_number_of_objects_attachments",
3267 /* Creation of hildon file chooser dialog for saving */
3268 save_dialog = modest_toolkit_factory_create_file_chooser_dialog (modest_runtime_get_toolkit_factory (),
3270 (GtkWindow *) window,
3271 GTK_FILE_CHOOSER_ACTION_SAVE);
3273 /* Get last used folder */
3274 conf_folder = modest_conf_get_string (modest_runtime_get_conf (),
3275 MODEST_CONF_LATEST_SAVE_ATTACHMENT_PATH, NULL);
3277 /* File chooser stops working if we select "file:///" as current folder */
3278 if (conf_folder && g_ascii_strcasecmp (root_folder, conf_folder) != 0) {
3279 g_free (conf_folder);
3283 if (conf_folder && conf_folder[0] != '\0') {
3284 gtk_file_chooser_set_current_folder_uri (GTK_FILE_CHOOSER (save_dialog), conf_folder);
3287 /* Set the default folder to documents folder */
3288 docs_folder = (gchar *) g_strdup(g_get_user_special_dir (G_USER_DIRECTORY_DOCUMENTS));
3291 docs_folder = g_build_filename (g_getenv (MYDOCS_ENV), DOCS_FOLDER, NULL);
3293 gtk_file_chooser_set_current_folder (GTK_FILE_CHOOSER (save_dialog), docs_folder);
3294 g_free (docs_folder);
3296 g_free (conf_folder);
3300 gtk_file_chooser_set_current_name (GTK_FILE_CHOOSER (save_dialog),
3305 /* if multiple, set multiple string */
3306 if (save_multiple_str) {
3307 g_object_set (G_OBJECT (save_dialog), "save-multiple", save_multiple_str, NULL);
3308 gtk_window_set_title (GTK_WINDOW (save_dialog), _FM("sfil_ti_save_objects_files"));
3309 g_free (save_multiple_str);
3312 /* We must run this asynchronously, because the hildon dialog
3313 performs a gtk_dialog_run by itself which leads to gdk
3315 SaveAttachmentsInfo *sa_info;
3316 sa_info = g_slice_new (SaveAttachmentsInfo);
3317 sa_info->attachments_list = mime_parts;
3318 sa_info->window = g_object_ref (window);
3319 g_signal_connect (save_dialog, "response",
3320 G_CALLBACK (save_attachments_response), sa_info);
3322 gtk_widget_show_all (save_dialog);
3326 show_remove_attachment_information (gpointer userdata)
3328 ModestMsgViewWindow *window = (ModestMsgViewWindow *) userdata;
3329 ModestMsgViewWindowPrivate *priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
3331 /* We're outside the main lock */
3332 gdk_threads_enter ();
3334 if (priv->remove_attachment_banner != NULL) {
3335 gtk_widget_destroy (priv->remove_attachment_banner);
3336 g_object_unref (priv->remove_attachment_banner);
3339 priv->remove_attachment_banner = g_object_ref (
3340 modest_platform_animation_banner (NULL, NULL, _("mcen_me_inbox_remove_attachments")));
3342 gdk_threads_leave ();
3348 modest_msg_view_window_remove_attachments (ModestMsgViewWindow *window, gboolean get_all)
3350 ModestMsgViewWindowPrivate *priv;
3351 TnyList *mime_parts = NULL, *tmp;
3352 gchar *confirmation_message;
3358 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window));
3359 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
3361 /* In hildon 2.2 we ignore the get_all flag as we always get all attachments. This is
3362 * because we don't have selection
3364 mime_parts = modest_msg_view_get_attachments (MODEST_MSG_VIEW (priv->msg_view));
3366 /* Remove already purged messages from mime parts list. We use
3367 a copy of the list to remove items in the original one */
3368 tmp = tny_list_copy (mime_parts);
3369 iter = tny_list_create_iterator (tmp);
3370 while (!tny_iterator_is_done (iter)) {
3371 TnyMimePart *part = TNY_MIME_PART (tny_iterator_get_current (iter));
3372 if (tny_mime_part_is_purged (part))
3373 tny_list_remove (mime_parts, (GObject *) part);
3375 g_object_unref (part);
3376 tny_iterator_next (iter);
3378 g_object_unref (tmp);
3379 g_object_unref (iter);
3381 if (!modest_toolkit_utils_select_attachments (GTK_WINDOW (window), mime_parts, TRUE) ||
3382 tny_list_get_length (mime_parts) == 0) {
3383 g_object_unref (mime_parts);
3387 n_attachments = tny_list_get_length (mime_parts);
3388 if (n_attachments == 1) {
3392 iter = tny_list_create_iterator (mime_parts);
3393 part = (TnyMimePart *) tny_iterator_get_current (iter);
3394 g_object_unref (iter);
3395 if (modest_tny_mime_part_is_msg (part)) {
3397 header = tny_msg_get_header (TNY_MSG (part));
3398 filename = tny_header_dup_subject (header);
3399 g_object_unref (header);
3400 if (filename == NULL)
3401 filename = g_strdup (_("mail_va_no_subject"));
3403 filename = g_strdup (tny_mime_part_get_filename (TNY_MIME_PART (part)));
3405 confirmation_message = g_strdup_printf (_("mcen_nc_purge_file_text"), filename);
3407 g_object_unref (part);
3409 confirmation_message = g_strdup_printf (ngettext("mcen_nc_purge_file_text",
3410 "mcen_nc_purge_files_text",
3411 n_attachments), n_attachments);
3413 response = modest_platform_run_confirmation_dialog (GTK_WINDOW (window),
3414 confirmation_message);
3415 g_free (confirmation_message);
3417 if (response != GTK_RESPONSE_OK) {
3418 g_object_unref (mime_parts);
3422 priv->purge_timeout = g_timeout_add (2000, show_remove_attachment_information, window);
3424 iter = tny_list_create_iterator (mime_parts);
3425 while (!tny_iterator_is_done (iter)) {
3428 part = (TnyMimePart *) tny_iterator_get_current (iter);
3429 tny_mime_part_set_purged (TNY_MIME_PART (part));
3430 g_object_unref (part);
3431 tny_iterator_next (iter);
3433 g_object_unref (iter);
3435 msg = tny_msg_view_get_msg (TNY_MSG_VIEW (priv->msg_view));
3436 tny_msg_view_clear (TNY_MSG_VIEW (priv->msg_view));
3437 tny_msg_rewrite_cache (msg);
3438 tny_msg_view_set_msg (TNY_MSG_VIEW (priv->msg_view), msg);
3439 g_object_unref (msg);
3440 update_branding (MODEST_MSG_VIEW_WINDOW (window));
3442 g_object_unref (mime_parts);
3444 if (priv->purge_timeout > 0) {
3445 g_source_remove (priv->purge_timeout);
3446 priv->purge_timeout = 0;
3449 if (priv->remove_attachment_banner) {
3450 gtk_widget_destroy (priv->remove_attachment_banner);
3451 g_object_unref (priv->remove_attachment_banner);
3452 priv->remove_attachment_banner = NULL;
3458 update_window_title (ModestMsgViewWindow *window)
3460 ModestMsgViewWindowPrivate *priv;
3462 TnyHeader *header = NULL;
3463 gchar *subject = NULL;
3465 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
3467 /* Note that if the window is closed while we're retrieving
3468 the message, this widget could de deleted */
3469 if (!priv->msg_view)
3472 msg = tny_msg_view_get_msg (TNY_MSG_VIEW (priv->msg_view));
3474 if (priv->other_body) {
3477 description = modest_tny_mime_part_get_header_value (priv->other_body, "Content-Description");
3479 g_strstrip (description);
3480 subject = description;
3482 } else if (msg != NULL) {
3483 header = tny_msg_get_header (msg);
3484 subject = tny_header_dup_subject (header);
3485 g_object_unref (header);
3486 g_object_unref (msg);
3489 if ((subject == NULL)||(subject[0] == '\0')) {
3491 subject = g_strdup (_("mail_va_no_subject"));
3494 modest_window_set_title (MODEST_WINDOW (window), subject);
3499 on_move_focus (GtkWidget *widget,
3500 GtkDirectionType direction,
3503 g_signal_stop_emission_by_name (G_OBJECT (widget), "move-focus");
3507 fetch_image_open_stream (TnyStreamCache *self, gint64 *expected_size, gchar *uri)
3509 GnomeVFSResult result;
3510 GnomeVFSHandle *handle = NULL;
3511 GnomeVFSFileInfo *info = NULL;
3514 result = gnome_vfs_open (&handle, uri, GNOME_VFS_OPEN_READ);
3515 if (result != GNOME_VFS_OK) {
3520 info = gnome_vfs_file_info_new ();
3521 result = gnome_vfs_get_file_info_from_handle (handle, info, GNOME_VFS_FILE_INFO_DEFAULT);
3522 if (result != GNOME_VFS_OK || ! (info->valid_fields & GNOME_VFS_FILE_INFO_FIELDS_SIZE)) {
3523 /* We put a "safe" default size for going to cache */
3524 *expected_size = (300*1024);
3526 *expected_size = info->size;
3528 gnome_vfs_file_info_unref (info);
3530 stream = tny_vfs_stream_new (handle);
3539 TnyStream *output_stream;
3540 GtkWidget *msg_view;
3545 on_fetch_image_idle_refresh_view (gpointer userdata)
3548 FetchImageData *fidata = (FetchImageData *) userdata;
3550 gdk_threads_enter ();
3551 if (GTK_WIDGET_DRAWABLE (fidata->msg_view)) {
3552 ModestMsgViewWindowPrivate *priv;
3554 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (fidata->window);
3555 priv->fetching_images--;
3556 gtk_widget_queue_draw (fidata->msg_view);
3557 update_progress_hint (MODEST_MSG_VIEW_WINDOW (fidata->window));
3559 gdk_threads_leave ();
3561 g_object_unref (fidata->msg_view);
3562 g_object_unref (fidata->window);
3563 g_slice_free (FetchImageData, fidata);
3568 on_fetch_image_thread (gpointer userdata)
3570 FetchImageData *fidata = (FetchImageData *) userdata;
3571 TnyStreamCache *cache;
3572 TnyStream *cache_stream;
3574 cache = modest_runtime_get_images_cache ();
3576 tny_stream_cache_get_stream (cache,
3578 (TnyStreamCacheOpenStreamFetcher) fetch_image_open_stream,
3579 (gpointer) fidata->uri);
3580 g_free (fidata->cache_id);
3581 g_free (fidata->uri);
3583 if (cache_stream != NULL) {
3586 while (G_LIKELY (!tny_stream_is_eos (cache_stream))) {
3589 nb_read = tny_stream_read (cache_stream, buffer, sizeof (buffer));
3590 if (G_UNLIKELY (nb_read < 0)) {
3592 } else if (G_LIKELY (nb_read > 0)) {
3593 gssize nb_written = 0;
3595 while (G_UNLIKELY (nb_written < nb_read)) {
3598 len = tny_stream_write (fidata->output_stream, buffer + nb_written,
3599 nb_read - nb_written);
3600 if (G_UNLIKELY (len < 0))
3606 tny_stream_close (cache_stream);
3607 g_object_unref (cache_stream);
3610 tny_stream_close (fidata->output_stream);
3611 g_object_unref (fidata->output_stream);
3613 g_idle_add (on_fetch_image_idle_refresh_view, fidata);
3619 on_fetch_image (ModestMsgView *msgview,
3622 ModestMsgViewWindow *window)
3624 const gchar *current_account;
3625 ModestMsgViewWindowPrivate *priv;
3626 FetchImageData *fidata;
3628 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
3630 current_account = modest_window_get_active_account (MODEST_WINDOW (window));
3632 fidata = g_slice_new0 (FetchImageData);
3633 fidata->msg_view = g_object_ref (msgview);
3634 fidata->window = g_object_ref (window);
3635 fidata->uri = g_strdup (uri);
3636 fidata->cache_id = modest_images_cache_get_id (current_account, uri);
3637 fidata->output_stream = g_object_ref (stream);
3639 priv->fetching_images++;
3640 if (g_thread_create (on_fetch_image_thread, fidata, FALSE, NULL) == NULL) {
3641 g_object_unref (fidata->output_stream);
3642 g_free (fidata->cache_id);
3643 g_free (fidata->uri);
3644 g_object_unref (fidata->msg_view);
3645 g_slice_free (FetchImageData, fidata);
3646 tny_stream_close (stream);
3647 priv->fetching_images--;
3648 update_progress_hint (window);
3651 update_progress_hint (window);
3657 setup_menu (ModestMsgViewWindow *self)
3659 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW(self));
3661 /* Settings menu buttons */
3662 modest_window_add_to_menu (MODEST_WINDOW (self), _("mcen_me_viewer_find"), NULL,
3663 MODEST_WINDOW_MENU_CALLBACK (modest_msg_view_window_show_isearch_toolbar),
3664 MODEST_DIMMING_CALLBACK (modest_ui_dimming_rules_on_find_in_msg));
3666 modest_window_add_to_menu (MODEST_WINDOW (self),
3667 dngettext(GETTEXT_PACKAGE,
3668 "mcen_me_move_message",
3669 "mcen_me_move_messages",
3672 MODEST_WINDOW_MENU_CALLBACK (modest_ui_actions_on_move_to),
3673 MODEST_DIMMING_CALLBACK (modest_ui_dimming_rules_on_move_to));
3675 modest_window_add_to_menu (MODEST_WINDOW (self), _("mcen_me_inbox_mark_as_read"), NULL,
3676 MODEST_WINDOW_MENU_CALLBACK (modest_ui_actions_on_mark_as_read),
3677 MODEST_DIMMING_CALLBACK (modest_ui_dimming_rules_on_mark_as_read_msg_in_view));
3679 modest_window_add_to_menu (MODEST_WINDOW (self), _("mcen_me_inbox_mark_as_unread"), NULL,
3680 MODEST_WINDOW_MENU_CALLBACK (modest_ui_actions_on_mark_as_unread),
3681 MODEST_DIMMING_CALLBACK (modest_ui_dimming_rules_on_mark_as_unread_msg_in_view));
3683 modest_window_add_to_menu (MODEST_WINDOW (self), _("mcen_me_viewer_save_attachments"), NULL,
3684 MODEST_WINDOW_MENU_CALLBACK (modest_ui_actions_save_attachments),
3685 MODEST_DIMMING_CALLBACK (modest_ui_dimming_rules_on_save_attachments));
3686 modest_window_add_to_menu (MODEST_WINDOW (self), _("mcen_me_inbox_remove_attachments"), NULL,
3687 MODEST_WINDOW_MENU_CALLBACK (modest_ui_actions_remove_attachments),
3688 MODEST_DIMMING_CALLBACK (modest_ui_dimming_rules_on_remove_attachments));
3690 modest_window_add_to_menu (MODEST_WINDOW (self), _("mcen_me_new_message"), "<Control>n",
3691 MODEST_WINDOW_MENU_CALLBACK (modest_ui_actions_on_new_msg),
3692 MODEST_DIMMING_CALLBACK (modest_ui_dimming_rules_on_new_msg));
3693 modest_window_add_to_menu (MODEST_WINDOW (self), _("mcen_me_viewer_addtocontacts"), NULL,
3694 MODEST_WINDOW_MENU_CALLBACK (modest_ui_actions_add_to_contacts),
3695 MODEST_DIMMING_CALLBACK (modest_ui_dimming_rules_on_add_to_contacts));
3697 modest_window_add_to_menu (MODEST_WINDOW (self), _("mcen_ti_message_properties"), NULL,
3698 MODEST_WINDOW_MENU_CALLBACK (modest_ui_actions_on_details),
3699 MODEST_DIMMING_CALLBACK (modest_ui_dimming_rules_on_details));
3703 modest_msg_view_window_add_to_contacts (ModestMsgViewWindow *self)
3705 ModestMsgViewWindowPrivate *priv;
3706 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
3707 GSList *recipients = NULL;
3710 msg = tny_msg_view_get_msg (TNY_MSG_VIEW (priv->msg_view));
3714 header = modest_msg_view_window_get_header (self);
3717 recipients = modest_tny_msg_header_get_all_recipients_list (header);
3718 g_object_unref (header);
3720 recipients = modest_tny_msg_get_all_recipients_list (msg);
3721 g_object_unref (msg);
3725 /* Offer the user to add recipients to the address book */
3726 modest_address_book_add_address_list_with_selector (recipients, (GtkWindow *) self);
3727 g_slist_foreach (recipients, (GFunc) g_free, NULL); g_slist_free (recipients);
3732 _modest_msg_view_window_map_event (GtkWidget *widget,
3736 ModestMsgViewWindow *self = (ModestMsgViewWindow *) userdata;
3738 update_progress_hint (self);
3744 modest_msg_view_window_fetch_images (ModestMsgViewWindow *self)
3746 ModestMsgViewWindowPrivate *priv;
3747 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
3749 modest_msg_view_request_fetch_images (MODEST_MSG_VIEW (priv->msg_view));
3753 modest_msg_view_window_has_blocked_external_images (ModestMsgViewWindow *self)
3755 ModestMsgViewWindowPrivate *priv;
3756 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
3758 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self), FALSE);
3760 return modest_msg_view_has_blocked_external_images (MODEST_MSG_VIEW (priv->msg_view));
3764 modest_msg_view_window_reload (ModestMsgViewWindow *self)
3766 ModestMsgViewWindowPrivate *priv;
3767 const gchar *msg_uid;
3768 TnyHeader *header = NULL;
3769 TnyFolder *folder = NULL;
3771 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self));
3773 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
3775 header = modest_msg_view_window_get_header (MODEST_MSG_VIEW_WINDOW (self));
3779 folder = tny_header_get_folder (header);
3780 g_object_unref (header);
3785 msg_uid = modest_msg_view_window_get_message_uid (self);
3787 GtkTreeRowReference *row_reference;
3789 if (priv->row_reference && gtk_tree_row_reference_valid (priv->row_reference)) {
3790 row_reference = priv->row_reference;
3792 row_reference = NULL;
3794 if (!message_reader (self, priv, NULL, msg_uid, folder, row_reference))
3795 g_warning ("Shouldn't happen, trying to reload a message failed");
3798 g_object_unref (folder);
3802 update_branding (ModestMsgViewWindow *self)
3804 const gchar *account;
3805 const gchar *mailbox;
3806 ModestAccountMgr *mgr;
3807 ModestProtocol *protocol = NULL;
3808 gchar *service_name = NULL;
3809 const GdkPixbuf *service_icon = NULL;
3810 ModestMsgViewWindowPrivate *priv;
3812 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
3814 account = modest_window_get_active_account (MODEST_WINDOW (self));
3815 mailbox = modest_window_get_active_mailbox (MODEST_WINDOW (self));
3817 mgr = modest_runtime_get_account_mgr ();
3819 if (modest_account_mgr_account_is_multimailbox (mgr, account, &protocol)) {
3820 if (MODEST_IS_ACCOUNT_PROTOCOL (protocol)) {
3821 service_name = modest_account_protocol_get_service_name (MODEST_ACCOUNT_PROTOCOL (protocol),
3823 service_icon = modest_account_protocol_get_service_icon (MODEST_ACCOUNT_PROTOCOL (protocol),
3824 account, mailbox, MODEST_ICON_SIZE_SMALL);
3828 modest_msg_view_set_branding (MODEST_MSG_VIEW (priv->msg_view), service_name, service_icon);
3829 g_free (service_name);
3833 sync_flags (ModestMsgViewWindow *self)
3835 TnyHeader *header = NULL;
3837 header = modest_msg_view_window_get_header (self);
3839 TnyMsg *msg = modest_msg_view_window_get_message (self);
3841 header = tny_msg_get_header (msg);
3842 g_object_unref (msg);
3847 TnyFolder *folder = tny_header_get_folder (header);
3850 ModestMailOperation *mail_op;
3852 /* Sync folder, we need this to save the seen flag */
3853 mail_op = modest_mail_operation_new (NULL);
3854 modest_mail_operation_queue_add (modest_runtime_get_mail_operation_queue (),
3856 modest_mail_operation_sync_folder (mail_op, folder, FALSE, NULL, NULL);
3857 g_object_unref (mail_op);
3858 g_object_unref (folder);
3860 g_object_unref (header);