1 /* Copyright (c) 2006, Nokia Corporation
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions are
8 * * Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 * * Redistributions in binary form must reproduce the above copyright
11 * notice, this list of conditions and the following disclaimer in the
12 * documentation and/or other materials provided with the distribution.
13 * * Neither the name of the Nokia Corporation nor the names of its
14 * contributors may be used to endorse or promote products derived from
15 * this software without specific prior written permission.
17 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
18 * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
19 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
20 * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
21 * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
22 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
23 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
24 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
25 * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
26 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
27 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29 #include <glib/gi18n.h>
31 #include <tny-account-store.h>
32 #include <tny-simple-list.h>
34 #include <tny-mime-part.h>
35 #include <tny-vfs-stream.h>
36 #include <tny-error.h>
37 #include "modest-marshal.h"
38 #include "modest-platform.h"
39 #include <modest-utils.h>
40 #include <modest-maemo-utils.h>
41 #include <modest-tny-msg.h>
42 #include <modest-msg-view-window.h>
43 #include "modest-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 <hildon/hildon-pannable-area.h>
52 #include <hildon/hildon-picker-dialog.h>
53 #include <hildon/hildon-app-menu.h>
54 #include "modest-defs.h"
55 #include "modest-hildon-includes.h"
56 #include "modest-ui-dimming-manager.h"
57 #include <gdk/gdkkeysyms.h>
58 #include <modest-tny-account.h>
59 #include <modest-mime-part-view.h>
60 #include <modest-isearch-view.h>
61 #include <modest-tny-mime-part.h>
62 #include <modest-address-book.h>
65 #include <glib/gstdio.h>
66 #include <modest-debug.h>
67 #include <modest-header-window.h>
68 #include <modest-account-protocol.h>
69 #include <modest-icon-names.h>
70 #include <modest-ui-actions.h>
71 #include <tny-camel-msg.h>
73 #define MYDOCS_ENV "MYDOCSDIR"
74 #define DOCS_FOLDER ".documents"
76 typedef struct _ModestMsgViewWindowPrivate ModestMsgViewWindowPrivate;
77 struct _ModestMsgViewWindowPrivate {
80 GtkWidget *main_scroll;
81 GtkWidget *find_toolbar;
84 /* Progress observers */
85 GSList *progress_widgets;
88 GtkWidget *prev_toolitem;
89 GtkWidget *next_toolitem;
90 gboolean progress_hint;
93 /* Optimized view enabled */
94 gboolean optimized_view;
96 /* Whether this was created via the *_new_for_search_result() function. */
97 gboolean is_search_result;
99 /* Whether the message is in outbox */
102 /* A reference to the @model of the header view
103 * to allow selecting previous/next messages,
104 * if the message is currently selected in the header view.
106 const gchar *header_folder_id;
107 GtkTreeModel *header_model;
108 GtkTreeRowReference *row_reference;
109 GtkTreeRowReference *next_row_reference;
111 gulong clipboard_change_handler;
112 gulong queue_change_handler;
113 gulong account_removed_handler;
114 gulong row_changed_handler;
115 gulong row_deleted_handler;
116 gulong row_inserted_handler;
117 gulong rows_reordered_handler;
120 GtkWidget *remove_attachment_banner;
123 TnyMimePart *other_body;
128 static void modest_msg_view_window_class_init (ModestMsgViewWindowClass *klass);
129 static void modest_msg_view_window_init (ModestMsgViewWindow *obj);
130 static void modest_header_view_observer_init (ModestHeaderViewObserverIface *iface_class);
131 static void modest_msg_view_window_finalize (GObject *obj);
132 static void modest_msg_view_window_show_find_toolbar (GtkWidget *obj, gpointer data);
133 static void modest_msg_view_window_find_toolbar_close (GtkWidget *widget,
134 ModestMsgViewWindow *obj);
135 static void modest_msg_view_window_find_toolbar_search (GtkWidget *widget,
136 ModestMsgViewWindow *obj);
137 static void modest_msg_view_window_toggle_find_toolbar (GtkWidget *obj,
139 static void modest_msg_view_window_disconnect_signals (ModestWindow *self);
141 static gdouble modest_msg_view_window_get_zoom (ModestWindow *window);
142 static void modest_msg_view_window_set_zoom (ModestWindow *window,
144 static gboolean modest_msg_view_window_zoom_minus (ModestWindow *window);
145 static gboolean modest_msg_view_window_zoom_plus (ModestWindow *window);
146 static gboolean modest_msg_view_window_key_event (GtkWidget *window,
149 static void modest_msg_view_window_update_priority (ModestMsgViewWindow *window);
151 static void modest_msg_view_window_show_toolbar (ModestWindow *window,
152 gboolean show_toolbar);
154 static void modest_msg_view_window_clipboard_owner_change (GtkClipboard *clipboard,
156 ModestMsgViewWindow *window);
158 static void modest_msg_view_window_on_row_changed (GtkTreeModel *header_model,
161 ModestMsgViewWindow *window);
163 static void modest_msg_view_window_on_row_deleted (GtkTreeModel *header_model,
165 ModestMsgViewWindow *window);
167 static void modest_msg_view_window_on_row_inserted (GtkTreeModel *header_model,
168 GtkTreePath *tree_path,
169 GtkTreeIter *tree_iter,
170 ModestMsgViewWindow *window);
172 static void modest_msg_view_window_on_row_reordered (GtkTreeModel *header_model,
176 ModestMsgViewWindow *window);
178 static void modest_msg_view_window_update_model_replaced (ModestHeaderViewObserver *window,
180 const gchar *tny_folder_id);
182 static void on_queue_changed (ModestMailOperationQueue *queue,
183 ModestMailOperation *mail_op,
184 ModestMailOperationQueueNotification type,
185 ModestMsgViewWindow *self);
187 static void on_account_removed (TnyAccountStore *account_store,
191 static void on_move_focus (GtkWidget *widget,
192 GtkDirectionType direction,
195 static void view_msg_cb (ModestMailOperation *mail_op,
202 static void set_progress_hint (ModestMsgViewWindow *self,
205 static void update_window_title (ModestMsgViewWindow *window);
207 static void init_window (ModestMsgViewWindow *obj);
209 static gboolean msg_is_visible (TnyHeader *header, gboolean check_outbox);
211 static void check_dimming_rules_after_change (ModestMsgViewWindow *window);
213 static gboolean on_fetch_image (ModestMsgView *msgview,
216 ModestMsgViewWindow *window);
218 static gboolean modest_msg_view_window_scroll_child (ModestMsgViewWindow *self,
219 GtkScrollType scroll_type,
222 static gboolean message_reader (ModestMsgViewWindow *window,
223 ModestMsgViewWindowPrivate *priv,
225 const gchar *msg_uid,
227 GtkTreeRowReference *row_reference);
229 static void setup_menu (ModestMsgViewWindow *self);
230 static gboolean _modest_msg_view_window_map_event (GtkWidget *widget,
233 static void update_branding (ModestMsgViewWindow *self);
235 /* list my signals */
242 static const GtkActionEntry msg_view_toolbar_action_entries [] = {
245 { "ToolbarMessageReply", MODEST_STOCK_REPLY, N_("mcen_me_inbox_reply"), "<CTRL>R", NULL, G_CALLBACK (modest_ui_actions_on_reply) },
246 { "ToolbarMessageReplyAll", MODEST_STOCK_REPLY_ALL, N_("mcen_me_inbox_replytoall"), NULL, NULL, G_CALLBACK (modest_ui_actions_on_reply_all) },
247 { "ToolbarMessageForward", MODEST_STOCK_FORWARD, N_("mcen_me_inbox_forward"), NULL, NULL, G_CALLBACK (modest_ui_actions_on_forward) },
248 { "ToolbarDeleteMessage", MODEST_STOCK_DELETE, N_("qgn_toolb_gene_deletebutton"), NULL, NULL, G_CALLBACK (modest_ui_actions_on_delete_message_or_folder) },
249 { "ToolbarMessageBack", MODEST_TOOLBAR_ICON_PREV, N_("qgn_toolb_gene_back"), NULL, NULL, G_CALLBACK (modest_ui_actions_on_prev) },
250 { "ToolbarMessageNext", MODEST_TOOLBAR_ICON_NEXT, N_("qgn_toolb_gene_forward"), NULL, NULL, G_CALLBACK (modest_ui_actions_on_next) },
251 { "ToolbarDownloadExternalImages", MODEST_TOOLBAR_ICON_DOWNLOAD_IMAGES, N_("mail_bd_external_images"), NULL, NULL, G_CALLBACK (modest_ui_actions_on_fetch_images) },
254 static const GtkToggleActionEntry msg_view_toggle_action_entries [] = {
255 { "FindInMessage", MODEST_TOOLBAR_ICON_FIND, N_("qgn_toolb_gene_find"), "<CTRL>F", NULL, G_CALLBACK (modest_msg_view_window_toggle_find_toolbar), FALSE },
258 #define MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE((o), \
259 MODEST_TYPE_MSG_VIEW_WINDOW, \
260 ModestMsgViewWindowPrivate))
262 static GtkWindowClass *parent_class = NULL;
264 /* uncomment the following if you have defined any signals */
265 static guint signals[LAST_SIGNAL] = {0};
268 modest_msg_view_window_get_type (void)
270 static GType my_type = 0;
272 static const GTypeInfo my_info = {
273 sizeof(ModestMsgViewWindowClass),
274 NULL, /* base init */
275 NULL, /* base finalize */
276 (GClassInitFunc) modest_msg_view_window_class_init,
277 NULL, /* class finalize */
278 NULL, /* class data */
279 sizeof(ModestMsgViewWindow),
281 (GInstanceInitFunc) modest_msg_view_window_init,
284 my_type = g_type_register_static (MODEST_TYPE_HILDON2_WINDOW,
285 "ModestMsgViewWindow",
288 static const GInterfaceInfo modest_header_view_observer_info =
290 (GInterfaceInitFunc) modest_header_view_observer_init,
291 NULL, /* interface_finalize */
292 NULL /* interface_data */
295 g_type_add_interface_static (my_type,
296 MODEST_TYPE_HEADER_VIEW_OBSERVER,
297 &modest_header_view_observer_info);
303 save_state (ModestWindow *self)
305 modest_widget_memory_save (modest_runtime_get_conf (),
307 MODEST_CONF_MSG_VIEW_WINDOW_KEY);
311 modest_msg_view_window_scroll_child (ModestMsgViewWindow *self,
312 GtkScrollType scroll_type,
316 ModestMsgViewWindowPrivate *priv;
319 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(self);
321 switch (scroll_type) {
322 case GTK_SCROLL_STEP_UP:
325 case GTK_SCROLL_STEP_DOWN:
328 case GTK_SCROLL_PAGE_UP:
331 case GTK_SCROLL_PAGE_DOWN:
334 case GTK_SCROLL_START:
345 modest_maemo_utils_scroll_pannable((HildonPannableArea *) priv->main_scroll, 0, step);
347 return (gboolean) step;
351 add_scroll_binding (GtkBindingSet *binding_set,
353 GtkScrollType scroll)
355 guint keypad_keyval = keyval - GDK_Left + GDK_KP_Left;
357 gtk_binding_entry_add_signal (binding_set, keyval, 0,
359 GTK_TYPE_SCROLL_TYPE, scroll,
360 G_TYPE_BOOLEAN, FALSE);
361 gtk_binding_entry_add_signal (binding_set, keypad_keyval, 0,
363 GTK_TYPE_SCROLL_TYPE, scroll,
364 G_TYPE_BOOLEAN, FALSE);
368 modest_msg_view_window_class_init (ModestMsgViewWindowClass *klass)
370 GObjectClass *gobject_class;
371 HildonWindowClass *hildon_window_class;
372 ModestWindowClass *modest_window_class;
373 GtkBindingSet *binding_set;
375 gobject_class = (GObjectClass*) klass;
376 hildon_window_class = (HildonWindowClass *) klass;
377 modest_window_class = (ModestWindowClass *) klass;
379 parent_class = g_type_class_peek_parent (klass);
380 gobject_class->finalize = modest_msg_view_window_finalize;
382 modest_window_class->set_zoom_func = modest_msg_view_window_set_zoom;
383 modest_window_class->get_zoom_func = modest_msg_view_window_get_zoom;
384 modest_window_class->zoom_plus_func = modest_msg_view_window_zoom_plus;
385 modest_window_class->zoom_minus_func = modest_msg_view_window_zoom_minus;
386 modest_window_class->show_toolbar_func = modest_msg_view_window_show_toolbar;
387 modest_window_class->disconnect_signals_func = modest_msg_view_window_disconnect_signals;
389 modest_window_class->save_state_func = save_state;
391 klass->scroll_child = modest_msg_view_window_scroll_child;
393 signals[MSG_CHANGED_SIGNAL] =
394 g_signal_new ("msg-changed",
395 G_TYPE_FROM_CLASS (gobject_class),
397 G_STRUCT_OFFSET (ModestMsgViewWindowClass, msg_changed),
399 modest_marshal_VOID__POINTER_POINTER,
400 G_TYPE_NONE, 2, G_TYPE_POINTER, G_TYPE_POINTER);
402 signals[SCROLL_CHILD_SIGNAL] =
403 g_signal_new ("scroll-child",
404 G_TYPE_FROM_CLASS (gobject_class),
405 G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
406 G_STRUCT_OFFSET (ModestMsgViewWindowClass, scroll_child),
408 modest_marshal_BOOLEAN__ENUM_BOOLEAN,
409 G_TYPE_BOOLEAN, 2, GTK_TYPE_SCROLL_TYPE, G_TYPE_BOOLEAN);
411 binding_set = gtk_binding_set_by_class (klass);
412 add_scroll_binding (binding_set, GDK_Up, GTK_SCROLL_STEP_UP);
413 add_scroll_binding (binding_set, GDK_Down, GTK_SCROLL_STEP_DOWN);
414 add_scroll_binding (binding_set, GDK_Page_Up, GTK_SCROLL_PAGE_UP);
415 add_scroll_binding (binding_set, GDK_Page_Down, GTK_SCROLL_PAGE_DOWN);
416 add_scroll_binding (binding_set, GDK_Home, GTK_SCROLL_START);
417 add_scroll_binding (binding_set, GDK_End, GTK_SCROLL_END);
419 g_type_class_add_private (gobject_class, sizeof(ModestMsgViewWindowPrivate));
423 static void modest_header_view_observer_init(
424 ModestHeaderViewObserverIface *iface_class)
426 iface_class->update_func = modest_msg_view_window_update_model_replaced;
430 modest_msg_view_window_init (ModestMsgViewWindow *obj)
432 ModestMsgViewWindowPrivate *priv;
433 ModestWindowPrivate *parent_priv = NULL;
434 GtkActionGroup *action_group = NULL;
435 GError *error = NULL;
437 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(obj);
438 parent_priv = MODEST_WINDOW_GET_PRIVATE(obj);
439 parent_priv->ui_manager = gtk_ui_manager_new();
441 action_group = gtk_action_group_new ("ModestMsgViewWindowActions");
442 gtk_action_group_set_translation_domain (action_group, GETTEXT_PACKAGE);
444 /* Add common actions */
445 gtk_action_group_add_actions (action_group,
446 msg_view_toolbar_action_entries,
447 G_N_ELEMENTS (msg_view_toolbar_action_entries),
449 gtk_action_group_add_toggle_actions (action_group,
450 msg_view_toggle_action_entries,
451 G_N_ELEMENTS (msg_view_toggle_action_entries),
454 gtk_ui_manager_insert_action_group (parent_priv->ui_manager, action_group, 0);
455 g_object_unref (action_group);
457 /* Load the UI definition */
458 gtk_ui_manager_add_ui_from_file (parent_priv->ui_manager, MODEST_UIDIR "modest-msg-view-window-ui.xml",
461 g_printerr ("modest: could not merge modest-msg-view-window-ui.xml: %s\n", error->message);
462 g_error_free (error);
467 /* Add accelerators */
468 gtk_window_add_accel_group (GTK_WINDOW (obj),
469 gtk_ui_manager_get_accel_group (parent_priv->ui_manager));
471 priv->is_search_result = FALSE;
472 priv->is_outbox = FALSE;
474 priv->msg_view = NULL;
475 priv->header_model = NULL;
476 priv->header_folder_id = NULL;
477 priv->clipboard_change_handler = 0;
478 priv->queue_change_handler = 0;
479 priv->account_removed_handler = 0;
480 priv->row_changed_handler = 0;
481 priv->row_deleted_handler = 0;
482 priv->row_inserted_handler = 0;
483 priv->rows_reordered_handler = 0;
484 priv->progress_hint = FALSE;
485 priv->fetching_images = 0;
487 priv->optimized_view = FALSE;
488 priv->purge_timeout = 0;
489 priv->remove_attachment_banner = NULL;
490 priv->msg_uid = NULL;
491 priv->other_body = NULL;
493 priv->sighandlers = NULL;
496 init_window (MODEST_MSG_VIEW_WINDOW(obj));
498 hildon_program_add_window (hildon_program_get_instance(),
504 update_progress_hint (ModestMsgViewWindow *self)
506 ModestMsgViewWindowPrivate *priv;
507 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(self);
509 if (GTK_WIDGET_VISIBLE (self)) {
510 hildon_gtk_window_set_progress_indicator (GTK_WINDOW (self),
511 (priv->progress_hint || (priv->fetching_images > 0))?1:0);
516 set_progress_hint (ModestMsgViewWindow *self,
519 ModestWindowPrivate *parent_priv;
520 ModestMsgViewWindowPrivate *priv;
522 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self));
524 parent_priv = MODEST_WINDOW_GET_PRIVATE(self);
525 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(self);
527 /* Sets current progress hint */
528 priv->progress_hint = enabled;
530 update_progress_hint (self);
536 init_window (ModestMsgViewWindow *obj)
538 GtkWidget *main_vbox;
539 ModestMsgViewWindowPrivate *priv;
541 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(obj);
543 priv->msg_view = GTK_WIDGET (tny_platform_factory_new_msg_view (modest_tny_platform_factory_get_instance ()));
544 modest_msg_view_set_shadow_type (MODEST_MSG_VIEW (priv->msg_view), GTK_SHADOW_NONE);
545 main_vbox = gtk_vbox_new (FALSE, 6);
546 priv->main_scroll = hildon_pannable_area_new ();
547 g_object_set (G_OBJECT (priv->main_scroll),
548 "mov-mode", HILDON_MOVEMENT_MODE_BOTH,
551 gtk_container_add (GTK_CONTAINER (priv->main_scroll), priv->msg_view);
552 gtk_box_pack_start (GTK_BOX(main_vbox), priv->main_scroll, TRUE, TRUE, 0);
553 gtk_container_add (GTK_CONTAINER(obj), main_vbox);
555 /* NULL-ize fields if the window is destroyed */
556 g_signal_connect (priv->msg_view, "destroy", G_CALLBACK (gtk_widget_destroyed), &(priv->msg_view));
558 gtk_widget_show_all (GTK_WIDGET(main_vbox));
562 modest_msg_view_window_disconnect_signals (ModestWindow *self)
564 ModestMsgViewWindowPrivate *priv;
565 GtkWidget *header_view = NULL;
566 GtkWindow *parent_window = NULL;
568 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
570 if (gtk_clipboard_get (GDK_SELECTION_PRIMARY) &&
571 g_signal_handler_is_connected (gtk_clipboard_get (GDK_SELECTION_PRIMARY),
572 priv->clipboard_change_handler))
573 g_signal_handler_disconnect (gtk_clipboard_get (GDK_SELECTION_PRIMARY),
574 priv->clipboard_change_handler);
576 if (g_signal_handler_is_connected (G_OBJECT (modest_runtime_get_mail_operation_queue ()),
577 priv->queue_change_handler))
578 g_signal_handler_disconnect (G_OBJECT (modest_runtime_get_mail_operation_queue ()),
579 priv->queue_change_handler);
581 if (g_signal_handler_is_connected (G_OBJECT (modest_runtime_get_account_store ()),
582 priv->account_removed_handler))
583 g_signal_handler_disconnect (G_OBJECT (modest_runtime_get_account_store ()),
584 priv->account_removed_handler);
586 if (priv->header_model) {
587 if (g_signal_handler_is_connected(G_OBJECT (priv->header_model),
588 priv->row_changed_handler))
589 g_signal_handler_disconnect(G_OBJECT (priv->header_model),
590 priv->row_changed_handler);
592 if (g_signal_handler_is_connected(G_OBJECT (priv->header_model),
593 priv->row_deleted_handler))
594 g_signal_handler_disconnect(G_OBJECT (priv->header_model),
595 priv->row_deleted_handler);
597 if (g_signal_handler_is_connected(G_OBJECT (priv->header_model),
598 priv->row_inserted_handler))
599 g_signal_handler_disconnect(G_OBJECT (priv->header_model),
600 priv->row_inserted_handler);
602 if (g_signal_handler_is_connected(G_OBJECT (priv->header_model),
603 priv->rows_reordered_handler))
604 g_signal_handler_disconnect(G_OBJECT (priv->header_model),
605 priv->rows_reordered_handler);
608 modest_signal_mgr_disconnect_all_and_destroy (priv->sighandlers);
609 priv->sighandlers = NULL;
611 parent_window = gtk_window_get_transient_for (GTK_WINDOW (self));
612 if (parent_window && MODEST_IS_HEADER_WINDOW (parent_window)) {
613 header_view = GTK_WIDGET (modest_header_window_get_header_view (MODEST_HEADER_WINDOW (parent_window)));
615 modest_header_view_remove_observer(MODEST_HEADER_VIEW (header_view),
616 MODEST_HEADER_VIEW_OBSERVER(self));
622 modest_msg_view_window_finalize (GObject *obj)
624 ModestMsgViewWindowPrivate *priv;
626 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (obj);
628 /* Sanity check: shouldn't be needed, the window mgr should
629 call this function before */
630 modest_msg_view_window_disconnect_signals (MODEST_WINDOW (obj));
632 if (priv->other_body != NULL) {
633 g_object_unref (priv->other_body);
634 priv->other_body = NULL;
637 if (priv->header_model != NULL) {
638 g_object_unref (priv->header_model);
639 priv->header_model = NULL;
642 if (priv->remove_attachment_banner) {
643 gtk_widget_destroy (priv->remove_attachment_banner);
644 g_object_unref (priv->remove_attachment_banner);
645 priv->remove_attachment_banner = NULL;
648 if (priv->purge_timeout > 0) {
649 g_source_remove (priv->purge_timeout);
650 priv->purge_timeout = 0;
653 if (priv->row_reference) {
654 gtk_tree_row_reference_free (priv->row_reference);
655 priv->row_reference = NULL;
658 if (priv->next_row_reference) {
659 gtk_tree_row_reference_free (priv->next_row_reference);
660 priv->next_row_reference = NULL;
664 g_free (priv->msg_uid);
665 priv->msg_uid = NULL;
668 G_OBJECT_CLASS(parent_class)->finalize (obj);
672 select_next_valid_row (GtkTreeModel *model,
673 GtkTreeRowReference **row_reference,
677 GtkTreeIter tmp_iter;
679 GtkTreePath *next = NULL;
680 gboolean retval = FALSE, finished;
682 g_return_val_if_fail (gtk_tree_row_reference_valid (*row_reference), FALSE);
684 path = gtk_tree_row_reference_get_path (*row_reference);
685 gtk_tree_model_get_iter (model, &tmp_iter, path);
686 gtk_tree_row_reference_free (*row_reference);
687 *row_reference = NULL;
691 TnyHeader *header = NULL;
693 if (gtk_tree_model_iter_next (model, &tmp_iter)) {
694 gtk_tree_model_get (model, &tmp_iter,
695 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
699 if (msg_is_visible (header, is_outbox)) {
700 next = gtk_tree_model_get_path (model, &tmp_iter);
701 *row_reference = gtk_tree_row_reference_new (model, next);
702 gtk_tree_path_free (next);
706 g_object_unref (header);
709 } else if (cycle && gtk_tree_model_get_iter_first (model, &tmp_iter)) {
710 next = gtk_tree_model_get_path (model, &tmp_iter);
712 /* Ensure that we are not selecting the same */
713 if (gtk_tree_path_compare (path, next) != 0) {
714 gtk_tree_model_get (model, &tmp_iter,
715 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
718 if (msg_is_visible (header, is_outbox)) {
719 *row_reference = gtk_tree_row_reference_new (model, next);
723 g_object_unref (header);
727 /* If we ended up in the same message
728 then there is no valid next
732 gtk_tree_path_free (next);
734 /* If there are no more messages and we don't
735 want to start again in the first one then
736 there is no valid next message */
742 gtk_tree_path_free (path);
747 /* TODO: This should be in _init(), with the parameters as properties. */
749 modest_msg_view_window_construct (ModestMsgViewWindow *self,
750 const gchar *modest_account_name,
751 const gchar *mailbox,
752 const gchar *msg_uid)
755 ModestMsgViewWindowPrivate *priv = NULL;
756 ModestWindowPrivate *parent_priv = NULL;
757 ModestDimmingRulesGroup *toolbar_rules_group = NULL;
758 ModestDimmingRulesGroup *clipboard_rules_group = NULL;
760 obj = G_OBJECT (self);
761 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(obj);
762 parent_priv = MODEST_WINDOW_GET_PRIVATE(obj);
764 priv->msg_uid = g_strdup (msg_uid);
767 parent_priv->menubar = NULL;
769 toolbar_rules_group = modest_dimming_rules_group_new (MODEST_DIMMING_RULES_TOOLBAR, TRUE);
770 clipboard_rules_group = modest_dimming_rules_group_new (MODEST_DIMMING_RULES_CLIPBOARD, FALSE);
773 /* Add common dimming rules */
774 modest_dimming_rules_group_add_rules (toolbar_rules_group,
775 modest_msg_view_toolbar_dimming_entries,
776 G_N_ELEMENTS (modest_msg_view_toolbar_dimming_entries),
777 MODEST_WINDOW (self));
778 modest_dimming_rules_group_add_rules (clipboard_rules_group,
779 modest_msg_view_clipboard_dimming_entries,
780 G_N_ELEMENTS (modest_msg_view_clipboard_dimming_entries),
781 MODEST_WINDOW (self));
783 /* Insert dimming rules group for this window */
784 modest_ui_dimming_manager_insert_rules_group (parent_priv->ui_dimming_manager, toolbar_rules_group);
785 modest_ui_dimming_manager_insert_rules_group (parent_priv->ui_dimming_manager, clipboard_rules_group);
786 g_object_unref (toolbar_rules_group);
787 g_object_unref (clipboard_rules_group);
789 /* g_signal_connect (G_OBJECT(obj), "delete-event", G_CALLBACK(on_delete_event), obj); */
791 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);
792 g_signal_connect (G_OBJECT(priv->msg_view), "activate_link",
793 G_CALLBACK (modest_ui_actions_on_msg_link_clicked), obj);
794 g_signal_connect (G_OBJECT(priv->msg_view), "link_hover",
795 G_CALLBACK (modest_ui_actions_on_msg_link_hover), obj);
796 g_signal_connect (G_OBJECT(priv->msg_view), "attachment_clicked",
797 G_CALLBACK (modest_ui_actions_on_msg_attachment_clicked), obj);
798 g_signal_connect (G_OBJECT(priv->msg_view), "recpt_activated",
799 G_CALLBACK (modest_ui_actions_on_msg_recpt_activated), obj);
800 g_signal_connect (G_OBJECT(priv->msg_view), "show_details",
801 G_CALLBACK (modest_ui_actions_on_details), obj);
802 g_signal_connect (G_OBJECT(priv->msg_view), "link_contextual",
803 G_CALLBACK (modest_ui_actions_on_msg_link_contextual), obj);
804 g_signal_connect (G_OBJECT(priv->msg_view), "limit_error",
805 G_CALLBACK (modest_ui_actions_on_limit_error), obj);
806 g_signal_connect (G_OBJECT (priv->msg_view), "fetch_image",
807 G_CALLBACK (on_fetch_image), obj);
809 g_signal_connect (G_OBJECT (obj), "key-release-event",
810 G_CALLBACK (modest_msg_view_window_key_event),
813 g_signal_connect (G_OBJECT (obj), "key-press-event",
814 G_CALLBACK (modest_msg_view_window_key_event),
817 g_signal_connect (G_OBJECT (obj), "move-focus",
818 G_CALLBACK (on_move_focus), obj);
820 g_signal_connect (G_OBJECT (obj), "map-event",
821 G_CALLBACK (_modest_msg_view_window_map_event),
824 /* Mail Operation Queue */
825 priv->queue_change_handler = g_signal_connect (G_OBJECT (modest_runtime_get_mail_operation_queue ()),
827 G_CALLBACK (on_queue_changed),
830 /* Account manager */
831 priv->account_removed_handler = g_signal_connect (G_OBJECT (modest_runtime_get_account_store ()),
833 G_CALLBACK(on_account_removed),
836 modest_window_set_active_account (MODEST_WINDOW(obj), modest_account_name);
837 modest_window_set_active_mailbox (MODEST_WINDOW(obj), mailbox);
839 /* First add out toolbar ... */
840 modest_msg_view_window_show_toolbar (MODEST_WINDOW (obj), TRUE);
842 /* ... and later the find toolbar. This way find toolbar will
843 be shown over the other */
844 priv->find_toolbar = hildon_find_toolbar_new (NULL);
845 hildon_window_add_toolbar (HILDON_WINDOW (obj), GTK_TOOLBAR (priv->find_toolbar));
846 gtk_widget_set_no_show_all (priv->find_toolbar, TRUE);
847 g_signal_connect (G_OBJECT (priv->find_toolbar), "close",
848 G_CALLBACK (modest_msg_view_window_find_toolbar_close), obj);
849 g_signal_connect (G_OBJECT (priv->find_toolbar), "search",
850 G_CALLBACK (modest_msg_view_window_find_toolbar_search), obj);
851 priv->last_search = NULL;
853 /* Init the clipboard actions dim status */
854 modest_msg_view_grab_focus(MODEST_MSG_VIEW (priv->msg_view));
856 update_window_title (MODEST_MSG_VIEW_WINDOW (obj));
861 /* FIXME: parameter checks */
863 modest_msg_view_window_new_with_header_model (TnyMsg *msg,
864 const gchar *modest_account_name,
865 const gchar *mailbox,
866 const gchar *msg_uid,
868 GtkTreeRowReference *row_reference)
870 ModestMsgViewWindow *window = NULL;
871 ModestMsgViewWindowPrivate *priv = NULL;
872 TnyFolder *header_folder = NULL;
873 ModestHeaderView *header_view = NULL;
874 ModestWindowMgr *mgr = NULL;
877 modest_tny_mime_part_to_string (TNY_MIME_PART (msg), 0);
880 mgr = modest_runtime_get_window_mgr ();
881 window = MODEST_MSG_VIEW_WINDOW (modest_window_mgr_get_msg_view_window (mgr));
882 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), NULL);
884 modest_msg_view_window_construct (window, modest_account_name, mailbox, msg_uid);
886 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
888 /* Remember the message list's TreeModel so we can detect changes
889 * and change the list selection when necessary: */
890 header_folder = modest_header_view_get_folder (header_view);
892 priv->is_outbox = (modest_tny_folder_guess_folder_type (header_folder) ==
893 TNY_FOLDER_TYPE_OUTBOX);
894 priv->header_folder_id = tny_folder_get_id (header_folder);
895 g_object_unref(header_folder);
898 /* Setup row references and connect signals */
899 priv->header_model = g_object_ref (model);
901 if (row_reference && gtk_tree_row_reference_valid (row_reference)) {
902 priv->row_reference = gtk_tree_row_reference_copy (row_reference);
903 priv->next_row_reference = gtk_tree_row_reference_copy (row_reference);
904 select_next_valid_row (model, &(priv->next_row_reference), TRUE, priv->is_outbox);
906 priv->row_reference = NULL;
907 priv->next_row_reference = NULL;
910 /* Connect signals */
911 priv->row_changed_handler =
912 g_signal_connect (GTK_TREE_MODEL(model), "row-changed",
913 G_CALLBACK(modest_msg_view_window_on_row_changed),
915 priv->row_deleted_handler =
916 g_signal_connect (GTK_TREE_MODEL(model), "row-deleted",
917 G_CALLBACK(modest_msg_view_window_on_row_deleted),
919 priv->row_inserted_handler =
920 g_signal_connect (GTK_TREE_MODEL(model), "row-inserted",
921 G_CALLBACK(modest_msg_view_window_on_row_inserted),
923 priv->rows_reordered_handler =
924 g_signal_connect(GTK_TREE_MODEL(model), "rows-reordered",
925 G_CALLBACK(modest_msg_view_window_on_row_reordered),
928 if (header_view != NULL){
929 modest_header_view_add_observer(header_view,
930 MODEST_HEADER_VIEW_OBSERVER(window));
933 tny_msg_view_set_msg (TNY_MSG_VIEW (priv->msg_view), msg);
934 update_window_title (MODEST_MSG_VIEW_WINDOW (window));
935 update_branding (MODEST_MSG_VIEW_WINDOW (window));
937 /* gtk_widget_show_all (GTK_WIDGET (window)); */
938 modest_msg_view_window_update_priority (window);
939 /* Check dimming rules */
940 modest_ui_actions_check_toolbar_dimming_rules (MODEST_WINDOW (window));
941 modest_ui_actions_check_menu_dimming_rules (MODEST_WINDOW (window));
942 modest_window_check_dimming_rules_group (MODEST_WINDOW (window), MODEST_DIMMING_RULES_CLIPBOARD);
944 return MODEST_WINDOW(window);
948 modest_msg_view_window_new_from_uid (const gchar *modest_account_name,
949 const gchar *mailbox,
950 const gchar *msg_uid)
952 ModestMsgViewWindow *window = NULL;
953 ModestMsgViewWindowPrivate *priv = NULL;
954 ModestWindowMgr *mgr = NULL;
956 TnyAccount *account = NULL;
958 mgr = modest_runtime_get_window_mgr ();
959 window = MODEST_MSG_VIEW_WINDOW (modest_window_mgr_get_msg_view_window (mgr));
960 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), NULL);
962 modest_msg_view_window_construct (window, modest_account_name, mailbox, msg_uid);
964 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
968 is_merge = g_str_has_prefix (msg_uid, "merge:");
970 /* Get the account */
972 account = tny_account_store_find_account (TNY_ACCOUNT_STORE (modest_runtime_get_account_store ()),
976 if (is_merge || account) {
977 TnyFolder *folder = NULL;
979 /* Try to get the message, if it's already downloaded
980 we don't need to connect */
982 folder = modest_tny_folder_store_find_folder_from_uri (TNY_FOLDER_STORE (account), msg_uid);
984 ModestTnyAccountStore *account_store;
985 ModestTnyLocalFoldersAccount *local_folders_account;
987 account_store = modest_runtime_get_account_store ();
988 local_folders_account = MODEST_TNY_LOCAL_FOLDERS_ACCOUNT (
989 modest_tny_account_store_get_local_folders_account (account_store));
990 folder = modest_tny_local_folders_account_get_merged_outbox (local_folders_account);
991 g_object_unref (local_folders_account);
995 gboolean device_online;
997 device = modest_runtime_get_device();
998 device_online = tny_device_is_online (device);
1000 message_reader (window, priv, NULL, msg_uid, folder, NULL);
1002 TnyMsg *msg = tny_folder_find_msg (folder, msg_uid, NULL);
1004 tny_msg_view_set_msg (TNY_MSG_VIEW (priv->msg_view), msg);
1005 update_window_title (MODEST_MSG_VIEW_WINDOW (window));
1006 update_branding (MODEST_MSG_VIEW_WINDOW (window));
1007 g_object_unref (msg);
1009 message_reader (window, priv, NULL, msg_uid, folder, NULL);
1012 g_object_unref (folder);
1017 /* Check dimming rules */
1018 modest_ui_actions_check_toolbar_dimming_rules (MODEST_WINDOW (window));
1019 modest_ui_actions_check_menu_dimming_rules (MODEST_WINDOW (window));
1020 modest_window_check_dimming_rules_group (MODEST_WINDOW (window), MODEST_DIMMING_RULES_CLIPBOARD);
1022 return MODEST_WINDOW(window);
1026 modest_msg_view_window_new_from_header_view (ModestHeaderView *header_view,
1027 const gchar *modest_account_name,
1028 const gchar *mailbox,
1029 const gchar *msg_uid,
1030 GtkTreeRowReference *row_reference)
1032 ModestMsgViewWindow *window = NULL;
1033 ModestMsgViewWindowPrivate *priv = NULL;
1034 TnyFolder *header_folder = NULL;
1035 ModestWindowMgr *mgr = NULL;
1039 mgr = modest_runtime_get_window_mgr ();
1040 window = MODEST_MSG_VIEW_WINDOW (modest_window_mgr_get_msg_view_window (mgr));
1041 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), NULL);
1043 modest_msg_view_window_construct (window, modest_account_name, mailbox, msg_uid);
1045 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1047 /* Remember the message list's TreeModel so we can detect changes
1048 * and change the list selection when necessary: */
1050 if (header_view != NULL){
1051 header_folder = modest_header_view_get_folder(header_view);
1052 /* This could happen if the header folder was
1053 unseleted before opening this msg window (for
1054 example if the user selects an account in the
1055 folder view of the main window */
1056 if (header_folder) {
1057 priv->is_outbox = (modest_tny_folder_guess_folder_type (header_folder) ==
1058 TNY_FOLDER_TYPE_OUTBOX);
1059 priv->header_folder_id = tny_folder_get_id(header_folder);
1060 g_object_unref(header_folder);
1064 /* Setup row references and connect signals */
1065 priv->header_model = gtk_tree_view_get_model (GTK_TREE_VIEW (header_view));
1066 g_object_ref (priv->header_model);
1068 if (row_reference && gtk_tree_row_reference_valid (row_reference)) {
1069 priv->row_reference = gtk_tree_row_reference_copy (row_reference);
1070 priv->next_row_reference = gtk_tree_row_reference_copy (row_reference);
1071 select_next_valid_row (priv->header_model, &(priv->next_row_reference), TRUE, priv->is_outbox);
1073 priv->row_reference = NULL;
1074 priv->next_row_reference = NULL;
1077 /* Connect signals */
1078 priv->row_changed_handler =
1079 g_signal_connect (GTK_TREE_MODEL(priv->header_model), "row-changed",
1080 G_CALLBACK(modest_msg_view_window_on_row_changed),
1082 priv->row_deleted_handler =
1083 g_signal_connect (GTK_TREE_MODEL(priv->header_model), "row-deleted",
1084 G_CALLBACK(modest_msg_view_window_on_row_deleted),
1086 priv->row_inserted_handler =
1087 g_signal_connect (GTK_TREE_MODEL(priv->header_model), "row-inserted",
1088 G_CALLBACK(modest_msg_view_window_on_row_inserted),
1090 priv->rows_reordered_handler =
1091 g_signal_connect(GTK_TREE_MODEL(priv->header_model), "rows-reordered",
1092 G_CALLBACK(modest_msg_view_window_on_row_reordered),
1095 if (header_view != NULL){
1096 modest_header_view_add_observer(header_view,
1097 MODEST_HEADER_VIEW_OBSERVER(window));
1100 tny_msg_view_set_msg (TNY_MSG_VIEW (priv->msg_view), NULL);
1101 update_branding (MODEST_MSG_VIEW_WINDOW (window));
1103 if (priv->row_reference) {
1104 path = gtk_tree_row_reference_get_path (priv->row_reference);
1105 if (gtk_tree_model_get_iter (priv->header_model, &iter, path)) {
1107 gtk_tree_model_get (priv->header_model, &iter,
1108 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
1110 message_reader (window, priv, header, NULL, NULL, priv->row_reference);
1111 g_object_unref (header);
1113 gtk_tree_path_free (path);
1115 /* Check dimming rules */
1116 modest_ui_actions_check_toolbar_dimming_rules (MODEST_WINDOW (window));
1117 modest_ui_actions_check_menu_dimming_rules (MODEST_WINDOW (window));
1118 modest_window_check_dimming_rules_group (MODEST_WINDOW (window), MODEST_DIMMING_RULES_CLIPBOARD);
1120 return MODEST_WINDOW(window);
1124 modest_msg_view_window_new_for_search_result (TnyMsg *msg,
1125 const gchar *modest_account_name,
1126 const gchar *mailbox,
1127 const gchar *msg_uid)
1129 ModestMsgViewWindow *window = NULL;
1130 ModestMsgViewWindowPrivate *priv = NULL;
1131 ModestWindowMgr *mgr = NULL;
1133 mgr = modest_runtime_get_window_mgr ();
1134 window = MODEST_MSG_VIEW_WINDOW (modest_window_mgr_get_msg_view_window (mgr));
1135 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), NULL);
1136 modest_msg_view_window_construct (window, modest_account_name, mailbox, msg_uid);
1138 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1140 /* Remember that this is a search result,
1141 * so we can disable some UI appropriately: */
1142 priv->is_search_result = TRUE;
1144 tny_msg_view_set_msg (TNY_MSG_VIEW (priv->msg_view), msg);
1145 update_branding (MODEST_MSG_VIEW_WINDOW (window));
1147 update_window_title (window);
1148 /* gtk_widget_show_all (GTK_WIDGET (window));*/
1149 modest_msg_view_window_update_priority (window);
1151 /* Check dimming rules */
1152 modest_ui_actions_check_toolbar_dimming_rules (MODEST_WINDOW (window));
1153 modest_ui_actions_check_menu_dimming_rules (MODEST_WINDOW (window));
1154 modest_window_check_dimming_rules_group (MODEST_WINDOW (window), MODEST_DIMMING_RULES_CLIPBOARD);
1156 return MODEST_WINDOW(window);
1160 modest_msg_view_window_is_other_body (ModestMsgViewWindow *self)
1162 ModestMsgViewWindowPrivate *priv = NULL;
1164 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self), FALSE);
1165 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
1167 return (priv->other_body != NULL);
1171 modest_msg_view_window_new_with_other_body (TnyMsg *msg,
1172 TnyMimePart *other_body,
1173 const gchar *modest_account_name,
1174 const gchar *mailbox,
1175 const gchar *msg_uid)
1177 GObject *obj = NULL;
1178 ModestMsgViewWindowPrivate *priv;
1179 ModestWindowMgr *mgr = NULL;
1181 g_return_val_if_fail (msg, NULL);
1182 mgr = modest_runtime_get_window_mgr ();
1183 obj = G_OBJECT (modest_window_mgr_get_msg_view_window (mgr));
1184 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (obj);
1185 modest_msg_view_window_construct (MODEST_MSG_VIEW_WINDOW (obj),
1186 modest_account_name, mailbox, msg_uid);
1189 priv->other_body = g_object_ref (other_body);
1190 modest_msg_view_set_msg_with_other_body (MODEST_MSG_VIEW (priv->msg_view), msg, other_body);
1192 tny_msg_view_set_msg (TNY_MSG_VIEW (priv->msg_view), msg);
1194 update_window_title (MODEST_MSG_VIEW_WINDOW (obj));
1195 update_branding (MODEST_MSG_VIEW_WINDOW (obj));
1197 /* gtk_widget_show_all (GTK_WIDGET (obj)); */
1199 /* Check dimming rules */
1200 modest_ui_actions_check_toolbar_dimming_rules (MODEST_WINDOW (obj));
1201 modest_ui_actions_check_menu_dimming_rules (MODEST_WINDOW (obj));
1202 modest_window_check_dimming_rules_group (MODEST_WINDOW (obj), MODEST_DIMMING_RULES_CLIPBOARD);
1204 return MODEST_WINDOW(obj);
1208 modest_msg_view_window_new_for_attachment (TnyMsg *msg,
1209 const gchar *modest_account_name,
1210 const gchar *mailbox,
1211 const gchar *msg_uid)
1213 return modest_msg_view_window_new_with_other_body (msg, NULL, modest_account_name, mailbox, msg_uid);
1217 modest_msg_view_window_on_row_changed (GtkTreeModel *header_model,
1220 ModestMsgViewWindow *window)
1222 check_dimming_rules_after_change (window);
1226 modest_msg_view_window_on_row_deleted(GtkTreeModel *header_model,
1228 ModestMsgViewWindow *window)
1230 check_dimming_rules_after_change (window);
1232 /* The window could have dissapeared */
1235 check_dimming_rules_after_change (ModestMsgViewWindow *window)
1237 modest_ui_actions_check_menu_dimming_rules (MODEST_WINDOW (window));
1238 modest_ui_actions_check_toolbar_dimming_rules (MODEST_WINDOW (window));
1242 /* On insertions we check if the folder still has the message we are
1243 * showing or do not. If do not, we do nothing. Which means we are still
1244 * not attached to any header folder and thus next/prev buttons are
1245 * still dimmed. Once the message that is shown by msg-view is found, the
1246 * new model of header-view will be attached and the references will be set.
1247 * On each further insertions dimming rules will be checked. However
1248 * this requires extra CPU time at least works.
1249 * (An message might be deleted from TnyFolder and thus will not be
1250 * inserted into the model again for example if it is removed by the
1251 * imap server and the header view is refreshed.)
1254 modest_msg_view_window_on_row_inserted (GtkTreeModel *model,
1255 GtkTreePath *tree_path,
1256 GtkTreeIter *tree_iter,
1257 ModestMsgViewWindow *window)
1259 ModestMsgViewWindowPrivate *priv = NULL;
1260 TnyHeader *header = NULL;
1262 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window));
1263 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1265 g_assert (model == priv->header_model);
1267 /* Check if the newly inserted message is the same we are actually
1268 * showing. IF not, we should remain detached from the header model
1269 * and thus prev and next toolbar buttons should remain dimmed. */
1270 gtk_tree_model_get (model, tree_iter,
1271 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
1274 if (TNY_IS_HEADER (header)) {
1277 uid = modest_tny_folder_get_header_unique_id (header);
1278 if (!g_str_equal(priv->msg_uid, uid)) {
1279 check_dimming_rules_after_change (window);
1281 g_object_unref (G_OBJECT(header));
1285 g_object_unref(G_OBJECT(header));
1288 if (priv->row_reference) {
1289 gtk_tree_row_reference_free (priv->row_reference);
1292 /* Setup row_reference for the actual msg. */
1293 priv->row_reference = gtk_tree_row_reference_new (priv->header_model, tree_path);
1294 if (priv->row_reference == NULL) {
1295 g_warning("%s: No reference for msg header item.", __FUNCTION__);
1299 /* Now set up next_row_reference. */
1300 if (priv->next_row_reference) {
1301 gtk_tree_row_reference_free (priv->next_row_reference);
1304 priv->next_row_reference = gtk_tree_row_reference_copy (priv->row_reference);
1305 select_next_valid_row (priv->header_model,
1306 &(priv->next_row_reference), FALSE, priv->is_outbox);
1308 /* Connect the remaining callbacks to become able to detect
1309 * changes in header-view. */
1310 priv->row_changed_handler =
1311 g_signal_connect (priv->header_model, "row-changed",
1312 G_CALLBACK (modest_msg_view_window_on_row_changed),
1314 priv->row_deleted_handler =
1315 g_signal_connect (priv->header_model, "row-deleted",
1316 G_CALLBACK (modest_msg_view_window_on_row_deleted),
1318 priv->rows_reordered_handler =
1319 g_signal_connect (priv->header_model, "rows-reordered",
1320 G_CALLBACK (modest_msg_view_window_on_row_reordered),
1323 check_dimming_rules_after_change (window);
1327 modest_msg_view_window_on_row_reordered (GtkTreeModel *header_model,
1331 ModestMsgViewWindow *window)
1333 ModestMsgViewWindowPrivate *priv = NULL;
1334 gboolean already_changed = FALSE;
1336 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(window);
1338 /* If the current row was reordered select the proper next
1339 valid row. The same if the next row reference changes */
1340 if (!priv->row_reference ||
1341 !gtk_tree_row_reference_valid (priv->row_reference))
1344 if (priv->next_row_reference &&
1345 gtk_tree_row_reference_valid (priv->next_row_reference)) {
1346 GtkTreePath *cur, *next;
1347 /* Check that the order is still the correct one */
1348 cur = gtk_tree_row_reference_get_path (priv->row_reference);
1349 next = gtk_tree_row_reference_get_path (priv->next_row_reference);
1350 gtk_tree_path_next (cur);
1351 if (gtk_tree_path_compare (cur, next) != 0) {
1352 gtk_tree_row_reference_free (priv->next_row_reference);
1353 priv->next_row_reference = gtk_tree_row_reference_copy (priv->row_reference);
1354 select_next_valid_row (header_model, &(priv->next_row_reference), FALSE, priv->is_outbox);
1355 already_changed = TRUE;
1357 gtk_tree_path_free (cur);
1358 gtk_tree_path_free (next);
1360 if (priv->next_row_reference)
1361 gtk_tree_row_reference_free (priv->next_row_reference);
1362 /* Update next row reference */
1363 priv->next_row_reference = gtk_tree_row_reference_copy (priv->row_reference);
1364 select_next_valid_row (header_model, &(priv->next_row_reference), FALSE, priv->is_outbox);
1365 already_changed = TRUE;
1368 check_dimming_rules_after_change (window);
1371 /* The modest_msg_view_window_update_model_replaced implements update
1372 * function for ModestHeaderViewObserver. Checks whether the TnyFolder
1373 * actually belongs to the header-view is the same as the TnyFolder of
1374 * the message of msg-view or not. If they are different, there is
1375 * nothing to do. If they are the same, then the model has replaced and
1376 * the reference in msg-view shall be replaced from the old model to
1377 * the new model. In this case the view will be detached from it's
1378 * header folder. From this point the next/prev buttons are dimmed.
1381 modest_msg_view_window_update_model_replaced (ModestHeaderViewObserver *observer,
1382 GtkTreeModel *model,
1383 const gchar *tny_folder_id)
1385 ModestMsgViewWindowPrivate *priv = NULL;
1386 ModestMsgViewWindow *window = NULL;
1388 g_assert(MODEST_IS_HEADER_VIEW_OBSERVER(observer));
1389 g_assert(MODEST_IS_MSG_VIEW_WINDOW(observer));
1391 window = MODEST_MSG_VIEW_WINDOW(observer);
1392 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(window);
1394 /* If there is an other folder in the header-view then we do
1395 * not care about it's model (msg list). Else if the
1396 * header-view shows the folder the msg shown by us is in, we
1397 * shall replace our model reference and make some check. */
1398 if(model == NULL || tny_folder_id == NULL ||
1399 (priv->header_folder_id && !g_str_equal(tny_folder_id, priv->header_folder_id)))
1402 /* Model is changed(replaced), so we should forget the old
1403 * one. Because there might be other references and there
1404 * might be some change on the model even if we unreferenced
1405 * it, we need to disconnect our signals here. */
1406 if (priv->header_model) {
1407 if (g_signal_handler_is_connected(G_OBJECT (priv->header_model),
1408 priv->row_changed_handler))
1409 g_signal_handler_disconnect(G_OBJECT (priv->header_model),
1410 priv->row_changed_handler);
1411 if (g_signal_handler_is_connected(G_OBJECT (priv->header_model),
1412 priv->row_deleted_handler))
1413 g_signal_handler_disconnect(G_OBJECT (priv->header_model),
1414 priv->row_deleted_handler);
1415 if (g_signal_handler_is_connected(G_OBJECT (priv->header_model),
1416 priv->row_inserted_handler))
1417 g_signal_handler_disconnect(G_OBJECT (priv->header_model),
1418 priv->row_inserted_handler);
1419 if (g_signal_handler_is_connected(G_OBJECT (priv->header_model),
1420 priv->rows_reordered_handler))
1421 g_signal_handler_disconnect(G_OBJECT (priv->header_model),
1422 priv->rows_reordered_handler);
1425 if (priv->row_reference)
1426 gtk_tree_row_reference_free (priv->row_reference);
1427 if (priv->next_row_reference)
1428 gtk_tree_row_reference_free (priv->next_row_reference);
1429 g_object_unref(priv->header_model);
1432 priv->row_changed_handler = 0;
1433 priv->row_deleted_handler = 0;
1434 priv->row_inserted_handler = 0;
1435 priv->rows_reordered_handler = 0;
1436 priv->next_row_reference = NULL;
1437 priv->row_reference = NULL;
1438 priv->header_model = NULL;
1441 priv->header_model = g_object_ref (model);
1443 /* Also we must connect to the new model for row insertions.
1444 * Only for insertions now. We will need other ones only after
1445 * the msg is show by msg-view is added to the new model. */
1446 priv->row_inserted_handler =
1447 g_signal_connect (priv->header_model, "row-inserted",
1448 G_CALLBACK(modest_msg_view_window_on_row_inserted),
1451 modest_ui_actions_check_menu_dimming_rules(MODEST_WINDOW(window));
1452 modest_ui_actions_check_toolbar_dimming_rules(MODEST_WINDOW(window));
1456 modest_msg_view_window_toolbar_on_transfer_mode (ModestMsgViewWindow *self)
1458 ModestMsgViewWindowPrivate *priv= NULL;
1460 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self), FALSE);
1461 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
1463 return priv->progress_hint;
1467 modest_msg_view_window_get_header (ModestMsgViewWindow *self)
1469 ModestMsgViewWindowPrivate *priv= NULL;
1471 TnyHeader *header = NULL;
1472 GtkTreePath *path = NULL;
1475 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self), NULL);
1476 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
1478 /* If the message was not obtained from a treemodel,
1479 * for instance if it was opened directly by the search UI:
1481 if (priv->header_model == NULL ||
1482 priv->row_reference == NULL ||
1483 !gtk_tree_row_reference_valid (priv->row_reference)) {
1484 msg = modest_msg_view_window_get_message (self);
1486 header = tny_msg_get_header (msg);
1487 g_object_unref (msg);
1492 /* Get iter of the currently selected message in the header view: */
1493 path = gtk_tree_row_reference_get_path (priv->row_reference);
1494 g_return_val_if_fail (path != NULL, NULL);
1495 gtk_tree_model_get_iter (priv->header_model,
1499 /* Get current message header */
1500 gtk_tree_model_get (priv->header_model, &iter,
1501 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
1504 gtk_tree_path_free (path);
1509 modest_msg_view_window_get_message (ModestMsgViewWindow *self)
1511 ModestMsgViewWindowPrivate *priv;
1513 g_return_val_if_fail (self, NULL);
1515 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(self);
1517 return tny_msg_view_get_msg (TNY_MSG_VIEW (priv->msg_view));
1521 modest_msg_view_window_get_message_uid (ModestMsgViewWindow *self)
1523 ModestMsgViewWindowPrivate *priv;
1525 g_return_val_if_fail (self, NULL);
1527 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
1529 return (const gchar*) priv->msg_uid;
1532 /* Used for the Ctrl+F accelerator */
1534 modest_msg_view_window_toggle_find_toolbar (GtkWidget *obj,
1537 ModestMsgViewWindow *window = MODEST_MSG_VIEW_WINDOW (data);
1538 ModestMsgViewWindowPrivate *priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1540 if (GTK_WIDGET_VISIBLE (priv->find_toolbar)) {
1541 modest_msg_view_window_find_toolbar_close (obj, data);
1543 modest_msg_view_window_show_find_toolbar (obj, data);
1547 /* Handler for menu option */
1549 modest_msg_view_window_show_find_toolbar (GtkWidget *obj,
1552 ModestMsgViewWindow *window = MODEST_MSG_VIEW_WINDOW (data);
1553 ModestMsgViewWindowPrivate *priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1555 gtk_widget_show (priv->find_toolbar);
1556 hildon_find_toolbar_highlight_entry (HILDON_FIND_TOOLBAR (priv->find_toolbar), TRUE);
1559 /* Handler for click on the "X" close button in find toolbar */
1561 modest_msg_view_window_find_toolbar_close (GtkWidget *widget,
1562 ModestMsgViewWindow *obj)
1564 ModestMsgViewWindowPrivate *priv;
1566 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (obj);
1569 gtk_widget_hide (priv->find_toolbar);
1570 modest_msg_view_grab_focus (MODEST_MSG_VIEW (priv->msg_view));
1574 modest_msg_view_window_find_toolbar_search (GtkWidget *widget,
1575 ModestMsgViewWindow *obj)
1577 gchar *current_search;
1578 ModestMsgViewWindowPrivate *priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (obj);
1580 if (modest_mime_part_view_is_empty (MODEST_MIME_PART_VIEW (priv->msg_view))) {
1581 hildon_banner_show_information (NULL, NULL, _("mail_ib_nothing_to_find"));
1585 g_object_get (G_OBJECT (widget), "prefix", ¤t_search, NULL);
1587 if ((current_search == NULL) || (strcmp (current_search, "") == 0)) {
1588 g_free (current_search);
1589 hildon_banner_show_information (NULL, NULL, _CS("ecdg_ib_find_rep_enter_text"));
1593 if ((priv->last_search == NULL) || (strcmp (priv->last_search, current_search) != 0)) {
1595 g_free (priv->last_search);
1596 priv->last_search = g_strdup (current_search);
1597 result = modest_isearch_view_search (MODEST_ISEARCH_VIEW (priv->msg_view),
1600 hildon_banner_show_information (NULL, NULL,
1601 _HL("ckct_ib_find_no_matches"));
1602 g_free (priv->last_search);
1603 priv->last_search = NULL;
1605 hildon_find_toolbar_highlight_entry (HILDON_FIND_TOOLBAR (priv->find_toolbar), TRUE);
1608 if (!modest_isearch_view_search_next (MODEST_ISEARCH_VIEW (priv->msg_view))) {
1609 hildon_banner_show_information (NULL, NULL,
1610 _HL("ckct_ib_find_search_complete"));
1611 g_free (priv->last_search);
1612 priv->last_search = NULL;
1614 hildon_find_toolbar_highlight_entry (HILDON_FIND_TOOLBAR (priv->find_toolbar), TRUE);
1618 g_free (current_search);
1623 modest_msg_view_window_set_zoom (ModestWindow *window,
1626 ModestMsgViewWindowPrivate *priv;
1627 ModestWindowPrivate *parent_priv;
1629 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window));
1631 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1632 parent_priv = MODEST_WINDOW_GET_PRIVATE (window);
1633 modest_zoomable_set_zoom (MODEST_ZOOMABLE (priv->msg_view), zoom);
1638 modest_msg_view_window_get_zoom (ModestWindow *window)
1640 ModestMsgViewWindowPrivate *priv;
1642 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), 1.0);
1644 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1645 return modest_zoomable_get_zoom (MODEST_ZOOMABLE (priv->msg_view));
1649 modest_msg_view_window_zoom_plus (ModestWindow *window)
1652 ModestMsgViewWindowPrivate *priv;
1656 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), 1.0);
1657 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1659 zoom_level = modest_zoomable_get_zoom (MODEST_ZOOMABLE (priv->msg_view));
1661 if (zoom_level >= 2.0) {
1662 hildon_banner_show_information (NULL, NULL,
1663 _CS("ckct_ib_max_zoom_level_reached"));
1665 } else if (zoom_level >= 1.5) {
1667 } else if (zoom_level >= 1.2) {
1669 } else if (zoom_level >= 1.0) {
1671 } else if (zoom_level >= 0.8) {
1673 } else if (zoom_level >= 0.5) {
1679 /* set zoom level */
1680 int_zoom = (gint) rint (zoom_level*100.0+0.1);
1681 banner_text = g_strdup_printf (_HL("wdgt_ib_zoom"), int_zoom);
1682 modest_platform_information_banner (GTK_WIDGET (window), NULL, banner_text);
1683 g_free (banner_text);
1684 modest_zoomable_set_zoom (MODEST_ZOOMABLE (priv->msg_view), zoom_level);
1690 modest_msg_view_window_zoom_minus (ModestWindow *window)
1693 ModestMsgViewWindowPrivate *priv;
1697 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), 1.0);
1698 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1700 zoom_level = modest_zoomable_get_zoom (MODEST_ZOOMABLE (priv->msg_view));
1702 if (zoom_level <= 0.5) {
1703 hildon_banner_show_information (NULL, NULL,
1704 _CS("ckct_ib_min_zoom_level_reached"));
1706 } else if (zoom_level <= 0.8) {
1708 } else if (zoom_level <= 1.0) {
1710 } else if (zoom_level <= 1.2) {
1712 } else if (zoom_level <= 1.5) {
1714 } else if (zoom_level <= 2.0) {
1720 /* set zoom level */
1721 int_zoom = (gint) rint (zoom_level*100.0+0.1);
1722 banner_text = g_strdup_printf (_HL("wdgt_ib_zoom"), int_zoom);
1723 modest_platform_information_banner (GTK_WIDGET (window), NULL, banner_text);
1724 g_free (banner_text);
1725 modest_zoomable_set_zoom (MODEST_ZOOMABLE (priv->msg_view), zoom_level);
1731 modest_msg_view_window_key_event (GtkWidget *window,
1737 focus = gtk_window_get_focus (GTK_WINDOW (window));
1739 /* for the find toolbar case */
1740 if (focus && GTK_IS_ENTRY (focus)) {
1741 if (event->keyval == GDK_BackSpace) {
1743 copy = gdk_event_copy ((GdkEvent *) event);
1744 gtk_widget_event (focus, copy);
1745 gdk_event_free (copy);
1755 modest_msg_view_window_last_message_selected (ModestMsgViewWindow *window)
1758 ModestMsgViewWindowPrivate *priv;
1759 GtkTreeIter tmp_iter;
1760 gboolean is_last_selected;
1762 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), TRUE);
1763 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1765 /*if no model (so no rows at all), then virtually we are the last*/
1766 if (!priv->header_model || !priv->row_reference)
1769 if (!gtk_tree_row_reference_valid (priv->row_reference))
1772 path = gtk_tree_row_reference_get_path (priv->row_reference);
1776 is_last_selected = TRUE;
1777 while (is_last_selected) {
1779 gtk_tree_path_next (path);
1780 if (!gtk_tree_model_get_iter (priv->header_model, &tmp_iter, path))
1782 gtk_tree_model_get (priv->header_model, &tmp_iter,
1783 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
1786 if (msg_is_visible (header, priv->is_outbox))
1787 is_last_selected = FALSE;
1788 g_object_unref(G_OBJECT(header));
1791 gtk_tree_path_free (path);
1792 return is_last_selected;
1796 modest_msg_view_window_has_headers_model (ModestMsgViewWindow *window)
1798 ModestMsgViewWindowPrivate *priv;
1800 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), TRUE);
1801 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1803 return priv->header_model != NULL;
1807 modest_msg_view_window_is_search_result (ModestMsgViewWindow *window)
1809 ModestMsgViewWindowPrivate *priv;
1811 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), TRUE);
1812 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1814 return priv->is_search_result;
1818 msg_is_visible (TnyHeader *header, gboolean check_outbox)
1820 if ((tny_header_get_flags(header) & TNY_HEADER_FLAG_DELETED))
1822 if (!check_outbox) {
1825 ModestTnySendQueueStatus status;
1826 status = modest_tny_all_send_queues_get_msg_status (header);
1827 return ((status != MODEST_TNY_SEND_QUEUE_FAILED) &&
1828 (status != MODEST_TNY_SEND_QUEUE_SENDING));
1833 modest_msg_view_window_first_message_selected (ModestMsgViewWindow *window)
1836 ModestMsgViewWindowPrivate *priv;
1837 gboolean is_first_selected;
1838 GtkTreeIter tmp_iter;
1840 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), TRUE);
1841 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1843 /*if no model (so no rows at all), then virtually we are the first*/
1844 if (!priv->header_model || !priv->row_reference)
1847 if (!gtk_tree_row_reference_valid (priv->row_reference))
1850 path = gtk_tree_row_reference_get_path (priv->row_reference);
1854 is_first_selected = TRUE;
1855 while (is_first_selected) {
1857 if(!gtk_tree_path_prev (path))
1859 /* Here the 'if' is needless for logic, but let make sure
1860 * iter is valid for gtk_tree_model_get. */
1861 if (!gtk_tree_model_get_iter (priv->header_model, &tmp_iter, path))
1863 gtk_tree_model_get (priv->header_model, &tmp_iter,
1864 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
1867 if (msg_is_visible (header, priv->is_outbox))
1868 is_first_selected = FALSE;
1869 g_object_unref(G_OBJECT(header));
1872 gtk_tree_path_free (path);
1873 return is_first_selected;
1880 GtkTreeRowReference *row_reference;
1884 message_reader_performer (gboolean canceled,
1886 GtkWindow *parent_window,
1887 TnyAccount *account,
1890 ModestMailOperation *mail_op = NULL;
1891 MsgReaderInfo *info;
1893 info = (MsgReaderInfo *) user_data;
1894 if (canceled || err) {
1895 update_window_title (MODEST_MSG_VIEW_WINDOW (parent_window));
1899 /* Register the header - it'll be unregistered in the callback */
1901 modest_window_mgr_register_header (modest_runtime_get_window_mgr (), info->header, NULL);
1903 /* New mail operation */
1904 mail_op = modest_mail_operation_new_with_error_handling (G_OBJECT(parent_window),
1905 modest_ui_actions_disk_operations_error_handler,
1908 modest_mail_operation_queue_add (modest_runtime_get_mail_operation_queue (), mail_op);
1910 modest_mail_operation_get_msg (mail_op, info->header, TRUE, view_msg_cb, info->row_reference);
1912 modest_mail_operation_find_msg (mail_op, info->folder, info->msg_uid, TRUE, view_msg_cb, NULL);
1913 g_object_unref (mail_op);
1915 /* Update dimming rules */
1916 modest_ui_actions_check_toolbar_dimming_rules (MODEST_WINDOW (parent_window));
1917 modest_ui_actions_check_menu_dimming_rules (MODEST_WINDOW (parent_window));
1920 /* Frees. The row_reference will be freed by the view_msg_cb callback */
1921 g_free (info->msg_uid);
1923 g_object_unref (info->folder);
1925 g_object_unref (info->header);
1926 g_slice_free (MsgReaderInfo, info);
1931 * Reads the message whose summary item is @header. It takes care of
1932 * several things, among others:
1934 * If the message was not previously downloaded then ask the user
1935 * before downloading. If there is no connection launch the connection
1936 * dialog. Update toolbar dimming rules.
1938 * Returns: TRUE if the mail operation was started, otherwise if the
1939 * user do not want to download the message, or if the user do not
1940 * want to connect, then the operation is not issued
1943 message_reader (ModestMsgViewWindow *window,
1944 ModestMsgViewWindowPrivate *priv,
1946 const gchar *msg_uid,
1948 GtkTreeRowReference *row_reference)
1950 ModestWindowMgr *mgr;
1951 TnyAccount *account = NULL;
1952 MsgReaderInfo *info;
1954 /* We set the header from model while we're loading */
1955 tny_header_view_set_header (TNY_HEADER_VIEW (priv->msg_view), header);
1956 gtk_window_set_title (GTK_WINDOW (window), _CS("ckdg_pb_updating"));
1962 g_object_ref (folder);
1964 mgr = modest_runtime_get_window_mgr ();
1965 /* Msg download completed */
1966 if (!header || !(tny_header_get_flags (header) & TNY_HEADER_FLAG_CACHED)) {
1968 /* Ask the user if he wants to download the message if
1970 if (!tny_device_is_online (modest_runtime_get_device())) {
1971 GtkResponseType response;
1973 response = modest_platform_run_confirmation_dialog (GTK_WINDOW (window),
1974 _("mcen_nc_get_msg"));
1975 if (response == GTK_RESPONSE_CANCEL) {
1976 update_window_title (window);
1981 folder = tny_header_get_folder (header);
1983 info = g_slice_new (MsgReaderInfo);
1984 info->msg_uid = g_strdup (msg_uid);
1986 info->header = g_object_ref (header);
1988 info->header = NULL;
1990 info->folder = g_object_ref (folder);
1992 info->folder = NULL;
1993 if (row_reference) {
1994 info->row_reference = gtk_tree_row_reference_copy (row_reference);
1996 info->row_reference = NULL;
1999 /* Offer the connection dialog if necessary */
2000 modest_platform_connect_if_remote_and_perform ((GtkWindow *) window,
2002 TNY_FOLDER_STORE (folder),
2003 message_reader_performer,
2006 g_object_unref (folder);
2012 folder = tny_header_get_folder (header);
2015 account = tny_folder_get_account (folder);
2017 info = g_slice_new (MsgReaderInfo);
2018 info->msg_uid = g_strdup (msg_uid);
2020 info->folder = g_object_ref (folder);
2022 info->folder = NULL;
2024 info->header = g_object_ref (header);
2026 info->header = NULL;
2028 info->row_reference = gtk_tree_row_reference_copy (row_reference);
2030 info->row_reference = NULL;
2032 message_reader_performer (FALSE, NULL, (GtkWindow *) window, account, info);
2034 g_object_unref (account);
2036 g_object_unref (folder);
2042 modest_msg_view_window_select_next_message (ModestMsgViewWindow *window)
2044 ModestMsgViewWindowPrivate *priv;
2045 GtkTreePath *path= NULL;
2046 GtkTreeIter tmp_iter;
2048 gboolean retval = TRUE;
2049 GtkTreeRowReference *row_reference = NULL;
2051 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), FALSE);
2052 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
2054 if (!priv->row_reference)
2057 /* Update the next row reference if it's not valid. This could
2058 happen if for example the header which it was pointing to,
2059 was deleted. The best place to do it is in the row-deleted
2060 handler but the tinymail model do not work like the glib
2061 tree models and reports the deletion when the row is still
2063 if (!gtk_tree_row_reference_valid (priv->next_row_reference)) {
2064 if (priv->next_row_reference) {
2065 gtk_tree_row_reference_free (priv->next_row_reference);
2067 if (gtk_tree_row_reference_valid (priv->row_reference)) {
2068 priv->next_row_reference = gtk_tree_row_reference_copy (priv->row_reference);
2069 select_next_valid_row (priv->header_model, &(priv->next_row_reference), FALSE, priv->is_outbox);
2071 priv->next_row_reference = NULL;
2074 if (priv->next_row_reference)
2075 path = gtk_tree_row_reference_get_path (priv->next_row_reference);
2079 row_reference = gtk_tree_row_reference_copy (priv->next_row_reference);
2081 gtk_tree_model_get_iter (priv->header_model,
2084 gtk_tree_path_free (path);
2086 gtk_tree_model_get (priv->header_model, &tmp_iter,
2087 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
2090 /* Read the message & show it */
2091 if (!message_reader (window, priv, header, NULL, NULL, row_reference)) {
2094 gtk_tree_row_reference_free (row_reference);
2097 g_object_unref (header);
2103 modest_msg_view_window_select_previous_message (ModestMsgViewWindow *window)
2105 ModestMsgViewWindowPrivate *priv = NULL;
2107 gboolean finished = FALSE;
2108 gboolean retval = FALSE;
2110 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), FALSE);
2111 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
2113 if (priv->row_reference && !gtk_tree_row_reference_valid (priv->row_reference)) {
2114 gtk_tree_row_reference_free (priv->row_reference);
2115 priv->row_reference = NULL;
2118 /* Return inmediatly if there is no header model */
2119 if (!priv->header_model || !priv->row_reference)
2122 path = gtk_tree_row_reference_get_path (priv->row_reference);
2123 while (!finished && gtk_tree_path_prev (path)) {
2127 gtk_tree_model_get_iter (priv->header_model, &iter, path);
2128 gtk_tree_model_get (priv->header_model, &iter,
2129 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
2133 if (msg_is_visible (header, priv->is_outbox)) {
2134 GtkTreeRowReference *row_reference;
2135 row_reference = gtk_tree_row_reference_new (priv->header_model, path);
2136 /* Read the message & show it */
2137 retval = message_reader (window, priv, header, NULL, NULL, row_reference);
2138 gtk_tree_row_reference_free (row_reference);
2142 g_object_unref (header);
2146 gtk_tree_path_free (path);
2151 view_msg_cb (ModestMailOperation *mail_op,
2158 ModestMsgViewWindow *self = NULL;
2159 ModestMsgViewWindowPrivate *priv = NULL;
2160 GtkTreeRowReference *row_reference = NULL;
2162 /* Unregister the header (it was registered before creating the mail operation) */
2163 modest_window_mgr_unregister_header (modest_runtime_get_window_mgr (), header);
2165 row_reference = (GtkTreeRowReference *) user_data;
2168 gtk_tree_row_reference_free (row_reference);
2169 self = (ModestMsgViewWindow *) modest_mail_operation_get_source (mail_op);
2171 /* Restore window title */
2172 update_window_title (self);
2173 modest_ui_actions_on_close_window (NULL, MODEST_WINDOW (self));
2174 g_object_unref (self);
2179 /* If there was any error */
2180 if (!modest_ui_actions_msg_retrieval_check (mail_op, header, msg)) {
2182 gtk_tree_row_reference_free (row_reference);
2183 self = (ModestMsgViewWindow *) modest_mail_operation_get_source (mail_op);
2185 /* Restore window title */
2186 update_window_title (self);
2187 modest_ui_actions_on_close_window (NULL, MODEST_WINDOW (self));
2188 g_object_unref (self);
2193 /* Get the window */
2194 self = (ModestMsgViewWindow *) modest_mail_operation_get_source (mail_op);
2195 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self));
2196 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
2198 /* Update the row reference */
2199 if (priv->row_reference != NULL) {
2200 gtk_tree_row_reference_free (priv->row_reference);
2201 priv->row_reference = (row_reference && gtk_tree_row_reference_valid (row_reference))?gtk_tree_row_reference_copy (row_reference):NULL;
2202 if (priv->next_row_reference != NULL) {
2203 gtk_tree_row_reference_free (priv->next_row_reference);
2205 if (priv->row_reference) {
2206 priv->next_row_reference = gtk_tree_row_reference_copy (priv->row_reference);
2207 select_next_valid_row (priv->header_model, &(priv->next_row_reference), TRUE, priv->is_outbox);
2209 priv->next_row_reference = NULL;
2213 /* Mark header as read */
2214 if (!(tny_header_get_flags (header) & TNY_HEADER_FLAG_SEEN))
2215 tny_header_set_flag (header, TNY_HEADER_FLAG_SEEN);
2217 /* Set new message */
2218 if (priv->msg_view != NULL && TNY_IS_MSG_VIEW (priv->msg_view)) {
2219 tny_msg_view_set_msg (TNY_MSG_VIEW (priv->msg_view), msg);
2220 modest_msg_view_window_update_priority (self);
2221 update_window_title (MODEST_MSG_VIEW_WINDOW (self));
2222 update_branding (MODEST_MSG_VIEW_WINDOW (self));
2223 modest_msg_view_grab_focus (MODEST_MSG_VIEW (priv->msg_view));
2226 /* Set the new message uid of the window */
2227 if (priv->msg_uid) {
2228 g_free (priv->msg_uid);
2229 priv->msg_uid = modest_tny_folder_get_header_unique_id (header);
2232 /* Notify the observers */
2233 g_signal_emit (G_OBJECT (self), signals[MSG_CHANGED_SIGNAL],
2234 0, priv->header_model, priv->row_reference);
2237 g_object_unref (self);
2239 gtk_tree_row_reference_free (row_reference);
2243 modest_msg_view_window_get_folder_type (ModestMsgViewWindow *window)
2245 ModestMsgViewWindowPrivate *priv;
2247 TnyFolderType folder_type;
2249 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
2251 folder_type = TNY_FOLDER_TYPE_UNKNOWN;
2253 msg = tny_msg_view_get_msg (TNY_MSG_VIEW (priv->msg_view));
2257 folder = tny_msg_get_folder (msg);
2259 folder_type = modest_tny_folder_guess_folder_type (folder);
2260 g_object_unref (folder);
2262 g_object_unref (msg);
2270 modest_msg_view_window_update_priority (ModestMsgViewWindow *window)
2272 ModestMsgViewWindowPrivate *priv;
2273 TnyHeader *header = NULL;
2274 TnyHeaderFlags flags = 0;
2276 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
2278 if (priv->header_model && priv->row_reference && gtk_tree_row_reference_valid (priv->row_reference)) {
2280 GtkTreePath *path = NULL;
2282 path = gtk_tree_row_reference_get_path (priv->row_reference);
2283 g_return_if_fail (path != NULL);
2284 gtk_tree_model_get_iter (priv->header_model,
2286 gtk_tree_row_reference_get_path (priv->row_reference));
2288 gtk_tree_model_get (priv->header_model, &iter, TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
2290 gtk_tree_path_free (path);
2293 msg = tny_msg_view_get_msg (TNY_MSG_VIEW (priv->msg_view));
2295 header = tny_msg_get_header (msg);
2296 g_object_unref (msg);
2301 flags = tny_header_get_flags (header);
2302 g_object_unref(G_OBJECT(header));
2305 modest_msg_view_set_priority (MODEST_MSG_VIEW(priv->msg_view), flags);
2310 toolbar_resize (ModestMsgViewWindow *self)
2312 ModestMsgViewWindowPrivate *priv = NULL;
2313 ModestWindowPrivate *parent_priv = NULL;
2315 gint static_button_size;
2316 ModestWindowMgr *mgr;
2318 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self));
2319 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
2320 parent_priv = MODEST_WINDOW_GET_PRIVATE(self);
2322 mgr = modest_runtime_get_window_mgr ();
2323 static_button_size = modest_window_mgr_get_fullscreen_mode (mgr)?120:120;
2325 if (parent_priv->toolbar) {
2326 /* Set expandable and homogeneous tool buttons */
2327 widget = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/ToolbarMessageReply");
2328 gtk_tool_item_set_expand (GTK_TOOL_ITEM (widget), TRUE);
2329 gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (widget), TRUE);
2330 widget = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/ToolbarMessageReplyAll");
2331 gtk_tool_item_set_expand (GTK_TOOL_ITEM (widget), TRUE);
2332 gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (widget), TRUE);
2333 widget = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/ToolbarMessageForward");
2334 gtk_tool_item_set_expand (GTK_TOOL_ITEM (widget), TRUE);
2335 gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (widget), TRUE);
2336 widget = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/ToolbarDeleteMessage");
2337 gtk_tool_item_set_expand (GTK_TOOL_ITEM (widget), TRUE);
2338 gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (widget), TRUE);
2339 widget = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/ToolbarDownloadExternalImages");
2340 gtk_tool_item_set_expand (GTK_TOOL_ITEM (widget), TRUE);
2341 gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (widget), TRUE);
2342 gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (priv->next_toolitem), TRUE);
2343 gtk_tool_item_set_expand (GTK_TOOL_ITEM (priv->next_toolitem), TRUE);
2344 gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (priv->prev_toolitem), TRUE);
2345 gtk_tool_item_set_expand (GTK_TOOL_ITEM (priv->prev_toolitem), TRUE);
2350 modest_msg_view_window_show_toolbar (ModestWindow *self,
2351 gboolean show_toolbar)
2353 ModestMsgViewWindowPrivate *priv = NULL;
2354 ModestWindowPrivate *parent_priv;
2356 parent_priv = MODEST_WINDOW_GET_PRIVATE(self);
2357 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(self);
2359 /* Set optimized view status */
2360 priv->optimized_view = !show_toolbar;
2362 if (!parent_priv->toolbar) {
2363 parent_priv->toolbar = gtk_ui_manager_get_widget (parent_priv->ui_manager,
2365 gtk_toolbar_set_icon_size (GTK_TOOLBAR (parent_priv->toolbar), HILDON_ICON_SIZE_FINGER);
2366 gtk_widget_set_no_show_all (parent_priv->toolbar, TRUE);
2368 priv->next_toolitem = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/ToolbarMessageNext");
2369 priv->prev_toolitem = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/ToolbarMessageBack");
2370 toolbar_resize (MODEST_MSG_VIEW_WINDOW (self));
2373 hildon_window_add_toolbar (HILDON_WINDOW (self),
2374 GTK_TOOLBAR (parent_priv->toolbar));
2379 /* Quick hack: this prevents toolbar icons "dance" when progress bar show status is changed */
2380 /* TODO: resize mode migth be GTK_RESIZE_QUEUE, in order to avoid unneccesary shows */
2381 gtk_container_set_resize_mode (GTK_CONTAINER(parent_priv->toolbar), GTK_RESIZE_IMMEDIATE);
2383 gtk_widget_show (GTK_WIDGET (parent_priv->toolbar));
2384 if (modest_msg_view_window_transfer_mode_enabled (MODEST_MSG_VIEW_WINDOW (self)))
2385 set_progress_hint (MODEST_MSG_VIEW_WINDOW (self), TRUE);
2387 set_progress_hint (MODEST_MSG_VIEW_WINDOW (self), FALSE);
2390 gtk_widget_set_no_show_all (parent_priv->toolbar, TRUE);
2391 gtk_widget_hide (GTK_WIDGET (parent_priv->toolbar));
2396 modest_msg_view_window_clipboard_owner_change (GtkClipboard *clipboard,
2398 ModestMsgViewWindow *window)
2400 if (!GTK_WIDGET_VISIBLE (window))
2403 modest_window_check_dimming_rules_group (MODEST_WINDOW (window), MODEST_DIMMING_RULES_CLIPBOARD);
2407 modest_msg_view_window_transfer_mode_enabled (ModestMsgViewWindow *self)
2409 ModestMsgViewWindowPrivate *priv;
2411 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self), FALSE);
2412 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(self);
2414 return priv->progress_hint;
2418 observers_empty (ModestMsgViewWindow *self)
2421 ModestMsgViewWindowPrivate *priv;
2422 gboolean is_empty = TRUE;
2423 guint pending_ops = 0;
2425 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(self);
2426 tmp = priv->progress_widgets;
2428 /* Check all observers */
2429 while (tmp && is_empty) {
2430 pending_ops = modest_progress_object_num_pending_operations (MODEST_PROGRESS_OBJECT(tmp->data));
2431 is_empty = pending_ops == 0;
2433 tmp = g_slist_next(tmp);
2440 on_account_removed (TnyAccountStore *account_store,
2441 TnyAccount *account,
2444 /* Do nothing if it's a transport account, because we only
2445 show the messages of a store account */
2446 if (tny_account_get_account_type(account) == TNY_ACCOUNT_TYPE_STORE) {
2447 const gchar *parent_acc = NULL;
2448 const gchar *our_acc = NULL;
2450 our_acc = modest_window_get_active_account (MODEST_WINDOW (user_data));
2451 parent_acc = modest_tny_account_get_parent_modest_account_name_for_server_account (account);
2453 /* Close this window if I'm showing a message of the removed account */
2454 if (our_acc && parent_acc && strcmp (parent_acc, our_acc) == 0)
2455 modest_ui_actions_on_close_window (NULL, MODEST_WINDOW (user_data));
2460 on_mail_operation_started (ModestMailOperation *mail_op,
2463 ModestMsgViewWindow *self;
2464 ModestMailOperationTypeOperation op_type;
2466 ModestMsgViewWindowPrivate *priv;
2467 GObject *source = NULL;
2469 self = MODEST_MSG_VIEW_WINDOW (user_data);
2470 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
2471 op_type = modest_mail_operation_get_type_operation (mail_op);
2472 tmp = priv->progress_widgets;
2473 source = modest_mail_operation_get_source(mail_op);
2474 if (G_OBJECT (self) == source) {
2475 if (op_type == MODEST_MAIL_OPERATION_TYPE_RECEIVE ||
2476 op_type == MODEST_MAIL_OPERATION_TYPE_OPEN ||
2477 op_type == MODEST_MAIL_OPERATION_TYPE_DELETE) {
2478 set_progress_hint (self, TRUE);
2480 modest_progress_object_add_operation (
2481 MODEST_PROGRESS_OBJECT (tmp->data),
2483 tmp = g_slist_next (tmp);
2487 g_object_unref (source);
2489 /* Update dimming rules */
2490 check_dimming_rules_after_change (self);
2494 on_mail_operation_finished (ModestMailOperation *mail_op,
2497 ModestMsgViewWindow *self;
2498 ModestMailOperationTypeOperation op_type;
2500 ModestMsgViewWindowPrivate *priv;
2502 self = MODEST_MSG_VIEW_WINDOW (user_data);
2503 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
2504 op_type = modest_mail_operation_get_type_operation (mail_op);
2505 tmp = priv->progress_widgets;
2507 if (op_type == MODEST_MAIL_OPERATION_TYPE_RECEIVE ||
2508 op_type == MODEST_MAIL_OPERATION_TYPE_OPEN ||
2509 op_type == MODEST_MAIL_OPERATION_TYPE_DELETE) {
2511 modest_progress_object_remove_operation (MODEST_PROGRESS_OBJECT (tmp->data),
2513 tmp = g_slist_next (tmp);
2516 /* If no more operations are being observed, NORMAL mode is enabled again */
2517 if (observers_empty (self)) {
2518 set_progress_hint (self, FALSE);
2522 /* Update dimming rules. We have to do this right here
2523 and not in view_msg_cb because at that point the
2524 transfer mode is still enabled so the dimming rule
2525 won't let the user delete the message that has been
2526 readed for example */
2527 check_dimming_rules_after_change (self);
2531 on_queue_changed (ModestMailOperationQueue *queue,
2532 ModestMailOperation *mail_op,
2533 ModestMailOperationQueueNotification type,
2534 ModestMsgViewWindow *self)
2536 ModestMsgViewWindowPrivate *priv;
2538 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
2540 /* If this operations was created by another window, do nothing */
2541 if (!modest_mail_operation_is_mine (mail_op, G_OBJECT(self)))
2544 if (type == MODEST_MAIL_OPERATION_QUEUE_OPERATION_ADDED) {
2545 priv->sighandlers = modest_signal_mgr_connect (priv->sighandlers,
2547 "operation-started",
2548 G_CALLBACK (on_mail_operation_started),
2550 priv->sighandlers = modest_signal_mgr_connect (priv->sighandlers,
2552 "operation-finished",
2553 G_CALLBACK (on_mail_operation_finished),
2555 } else if (type == MODEST_MAIL_OPERATION_QUEUE_OPERATION_REMOVED) {
2556 priv->sighandlers = modest_signal_mgr_disconnect (priv->sighandlers,
2558 "operation-started");
2559 priv->sighandlers = modest_signal_mgr_disconnect (priv->sighandlers,
2561 "operation-finished");
2566 modest_msg_view_window_get_attachments (ModestMsgViewWindow *win)
2568 ModestMsgViewWindowPrivate *priv;
2569 TnyList *selected_attachments = NULL;
2571 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (win), NULL);
2572 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (win);
2574 /* In Hildon 2.2 as there's no selection we assume we have all attachments selected */
2575 selected_attachments = modest_msg_view_get_attachments (MODEST_MSG_VIEW (priv->msg_view));
2577 return selected_attachments;
2581 ModestMsgViewWindow *self;
2583 gchar *attachment_uid;
2584 } DecodeAsyncHelper;
2587 on_decode_to_stream_async_handler (TnyMimePart *mime_part,
2593 DecodeAsyncHelper *helper = (DecodeAsyncHelper *) user_data;
2594 const gchar *content_type;
2596 if (cancelled || err) {
2599 if ((err->domain == TNY_ERROR_DOMAIN) &&
2600 (err->code == TNY_IO_ERROR_WRITE) &&
2601 (errno == ENOSPC)) {
2602 msg = g_strdup_printf (_KR("cerm_device_memory_full"), "");
2604 msg = g_strdup (_("mail_ib_file_operation_failed"));
2606 modest_platform_information_banner (NULL, NULL, msg);
2612 /* It could happen that the window was closed. So we
2613 assume it is a cancelation */
2614 if (!GTK_WIDGET_VISIBLE (helper->self))
2617 /* Remove the progress hint */
2618 set_progress_hint (helper->self, FALSE);
2620 content_type = tny_mime_part_get_content_type (mime_part);
2621 if (g_str_has_prefix (content_type, "message/rfc822")) {
2622 ModestWindowMgr *mgr;
2623 ModestWindow *msg_win = NULL;
2626 const gchar *mailbox;
2627 TnyStream *file_stream;
2630 fd = g_open (helper->file_path, O_RDONLY, 0644);
2632 file_stream = tny_fs_stream_new (fd);
2634 mgr = modest_runtime_get_window_mgr ();
2636 account = g_strdup (modest_window_get_active_account (MODEST_WINDOW (helper->self)));
2637 mailbox = modest_window_get_active_mailbox (MODEST_WINDOW (helper->self));
2640 account = modest_account_mgr_get_default_account (modest_runtime_get_account_mgr ());
2642 msg = tny_camel_msg_new ();
2643 tny_camel_msg_parse (msg, file_stream);
2644 msg_win = modest_msg_view_window_new_for_attachment (TNY_MSG (msg), account, mailbox, helper->attachment_uid);
2645 modest_window_set_zoom (MODEST_WINDOW (msg_win),
2646 modest_window_get_zoom (MODEST_WINDOW (helper->self)));
2647 if (modest_window_mgr_register_window (mgr, msg_win, MODEST_WINDOW (helper->self)))
2648 gtk_widget_show_all (GTK_WIDGET (msg_win));
2650 gtk_widget_destroy (GTK_WIDGET (msg_win));
2651 g_object_unref (msg);
2652 g_object_unref (file_stream);
2654 modest_platform_information_banner (NULL, NULL, _("mail_ib_file_operation_failed"));
2659 /* make the file read-only */
2660 g_chmod(helper->file_path, 0444);
2662 /* Activate the file */
2663 modest_platform_activate_file (helper->file_path, tny_mime_part_get_content_type (mime_part));
2668 g_object_unref (helper->self);
2669 g_free (helper->file_path);
2670 g_free (helper->attachment_uid);
2671 g_slice_free (DecodeAsyncHelper, helper);
2675 modest_msg_view_window_view_attachment (ModestMsgViewWindow *window,
2676 TnyMimePart *mime_part)
2678 ModestMsgViewWindowPrivate *priv;
2679 const gchar *msg_uid;
2680 gchar *attachment_uid = NULL;
2681 gint attachment_index = 0;
2682 TnyList *attachments;
2684 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window));
2685 g_return_if_fail (TNY_IS_MIME_PART (mime_part) || (mime_part == NULL));
2686 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
2688 msg_uid = modest_msg_view_window_get_message_uid (MODEST_MSG_VIEW_WINDOW (window));
2689 attachments = modest_msg_view_get_attachments (MODEST_MSG_VIEW (priv->msg_view));
2690 attachment_index = modest_list_index (attachments, (GObject *) mime_part);
2691 g_object_unref (attachments);
2693 if (msg_uid && attachment_index >= 0) {
2694 attachment_uid = g_strdup_printf ("%s/%d", msg_uid, attachment_index);
2697 if (mime_part == NULL) {
2698 gboolean error = FALSE;
2699 TnyList *selected_attachments = modest_msg_view_get_selected_attachments (MODEST_MSG_VIEW (priv->msg_view));
2700 if (selected_attachments == NULL || tny_list_get_length (selected_attachments) == 0) {
2702 } else if (tny_list_get_length (selected_attachments) > 1) {
2703 hildon_banner_show_information (NULL, NULL, _("mcen_ib_unable_to_display_more"));
2707 iter = tny_list_create_iterator (selected_attachments);
2708 mime_part = (TnyMimePart *) tny_iterator_get_current (iter);
2709 g_object_unref (iter);
2711 if (selected_attachments)
2712 g_object_unref (selected_attachments);
2717 g_object_ref (mime_part);
2720 if (tny_mime_part_is_purged (mime_part))
2723 if (!modest_tny_mime_part_is_msg (mime_part) && tny_mime_part_get_filename (mime_part)) {
2724 gchar *filepath = NULL;
2725 const gchar *att_filename = tny_mime_part_get_filename (mime_part);
2726 gboolean show_error_banner = FALSE;
2727 TnyFsStream *temp_stream = NULL;
2728 temp_stream = modest_utils_create_temp_stream (att_filename, attachment_uid,
2731 if (temp_stream != NULL) {
2732 ModestAccountMgr *mgr;
2733 DecodeAsyncHelper *helper;
2734 gboolean decode_in_provider;
2735 ModestProtocol *protocol;
2736 const gchar *account;
2738 /* Activate progress hint */
2739 set_progress_hint (window, TRUE);
2741 helper = g_slice_new0 (DecodeAsyncHelper);
2742 helper->self = g_object_ref (window);
2743 helper->file_path = g_strdup (filepath);
2744 helper->attachment_uid = g_strdup (attachment_uid);
2746 decode_in_provider = FALSE;
2747 mgr = modest_runtime_get_account_mgr ();
2748 account = modest_window_get_active_account (MODEST_WINDOW (window));
2749 if (modest_account_mgr_account_is_multimailbox (mgr, account, &protocol)) {
2750 if (MODEST_IS_ACCOUNT_PROTOCOL (protocol)) {
2752 uri = g_strconcat ("file://", filepath, NULL);
2753 decode_in_provider =
2754 modest_account_protocol_decode_part_to_stream_async (
2755 MODEST_ACCOUNT_PROTOCOL (protocol),
2758 TNY_STREAM (temp_stream),
2759 on_decode_to_stream_async_handler,
2766 if (!decode_in_provider)
2767 tny_mime_part_decode_to_stream_async (mime_part, TNY_STREAM (temp_stream),
2768 on_decode_to_stream_async_handler,
2771 g_object_unref (temp_stream);
2772 /* NOTE: files in the temporary area will be automatically
2773 * cleaned after some time if they are no longer in use */
2776 const gchar *content_type;
2777 /* the file may already exist but it isn't writable,
2778 * let's try to open it anyway */
2779 content_type = tny_mime_part_get_content_type (mime_part);
2780 modest_platform_activate_file (filepath, content_type);
2782 g_warning ("%s: modest_utils_create_temp_stream failed", __FUNCTION__);
2783 show_error_banner = TRUE;
2788 if (show_error_banner)
2789 modest_platform_information_banner (NULL, NULL, _("mail_ib_file_operation_failed"));
2790 } else if (!modest_tny_mime_part_is_msg (mime_part)) {
2791 ModestWindowMgr *mgr;
2792 ModestWindow *msg_win = NULL;
2793 TnyMsg *current_msg;
2797 current_msg = modest_msg_view_window_get_message (MODEST_MSG_VIEW_WINDOW (window));
2798 mgr = modest_runtime_get_window_mgr ();
2799 header = tny_msg_get_header (TNY_MSG (current_msg));
2800 found = modest_window_mgr_find_registered_message_uid (mgr,
2805 g_debug ("window for this body is already being created");
2808 /* it's not found, so create a new window for it */
2809 modest_window_mgr_register_header (mgr, header, attachment_uid); /* register the uid before building the window */
2810 gchar *account = g_strdup (modest_window_get_active_account (MODEST_WINDOW (window)));
2811 const gchar *mailbox = modest_window_get_active_mailbox (MODEST_WINDOW (window));
2813 account = modest_account_mgr_get_default_account (modest_runtime_get_account_mgr ());
2815 msg_win = modest_msg_view_window_new_with_other_body (TNY_MSG (current_msg), TNY_MIME_PART (mime_part),
2816 account, mailbox, attachment_uid);
2818 modest_window_set_zoom (MODEST_WINDOW (msg_win),
2819 modest_window_get_zoom (MODEST_WINDOW (window)));
2820 if (modest_window_mgr_register_window (mgr, msg_win, MODEST_WINDOW (window)))
2821 gtk_widget_show_all (GTK_WIDGET (msg_win));
2823 gtk_widget_destroy (GTK_WIDGET (msg_win));
2825 g_object_unref (current_msg);
2827 /* message attachment */
2828 TnyHeader *header = NULL;
2829 ModestWindowMgr *mgr;
2830 ModestWindow *msg_win = NULL;
2833 header = tny_msg_get_header (TNY_MSG (mime_part));
2834 mgr = modest_runtime_get_window_mgr ();
2835 found = modest_window_mgr_find_registered_header (mgr, header, &msg_win);
2838 /* if it's found, but there is no msg_win, it's probably in the process of being created;
2839 * thus, we don't do anything */
2840 g_debug ("window for is already being created");
2842 /* it's not found, so create a new window for it */
2843 modest_window_mgr_register_header (mgr, header, attachment_uid); /* register the uid before building the window */
2844 gchar *account = g_strdup (modest_window_get_active_account (MODEST_WINDOW (window)));
2845 const gchar *mailbox = modest_window_get_active_mailbox (MODEST_WINDOW (window));
2847 account = modest_account_mgr_get_default_account (modest_runtime_get_account_mgr ());
2848 msg_win = modest_msg_view_window_new_for_attachment (TNY_MSG (mime_part), account,
2849 mailbox, attachment_uid);
2850 modest_window_set_zoom (MODEST_WINDOW (msg_win),
2851 modest_window_get_zoom (MODEST_WINDOW (window)));
2852 if (modest_window_mgr_register_window (mgr, msg_win, MODEST_WINDOW (window)))
2853 gtk_widget_show_all (GTK_WIDGET (msg_win));
2855 gtk_widget_destroy (GTK_WIDGET (msg_win));
2861 g_free (attachment_uid);
2863 g_object_unref (mime_part);
2875 GnomeVFSResult result;
2877 ModestMsgViewWindow *window;
2880 static void save_mime_part_info_free (SaveMimePartInfo *info, gboolean with_struct);
2881 static gboolean idle_save_mime_part_show_result (SaveMimePartInfo *info);
2882 static gpointer save_mime_part_to_file (SaveMimePartInfo *info);
2883 static void save_mime_parts_to_file_with_checks (GtkWindow *parent, SaveMimePartInfo *info);
2886 save_mime_part_info_free (SaveMimePartInfo *info, gboolean with_struct)
2890 for (node = info->pairs; node != NULL; node = g_list_next (node)) {
2891 SaveMimePartPair *pair = (SaveMimePartPair *) node->data;
2892 g_free (pair->filename);
2893 g_object_unref (pair->part);
2894 g_slice_free (SaveMimePartPair, pair);
2896 g_list_free (info->pairs);
2899 g_object_unref (info->window);
2900 info->window = NULL;
2902 g_slice_free (SaveMimePartInfo, info);
2907 idle_save_mime_part_show_result (SaveMimePartInfo *info)
2909 /* This is a GDK lock because we are an idle callback and
2910 * hildon_banner_show_information is or does Gtk+ code */
2912 gdk_threads_enter (); /* CHECKED */
2913 if (info->result == GNOME_VFS_OK) {
2914 hildon_banner_show_information (NULL, NULL, _CS("sfil_ib_saved"));
2915 } else if (info->result == GNOME_VFS_ERROR_NO_SPACE) {
2918 /* Check if the uri belongs to the external mmc */
2919 if (g_str_has_prefix (info->uri, g_getenv (MODEST_MMC1_VOLUMEPATH_ENV)))
2920 msg = g_strdup_printf (_KR("cerm_device_memory_full"), "");
2922 msg = g_strdup (_KR("cerm_memory_card_full"));
2923 modest_platform_information_banner (NULL, NULL, msg);
2926 hildon_banner_show_information (NULL, NULL, _("mail_ib_file_operation_failed"));
2928 save_mime_part_info_free (info, FALSE);
2929 gdk_threads_leave (); /* CHECKED */
2935 save_mime_part_to_file (SaveMimePartInfo *info)
2937 GnomeVFSHandle *handle;
2939 SaveMimePartPair *pair = (SaveMimePartPair *) info->pairs->data;
2941 info->result = gnome_vfs_create (&handle, pair->filename, GNOME_VFS_OPEN_WRITE, FALSE, 0644);
2942 if (info->result == GNOME_VFS_OK) {
2943 GError *error = NULL;
2944 gboolean decode_in_provider;
2946 ModestAccountMgr *mgr;
2947 const gchar *account;
2948 ModestProtocol *protocol = NULL;
2950 stream = tny_vfs_stream_new (handle);
2952 decode_in_provider = FALSE;
2953 mgr = modest_runtime_get_account_mgr ();
2954 account = modest_window_get_active_account (MODEST_WINDOW (info->window));
2955 if (modest_account_mgr_account_is_multimailbox (mgr, account, &protocol)) {
2956 if (MODEST_IS_ACCOUNT_PROTOCOL (protocol)) {
2957 decode_in_provider =
2958 modest_account_protocol_decode_part_to_stream (
2959 MODEST_ACCOUNT_PROTOCOL (protocol),
2967 if (!decode_in_provider)
2968 written = tny_mime_part_decode_to_stream (pair->part, stream, &error);
2971 g_warning ("modest: could not save attachment %s: %d (%s)\n", pair->filename, error?error->code:-1, error?error->message:"Unknown error");
2973 if ((error->domain == TNY_ERROR_DOMAIN) &&
2974 (error->code == TNY_IO_ERROR_WRITE) &&
2975 (errno == ENOSPC)) {
2976 info->result = GNOME_VFS_ERROR_NO_SPACE;
2978 info->result = GNOME_VFS_ERROR_IO;
2981 g_object_unref (G_OBJECT (stream));
2983 g_warning ("Could not create save attachment %s: %s\n",
2984 pair->filename, gnome_vfs_result_to_string (info->result));
2987 /* Go on saving remaining files */
2988 info->pairs = g_list_remove_link (info->pairs, info->pairs);
2989 if (info->pairs != NULL) {
2990 save_mime_part_to_file (info);
2992 g_idle_add ((GSourceFunc) idle_save_mime_part_show_result, info);
2999 save_mime_parts_to_file_with_checks (GtkWindow *parent,
3000 SaveMimePartInfo *info)
3002 gboolean is_ok = TRUE;
3003 gint replaced_files = 0;
3004 const GList *files = info->pairs;
3005 const GList *iter, *to_replace = NULL;
3007 for (iter = files; (iter != NULL) && (replaced_files < 2); iter = g_list_next(iter)) {
3008 SaveMimePartPair *pair = iter->data;
3009 gchar *unescaped = g_uri_unescape_string (pair->filename, NULL);
3011 if (modest_utils_file_exists (unescaped)) {
3013 if (replaced_files == 1)
3018 if (replaced_files) {
3021 if (replaced_files == 1) {
3022 SaveMimePartPair *pair = to_replace->data;
3023 const gchar *basename = strrchr (pair->filename, G_DIR_SEPARATOR) + 1;
3024 gchar *escaped_basename, *message;
3026 escaped_basename = g_uri_unescape_string (basename, NULL);
3027 message = g_strdup_printf ("%s\n%s",
3028 _FM("docm_nc_replace_file"),
3029 (escaped_basename) ? escaped_basename : "");
3030 response = modest_platform_run_confirmation_dialog (parent, message);
3032 g_free (escaped_basename);
3034 response = modest_platform_run_confirmation_dialog (parent,
3035 _FM("docm_nc_replace_multiple"));
3037 if (response != GTK_RESPONSE_OK)
3042 save_mime_part_info_free (info, TRUE);
3044 g_thread_create ((GThreadFunc)save_mime_part_to_file, info, FALSE, NULL);
3049 typedef struct _SaveAttachmentsInfo {
3050 TnyList *attachments_list;
3051 ModestMsgViewWindow *window;
3052 } SaveAttachmentsInfo;
3055 save_attachments_response (GtkDialog *dialog,
3059 TnyList *mime_parts;
3061 GList *files_to_save = NULL;
3062 gchar *current_folder;
3063 SaveAttachmentsInfo *sa_info = (SaveAttachmentsInfo *) user_data;
3065 mime_parts = TNY_LIST (sa_info->attachments_list);
3067 if (arg1 != GTK_RESPONSE_OK)
3070 chooser_uri = gtk_file_chooser_get_uri (GTK_FILE_CHOOSER (dialog));
3071 current_folder = gtk_file_chooser_get_current_folder_uri (GTK_FILE_CHOOSER (dialog));
3072 if (current_folder && *current_folder != '\0') {
3074 modest_conf_set_string (modest_runtime_get_conf (), MODEST_CONF_LATEST_SAVE_ATTACHMENT_PATH,
3075 current_folder,&err);
3077 g_debug ("Error storing latest used folder: %s", err->message);
3081 g_free (current_folder);
3083 if (!modest_utils_folder_writable (chooser_uri)) {
3084 const gchar *err_msg;
3086 #ifdef MODEST_PLATFORM_MAEMO
3087 if (modest_maemo_utils_in_usb_mode ()) {
3088 err_msg = dgettext ("hildon-status-bar-usb", "usbh_ib_mmc_usb_connected");
3090 err_msg = _FM("sfil_ib_readonly_location");
3093 err_msg = _FM("sfil_ib_readonly_location");
3095 hildon_banner_show_information (NULL, NULL, err_msg);
3099 iter = tny_list_create_iterator (mime_parts);
3100 while (!tny_iterator_is_done (iter)) {
3101 TnyMimePart *mime_part = (TnyMimePart *) tny_iterator_get_current (iter);
3103 if ((modest_tny_mime_part_is_attachment_for_modest (mime_part)) &&
3104 !tny_mime_part_is_purged (mime_part) &&
3105 (tny_mime_part_get_filename (mime_part) != NULL)) {
3106 SaveMimePartPair *pair;
3108 pair = g_slice_new0 (SaveMimePartPair);
3110 if (tny_list_get_length (mime_parts) > 1) {
3112 gnome_vfs_escape_slashes (tny_mime_part_get_filename (mime_part));
3113 pair->filename = g_build_filename (chooser_uri, escaped, NULL);
3116 pair->filename = g_strdup (chooser_uri);
3118 pair->part = mime_part;
3119 files_to_save = g_list_prepend (files_to_save, pair);
3121 tny_iterator_next (iter);
3123 g_object_unref (iter);
3126 if (files_to_save != NULL) {
3127 SaveMimePartInfo *info = g_slice_new0 (SaveMimePartInfo);
3128 info->pairs = files_to_save;
3129 info->result = TRUE;
3130 info->uri = g_strdup (chooser_uri);
3131 info->window = g_object_ref (sa_info->window);
3132 save_mime_parts_to_file_with_checks ((GtkWindow *) dialog, info);
3134 g_free (chooser_uri);
3137 /* Free and close the dialog */
3138 g_object_unref (mime_parts);
3139 g_object_unref (sa_info->window);
3140 g_slice_free (SaveAttachmentsInfo, sa_info);
3141 gtk_widget_destroy (GTK_WIDGET (dialog));
3145 modest_msg_view_window_save_attachments (ModestMsgViewWindow *window,
3146 TnyList *mime_parts)
3148 ModestMsgViewWindowPrivate *priv;
3149 GtkWidget *save_dialog = NULL;
3150 gchar *conf_folder = NULL;
3151 gchar *filename = NULL;
3152 gchar *save_multiple_str = NULL;
3153 const gchar *root_folder = "file:///";
3155 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window));
3156 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
3158 if (mime_parts == NULL) {
3159 /* In Hildon 2.2 save and delete operate over all the attachments as there's no
3160 * selection available */
3161 mime_parts = modest_msg_view_get_attachments (MODEST_MSG_VIEW (priv->msg_view));
3162 if (mime_parts && !modest_maemo_utils_select_attachments (GTK_WINDOW (window), mime_parts, FALSE)) {
3163 g_object_unref (mime_parts);
3166 if (mime_parts == NULL || tny_list_get_length (mime_parts) == 0) {
3168 g_object_unref (mime_parts);
3174 g_object_ref (mime_parts);
3177 /* prepare dialog */
3178 if (tny_list_get_length (mime_parts) == 1) {
3180 /* only one attachment selected */
3181 iter = tny_list_create_iterator (mime_parts);
3182 TnyMimePart *mime_part = (TnyMimePart *) tny_iterator_get_current (iter);
3183 g_object_unref (iter);
3184 if (!modest_tny_mime_part_is_msg (mime_part) &&
3185 modest_tny_mime_part_is_attachment_for_modest (mime_part) &&
3186 !tny_mime_part_is_purged (mime_part)) {
3187 filename = g_strdup (tny_mime_part_get_filename (mime_part));
3189 /* TODO: show any error? */
3190 g_warning ("%s: Tried to save a non-file attachment", __FUNCTION__);
3191 g_object_unref (mime_parts);
3194 g_object_unref (mime_part);
3196 gint num = tny_list_get_length (mime_parts);
3197 save_multiple_str = g_strdup_printf (dngettext("hildon-fm",
3198 "sfil_va_number_of_objects_attachment",
3199 "sfil_va_number_of_objects_attachments",
3203 save_dialog = hildon_file_chooser_dialog_new (GTK_WINDOW (window),
3204 GTK_FILE_CHOOSER_ACTION_SAVE);
3206 /* Get last used folder */
3207 conf_folder = modest_conf_get_string (modest_runtime_get_conf (),
3208 MODEST_CONF_LATEST_SAVE_ATTACHMENT_PATH, NULL);
3210 /* File chooser stops working if we select "file:///" as current folder */
3211 if (conf_folder && g_ascii_strcasecmp (root_folder, conf_folder) != 0) {
3212 g_free (conf_folder);
3216 if (conf_folder && conf_folder[0] != '\0') {
3217 gtk_file_chooser_set_current_folder_uri (GTK_FILE_CHOOSER (save_dialog), conf_folder);
3220 /* Set the default folder to documents folder */
3221 docs_folder = (gchar *) g_strdup(g_get_user_special_dir (G_USER_DIRECTORY_DOCUMENTS));
3224 docs_folder = g_build_filename (g_getenv (MYDOCS_ENV), DOCS_FOLDER, NULL);
3226 gtk_file_chooser_set_current_folder (GTK_FILE_CHOOSER (save_dialog), docs_folder);
3227 g_free (docs_folder);
3229 g_free (conf_folder);
3233 gtk_file_chooser_set_current_name (GTK_FILE_CHOOSER (save_dialog),
3238 /* if multiple, set multiple string */
3239 if (save_multiple_str) {
3240 g_object_set (G_OBJECT (save_dialog), "save-multiple", save_multiple_str, NULL);
3241 gtk_window_set_title (GTK_WINDOW (save_dialog), _FM("sfil_ti_save_objects_files"));
3242 g_free (save_multiple_str);
3245 /* We must run this asynchronously, because the hildon dialog
3246 performs a gtk_dialog_run by itself which leads to gdk
3248 SaveAttachmentsInfo *sa_info;
3249 sa_info = g_slice_new (SaveAttachmentsInfo);
3250 sa_info->attachments_list = mime_parts;
3251 sa_info->window = g_object_ref (window);
3252 g_signal_connect (save_dialog, "response",
3253 G_CALLBACK (save_attachments_response), sa_info);
3255 gtk_widget_show_all (save_dialog);
3259 show_remove_attachment_information (gpointer userdata)
3261 ModestMsgViewWindow *window = (ModestMsgViewWindow *) userdata;
3262 ModestMsgViewWindowPrivate *priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
3264 /* We're outside the main lock */
3265 gdk_threads_enter ();
3267 if (priv->remove_attachment_banner != NULL) {
3268 gtk_widget_destroy (priv->remove_attachment_banner);
3269 g_object_unref (priv->remove_attachment_banner);
3272 priv->remove_attachment_banner = g_object_ref (
3273 hildon_banner_show_animation (NULL, NULL, _("mcen_me_inbox_remove_attachments")));
3275 gdk_threads_leave ();
3281 modest_msg_view_window_remove_attachments (ModestMsgViewWindow *window, gboolean get_all)
3283 ModestMsgViewWindowPrivate *priv;
3284 TnyList *mime_parts = NULL, *tmp;
3285 gchar *confirmation_message;
3291 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window));
3292 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
3294 /* In hildon 2.2 we ignore the get_all flag as we always get all attachments. This is
3295 * because we don't have selection
3297 mime_parts = modest_msg_view_get_attachments (MODEST_MSG_VIEW (priv->msg_view));
3299 /* Remove already purged messages from mime parts list. We use
3300 a copy of the list to remove items in the original one */
3301 tmp = tny_list_copy (mime_parts);
3302 iter = tny_list_create_iterator (tmp);
3303 while (!tny_iterator_is_done (iter)) {
3304 TnyMimePart *part = TNY_MIME_PART (tny_iterator_get_current (iter));
3305 if (tny_mime_part_is_purged (part))
3306 tny_list_remove (mime_parts, (GObject *) part);
3308 g_object_unref (part);
3309 tny_iterator_next (iter);
3311 g_object_unref (tmp);
3312 g_object_unref (iter);
3314 if (!modest_maemo_utils_select_attachments (GTK_WINDOW (window), mime_parts, TRUE) ||
3315 tny_list_get_length (mime_parts) == 0) {
3316 g_object_unref (mime_parts);
3320 n_attachments = tny_list_get_length (mime_parts);
3321 if (n_attachments == 1) {
3325 iter = tny_list_create_iterator (mime_parts);
3326 part = (TnyMimePart *) tny_iterator_get_current (iter);
3327 g_object_unref (iter);
3328 if (modest_tny_mime_part_is_msg (part)) {
3330 header = tny_msg_get_header (TNY_MSG (part));
3331 filename = tny_header_dup_subject (header);
3332 g_object_unref (header);
3333 if (filename == NULL)
3334 filename = g_strdup (_("mail_va_no_subject"));
3336 filename = g_strdup (tny_mime_part_get_filename (TNY_MIME_PART (part)));
3338 confirmation_message = g_strdup_printf (_("mcen_nc_purge_file_text"), filename);
3340 g_object_unref (part);
3342 confirmation_message = g_strdup_printf (ngettext("mcen_nc_purge_file_text",
3343 "mcen_nc_purge_files_text",
3344 n_attachments), n_attachments);
3346 response = modest_platform_run_confirmation_dialog (GTK_WINDOW (window),
3347 confirmation_message);
3348 g_free (confirmation_message);
3350 if (response != GTK_RESPONSE_OK) {
3351 g_object_unref (mime_parts);
3355 priv->purge_timeout = g_timeout_add (2000, show_remove_attachment_information, window);
3357 iter = tny_list_create_iterator (mime_parts);
3358 while (!tny_iterator_is_done (iter)) {
3361 part = (TnyMimePart *) tny_iterator_get_current (iter);
3362 tny_mime_part_set_purged (TNY_MIME_PART (part));
3363 g_object_unref (part);
3364 tny_iterator_next (iter);
3366 g_object_unref (iter);
3368 msg = tny_msg_view_get_msg (TNY_MSG_VIEW (priv->msg_view));
3369 tny_msg_view_clear (TNY_MSG_VIEW (priv->msg_view));
3370 tny_msg_rewrite_cache (msg);
3371 tny_msg_view_set_msg (TNY_MSG_VIEW (priv->msg_view), msg);
3372 g_object_unref (msg);
3373 update_branding (MODEST_MSG_VIEW_WINDOW (window));
3375 g_object_unref (mime_parts);
3377 if (priv->purge_timeout > 0) {
3378 g_source_remove (priv->purge_timeout);
3379 priv->purge_timeout = 0;
3382 if (priv->remove_attachment_banner) {
3383 gtk_widget_destroy (priv->remove_attachment_banner);
3384 g_object_unref (priv->remove_attachment_banner);
3385 priv->remove_attachment_banner = NULL;
3391 update_window_title (ModestMsgViewWindow *window)
3393 ModestMsgViewWindowPrivate *priv;
3395 TnyHeader *header = NULL;
3396 gchar *subject = NULL;
3398 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
3400 /* Note that if the window is closed while we're retrieving
3401 the message, this widget could de deleted */
3402 if (!priv->msg_view)
3405 msg = tny_msg_view_get_msg (TNY_MSG_VIEW (priv->msg_view));
3407 if (priv->other_body) {
3410 description = modest_tny_mime_part_get_header_value (priv->other_body, "Content-Description");
3412 g_strstrip (description);
3413 subject = description;
3415 } else if (msg != NULL) {
3416 header = tny_msg_get_header (msg);
3417 subject = tny_header_dup_subject (header);
3418 g_object_unref (header);
3419 g_object_unref (msg);
3422 if ((subject == NULL)||(subject[0] == '\0')) {
3424 subject = g_strdup (_("mail_va_no_subject"));
3427 gtk_window_set_title (GTK_WINDOW (window), subject);
3432 on_move_focus (GtkWidget *widget,
3433 GtkDirectionType direction,
3436 g_signal_stop_emission_by_name (G_OBJECT (widget), "move-focus");
3440 fetch_image_open_stream (TnyStreamCache *self, gint64 *expected_size, gchar *uri)
3442 GnomeVFSResult result;
3443 GnomeVFSHandle *handle = NULL;
3444 GnomeVFSFileInfo *info = NULL;
3447 result = gnome_vfs_open (&handle, uri, GNOME_VFS_OPEN_READ);
3448 if (result != GNOME_VFS_OK) {
3453 info = gnome_vfs_file_info_new ();
3454 result = gnome_vfs_get_file_info_from_handle (handle, info, GNOME_VFS_FILE_INFO_DEFAULT);
3455 if (result != GNOME_VFS_OK || ! (info->valid_fields & GNOME_VFS_FILE_INFO_FIELDS_SIZE)) {
3456 /* We put a "safe" default size for going to cache */
3457 *expected_size = (300*1024);
3459 *expected_size = info->size;
3461 gnome_vfs_file_info_unref (info);
3463 stream = tny_vfs_stream_new (handle);
3472 TnyStream *output_stream;
3473 GtkWidget *msg_view;
3478 on_fetch_image_idle_refresh_view (gpointer userdata)
3481 FetchImageData *fidata = (FetchImageData *) userdata;
3483 gdk_threads_enter ();
3484 if (GTK_WIDGET_DRAWABLE (fidata->msg_view)) {
3485 ModestMsgViewWindowPrivate *priv;
3487 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (fidata->window);
3488 priv->fetching_images--;
3489 gtk_widget_queue_draw (fidata->msg_view);
3490 update_progress_hint (MODEST_MSG_VIEW_WINDOW (fidata->window));
3492 gdk_threads_leave ();
3494 g_object_unref (fidata->msg_view);
3495 g_object_unref (fidata->window);
3496 g_slice_free (FetchImageData, fidata);
3501 on_fetch_image_thread (gpointer userdata)
3503 FetchImageData *fidata = (FetchImageData *) userdata;
3504 TnyStreamCache *cache;
3505 TnyStream *cache_stream;
3507 cache = modest_runtime_get_images_cache ();
3509 tny_stream_cache_get_stream (cache,
3511 (TnyStreamCacheOpenStreamFetcher) fetch_image_open_stream,
3512 (gpointer) fidata->uri);
3513 g_free (fidata->cache_id);
3514 g_free (fidata->uri);
3516 if (cache_stream != NULL) {
3519 while (G_LIKELY (!tny_stream_is_eos (cache_stream))) {
3522 nb_read = tny_stream_read (cache_stream, buffer, sizeof (buffer));
3523 if (G_UNLIKELY (nb_read < 0)) {
3525 } else if (G_LIKELY (nb_read > 0)) {
3526 gssize nb_written = 0;
3528 while (G_UNLIKELY (nb_written < nb_read)) {
3531 len = tny_stream_write (fidata->output_stream, buffer + nb_written,
3532 nb_read - nb_written);
3533 if (G_UNLIKELY (len < 0))
3539 tny_stream_close (cache_stream);
3540 g_object_unref (cache_stream);
3543 tny_stream_close (fidata->output_stream);
3544 g_object_unref (fidata->output_stream);
3546 g_idle_add (on_fetch_image_idle_refresh_view, fidata);
3552 on_fetch_image (ModestMsgView *msgview,
3555 ModestMsgViewWindow *window)
3557 const gchar *current_account;
3558 ModestMsgViewWindowPrivate *priv;
3559 FetchImageData *fidata;
3561 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
3563 current_account = modest_window_get_active_account (MODEST_WINDOW (window));
3565 fidata = g_slice_new0 (FetchImageData);
3566 fidata->msg_view = g_object_ref (msgview);
3567 fidata->window = g_object_ref (window);
3568 fidata->uri = g_strdup (uri);
3569 fidata->cache_id = modest_images_cache_get_id (current_account, uri);
3570 fidata->output_stream = g_object_ref (stream);
3572 priv->fetching_images++;
3573 if (g_thread_create (on_fetch_image_thread, fidata, FALSE, NULL) == NULL) {
3574 g_object_unref (fidata->output_stream);
3575 g_free (fidata->cache_id);
3576 g_free (fidata->uri);
3577 g_object_unref (fidata->msg_view);
3578 g_slice_free (FetchImageData, fidata);
3579 tny_stream_close (stream);
3580 priv->fetching_images--;
3581 update_progress_hint (window);
3584 update_progress_hint (window);
3590 setup_menu (ModestMsgViewWindow *self)
3592 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW(self));
3594 /* Settings menu buttons */
3595 modest_hildon2_window_add_to_menu (MODEST_HILDON2_WINDOW (self), _("mcen_me_viewer_find"), NULL,
3596 APP_MENU_CALLBACK (modest_msg_view_window_show_find_toolbar),
3597 MODEST_DIMMING_CALLBACK (modest_ui_dimming_rules_on_find_in_msg));
3599 modest_hildon2_window_add_to_menu (MODEST_HILDON2_WINDOW (self),
3600 dngettext(GETTEXT_PACKAGE,
3601 "mcen_me_move_message",
3602 "mcen_me_move_messages",
3605 APP_MENU_CALLBACK (modest_ui_actions_on_move_to),
3606 MODEST_DIMMING_CALLBACK (modest_ui_dimming_rules_on_move_to));
3608 modest_hildon2_window_add_to_menu (MODEST_HILDON2_WINDOW (self), _("mcen_me_inbox_mark_as_read"), NULL,
3609 APP_MENU_CALLBACK (modest_ui_actions_on_mark_as_read),
3610 MODEST_DIMMING_CALLBACK (modest_ui_dimming_rules_on_mark_as_read_msg_in_view));
3612 modest_hildon2_window_add_to_menu (MODEST_HILDON2_WINDOW (self), _("mcen_me_inbox_mark_as_unread"), NULL,
3613 APP_MENU_CALLBACK (modest_ui_actions_on_mark_as_unread),
3614 MODEST_DIMMING_CALLBACK (modest_ui_dimming_rules_on_mark_as_unread_msg_in_view));
3616 modest_hildon2_window_add_to_menu (MODEST_HILDON2_WINDOW (self), _("mcen_me_viewer_save_attachments"), NULL,
3617 APP_MENU_CALLBACK (modest_ui_actions_save_attachments),
3618 MODEST_DIMMING_CALLBACK (modest_ui_dimming_rules_on_save_attachments));
3619 modest_hildon2_window_add_to_menu (MODEST_HILDON2_WINDOW (self), _("mcen_me_inbox_remove_attachments"), NULL,
3620 APP_MENU_CALLBACK (modest_ui_actions_remove_attachments),
3621 MODEST_DIMMING_CALLBACK (modest_ui_dimming_rules_on_remove_attachments));
3623 modest_hildon2_window_add_to_menu (MODEST_HILDON2_WINDOW (self), _("mcen_me_new_message"), "<Control>n",
3624 APP_MENU_CALLBACK (modest_ui_actions_on_new_msg),
3625 MODEST_DIMMING_CALLBACK (modest_ui_dimming_rules_on_new_msg));
3626 modest_hildon2_window_add_to_menu (MODEST_HILDON2_WINDOW (self), _("mcen_me_viewer_addtocontacts"), NULL,
3627 APP_MENU_CALLBACK (modest_ui_actions_add_to_contacts),
3628 MODEST_DIMMING_CALLBACK (modest_ui_dimming_rules_on_add_to_contacts));
3630 modest_hildon2_window_add_to_menu (MODEST_HILDON2_WINDOW (self), _("mcen_ti_message_properties"), NULL,
3631 APP_MENU_CALLBACK (modest_ui_actions_on_details),
3632 MODEST_DIMMING_CALLBACK (modest_ui_dimming_rules_on_details));
3636 modest_msg_view_window_add_to_contacts (ModestMsgViewWindow *self)
3638 ModestMsgViewWindowPrivate *priv;
3639 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
3640 GSList *recipients = NULL;
3642 gboolean contacts_to_add = FALSE;
3644 msg = tny_msg_view_get_msg (TNY_MSG_VIEW (priv->msg_view));
3648 header = modest_msg_view_window_get_header (self);
3651 recipients = modest_tny_msg_header_get_all_recipients_list (header);
3652 g_object_unref (header);
3654 recipients = modest_tny_msg_get_all_recipients_list (msg);
3655 g_object_unref (msg);
3658 if (recipients != NULL) {
3659 GtkWidget *picker_dialog;
3660 GtkWidget *selector;
3662 gchar *selected = NULL;
3664 selector = hildon_touch_selector_new_text ();
3665 g_object_ref (selector);
3667 for (node = recipients; node != NULL; node = g_slist_next (node)) {
3668 if (!modest_address_book_has_address ((const gchar *) node->data)) {
3669 hildon_touch_selector_append_text (HILDON_TOUCH_SELECTOR (selector),
3670 (const gchar *) node->data);
3671 contacts_to_add = TRUE;
3675 if (contacts_to_add) {
3678 picker_dialog = hildon_picker_dialog_new (GTK_WINDOW (self));
3679 gtk_window_set_title (GTK_WINDOW (picker_dialog), _("mcen_me_viewer_addtocontacts"));
3681 hildon_picker_dialog_set_selector (HILDON_PICKER_DIALOG (picker_dialog),
3682 HILDON_TOUCH_SELECTOR (selector));
3684 picker_result = gtk_dialog_run (GTK_DIALOG (picker_dialog));
3686 if (picker_result == GTK_RESPONSE_OK) {
3687 selected = hildon_touch_selector_get_current_text (HILDON_TOUCH_SELECTOR (selector));
3689 gtk_widget_destroy (picker_dialog);
3692 modest_address_book_add_address (selected, (GtkWindow *) self);
3697 g_object_unref (selector);
3702 if (recipients) {g_slist_foreach (recipients, (GFunc) g_free, NULL); g_slist_free (recipients);}
3706 _modest_msg_view_window_map_event (GtkWidget *widget,
3710 ModestMsgViewWindow *self = (ModestMsgViewWindow *) userdata;
3712 update_progress_hint (self);
3718 modest_msg_view_window_fetch_images (ModestMsgViewWindow *self)
3720 ModestMsgViewWindowPrivate *priv;
3721 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
3723 modest_msg_view_request_fetch_images (MODEST_MSG_VIEW (priv->msg_view));
3727 modest_msg_view_window_has_blocked_external_images (ModestMsgViewWindow *self)
3729 ModestMsgViewWindowPrivate *priv;
3730 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
3732 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self), FALSE);
3734 return modest_msg_view_has_blocked_external_images (MODEST_MSG_VIEW (priv->msg_view));
3738 modest_msg_view_window_reload (ModestMsgViewWindow *self)
3740 ModestMsgViewWindowPrivate *priv;
3741 const gchar *msg_uid;
3742 TnyHeader *header = NULL;
3743 TnyFolder *folder = NULL;
3745 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self));
3747 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
3749 header = modest_msg_view_window_get_header (MODEST_MSG_VIEW_WINDOW (self));
3753 folder = tny_header_get_folder (header);
3754 g_object_unref (header);
3759 msg_uid = modest_msg_view_window_get_message_uid (self);
3761 GtkTreeRowReference *row_reference;
3763 if (priv->row_reference && gtk_tree_row_reference_valid (priv->row_reference)) {
3764 row_reference = priv->row_reference;
3766 row_reference = NULL;
3768 if (!message_reader (self, priv, NULL, msg_uid, folder, row_reference))
3769 g_warning ("Shouldn't happen, trying to reload a message failed");
3772 g_object_unref (folder);
3776 update_branding (ModestMsgViewWindow *self)
3778 const gchar *account;
3779 const gchar *mailbox;
3780 ModestAccountMgr *mgr;
3781 ModestProtocol *protocol = NULL;
3782 gchar *service_name = NULL;
3783 const GdkPixbuf *service_icon = NULL;
3784 ModestMsgViewWindowPrivate *priv;
3786 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
3788 account = modest_window_get_active_account (MODEST_WINDOW (self));
3789 mailbox = modest_window_get_active_mailbox (MODEST_WINDOW (self));
3791 mgr = modest_runtime_get_account_mgr ();
3793 if (modest_account_mgr_account_is_multimailbox (mgr, account, &protocol)) {
3794 if (MODEST_IS_ACCOUNT_PROTOCOL (protocol)) {
3795 service_name = modest_account_protocol_get_service_name (MODEST_ACCOUNT_PROTOCOL (protocol),
3797 service_icon = modest_account_protocol_get_service_icon (MODEST_ACCOUNT_PROTOCOL (protocol),
3798 account, mailbox, MODEST_ICON_SIZE_SMALL);
3802 modest_msg_view_set_branding (MODEST_MSG_VIEW (priv->msg_view), service_name, service_icon);
3803 g_free (service_name);