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);
234 static void sync_flags (ModestMsgViewWindow *self);
236 /* list my signals */
243 static const GtkActionEntry msg_view_toolbar_action_entries [] = {
246 { "ToolbarMessageReply", MODEST_STOCK_REPLY, N_("mcen_me_inbox_reply"), "<CTRL>R", NULL, G_CALLBACK (modest_ui_actions_on_reply) },
247 { "ToolbarMessageReplyAll", MODEST_STOCK_REPLY_ALL, N_("mcen_me_inbox_replytoall"), NULL, NULL, G_CALLBACK (modest_ui_actions_on_reply_all) },
248 { "ToolbarMessageForward", MODEST_STOCK_FORWARD, N_("mcen_me_inbox_forward"), NULL, NULL, G_CALLBACK (modest_ui_actions_on_forward) },
249 { "ToolbarDeleteMessage", MODEST_STOCK_DELETE, N_("qgn_toolb_gene_deletebutton"), NULL, NULL, G_CALLBACK (modest_ui_actions_on_delete_message_or_folder) },
250 { "ToolbarMessageBack", MODEST_TOOLBAR_ICON_PREV, N_("qgn_toolb_gene_back"), NULL, NULL, G_CALLBACK (modest_ui_actions_on_prev) },
251 { "ToolbarMessageNext", MODEST_TOOLBAR_ICON_NEXT, N_("qgn_toolb_gene_forward"), NULL, NULL, G_CALLBACK (modest_ui_actions_on_next) },
252 { "ToolbarDownloadExternalImages", MODEST_TOOLBAR_ICON_DOWNLOAD_IMAGES, N_("mail_bd_external_images"), NULL, NULL, G_CALLBACK (modest_ui_actions_on_fetch_images) },
255 static const GtkToggleActionEntry msg_view_toggle_action_entries [] = {
256 { "FindInMessage", MODEST_TOOLBAR_ICON_FIND, N_("qgn_toolb_gene_find"), "<CTRL>F", NULL, G_CALLBACK (modest_msg_view_window_toggle_find_toolbar), FALSE },
259 #define MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE((o), \
260 MODEST_TYPE_MSG_VIEW_WINDOW, \
261 ModestMsgViewWindowPrivate))
263 static GtkWindowClass *parent_class = NULL;
265 /* uncomment the following if you have defined any signals */
266 static guint signals[LAST_SIGNAL] = {0};
269 modest_msg_view_window_get_type (void)
271 static GType my_type = 0;
273 static const GTypeInfo my_info = {
274 sizeof(ModestMsgViewWindowClass),
275 NULL, /* base init */
276 NULL, /* base finalize */
277 (GClassInitFunc) modest_msg_view_window_class_init,
278 NULL, /* class finalize */
279 NULL, /* class data */
280 sizeof(ModestMsgViewWindow),
282 (GInstanceInitFunc) modest_msg_view_window_init,
285 #ifdef MODEST_TOOLKIT_GTK
286 my_type = g_type_register_static (MODEST_TYPE_WINDOW,
287 "ModestMsgViewWindow",
290 my_type = g_type_register_static (MODEST_TYPE_HILDON2_WINDOW,
291 "ModestMsgViewWindow",
295 static const GInterfaceInfo modest_header_view_observer_info =
297 (GInterfaceInitFunc) modest_header_view_observer_init,
298 NULL, /* interface_finalize */
299 NULL /* interface_data */
302 g_type_add_interface_static (my_type,
303 MODEST_TYPE_HEADER_VIEW_OBSERVER,
304 &modest_header_view_observer_info);
310 save_state (ModestWindow *self)
312 modest_widget_memory_save (modest_runtime_get_conf (),
314 MODEST_CONF_MSG_VIEW_WINDOW_KEY);
318 modest_msg_view_window_scroll_child (ModestMsgViewWindow *self,
319 GtkScrollType scroll_type,
323 ModestMsgViewWindowPrivate *priv;
326 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(self);
328 switch (scroll_type) {
329 case GTK_SCROLL_STEP_UP:
332 case GTK_SCROLL_STEP_DOWN:
335 case GTK_SCROLL_PAGE_UP:
338 case GTK_SCROLL_PAGE_DOWN:
341 case GTK_SCROLL_START:
352 modest_maemo_utils_scroll_pannable((HildonPannableArea *) priv->main_scroll, 0, step);
354 return (gboolean) step;
358 add_scroll_binding (GtkBindingSet *binding_set,
360 GtkScrollType scroll)
362 guint keypad_keyval = keyval - GDK_Left + GDK_KP_Left;
364 gtk_binding_entry_add_signal (binding_set, keyval, 0,
366 GTK_TYPE_SCROLL_TYPE, scroll,
367 G_TYPE_BOOLEAN, FALSE);
368 gtk_binding_entry_add_signal (binding_set, keypad_keyval, 0,
370 GTK_TYPE_SCROLL_TYPE, scroll,
371 G_TYPE_BOOLEAN, FALSE);
375 modest_msg_view_window_class_init (ModestMsgViewWindowClass *klass)
377 GObjectClass *gobject_class;
378 HildonWindowClass *hildon_window_class;
379 ModestWindowClass *modest_window_class;
380 GtkBindingSet *binding_set;
382 gobject_class = (GObjectClass*) klass;
383 hildon_window_class = (HildonWindowClass *) klass;
384 modest_window_class = (ModestWindowClass *) klass;
386 parent_class = g_type_class_peek_parent (klass);
387 gobject_class->finalize = modest_msg_view_window_finalize;
389 modest_window_class->set_zoom_func = modest_msg_view_window_set_zoom;
390 modest_window_class->get_zoom_func = modest_msg_view_window_get_zoom;
391 modest_window_class->zoom_plus_func = modest_msg_view_window_zoom_plus;
392 modest_window_class->zoom_minus_func = modest_msg_view_window_zoom_minus;
393 modest_window_class->show_toolbar_func = modest_msg_view_window_show_toolbar;
394 modest_window_class->disconnect_signals_func = modest_msg_view_window_disconnect_signals;
396 modest_window_class->save_state_func = save_state;
398 klass->scroll_child = modest_msg_view_window_scroll_child;
400 signals[MSG_CHANGED_SIGNAL] =
401 g_signal_new ("msg-changed",
402 G_TYPE_FROM_CLASS (gobject_class),
404 G_STRUCT_OFFSET (ModestMsgViewWindowClass, msg_changed),
406 modest_marshal_VOID__POINTER_POINTER,
407 G_TYPE_NONE, 2, G_TYPE_POINTER, G_TYPE_POINTER);
409 signals[SCROLL_CHILD_SIGNAL] =
410 g_signal_new ("scroll-child",
411 G_TYPE_FROM_CLASS (gobject_class),
412 G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
413 G_STRUCT_OFFSET (ModestMsgViewWindowClass, scroll_child),
415 modest_marshal_BOOLEAN__ENUM_BOOLEAN,
416 G_TYPE_BOOLEAN, 2, GTK_TYPE_SCROLL_TYPE, G_TYPE_BOOLEAN);
418 binding_set = gtk_binding_set_by_class (klass);
419 add_scroll_binding (binding_set, GDK_Up, GTK_SCROLL_STEP_UP);
420 add_scroll_binding (binding_set, GDK_Down, GTK_SCROLL_STEP_DOWN);
421 add_scroll_binding (binding_set, GDK_Page_Up, GTK_SCROLL_PAGE_UP);
422 add_scroll_binding (binding_set, GDK_Page_Down, GTK_SCROLL_PAGE_DOWN);
423 add_scroll_binding (binding_set, GDK_Home, GTK_SCROLL_START);
424 add_scroll_binding (binding_set, GDK_End, GTK_SCROLL_END);
426 g_type_class_add_private (gobject_class, sizeof(ModestMsgViewWindowPrivate));
430 static void modest_header_view_observer_init(
431 ModestHeaderViewObserverIface *iface_class)
433 iface_class->update_func = modest_msg_view_window_update_model_replaced;
437 modest_msg_view_window_init (ModestMsgViewWindow *obj)
439 ModestMsgViewWindowPrivate *priv;
440 ModestWindowPrivate *parent_priv = NULL;
441 GtkActionGroup *action_group = NULL;
442 GError *error = NULL;
444 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(obj);
445 parent_priv = MODEST_WINDOW_GET_PRIVATE(obj);
446 parent_priv->ui_manager = gtk_ui_manager_new();
448 action_group = gtk_action_group_new ("ModestMsgViewWindowActions");
449 gtk_action_group_set_translation_domain (action_group, GETTEXT_PACKAGE);
451 /* Add common actions */
452 gtk_action_group_add_actions (action_group,
453 msg_view_toolbar_action_entries,
454 G_N_ELEMENTS (msg_view_toolbar_action_entries),
456 gtk_action_group_add_toggle_actions (action_group,
457 msg_view_toggle_action_entries,
458 G_N_ELEMENTS (msg_view_toggle_action_entries),
461 gtk_ui_manager_insert_action_group (parent_priv->ui_manager, action_group, 0);
462 g_object_unref (action_group);
464 /* Load the UI definition */
465 gtk_ui_manager_add_ui_from_file (parent_priv->ui_manager, MODEST_UIDIR "modest-msg-view-window-ui.xml",
468 g_printerr ("modest: could not merge modest-msg-view-window-ui.xml: %s\n", error->message);
469 g_error_free (error);
474 /* Add accelerators */
475 gtk_window_add_accel_group (GTK_WINDOW (obj),
476 gtk_ui_manager_get_accel_group (parent_priv->ui_manager));
478 priv->is_search_result = FALSE;
479 priv->is_outbox = FALSE;
481 priv->msg_view = NULL;
482 priv->header_model = NULL;
483 priv->header_folder_id = NULL;
484 priv->clipboard_change_handler = 0;
485 priv->queue_change_handler = 0;
486 priv->account_removed_handler = 0;
487 priv->row_changed_handler = 0;
488 priv->row_deleted_handler = 0;
489 priv->row_inserted_handler = 0;
490 priv->rows_reordered_handler = 0;
491 priv->progress_hint = FALSE;
492 priv->fetching_images = 0;
494 priv->optimized_view = FALSE;
495 priv->purge_timeout = 0;
496 priv->remove_attachment_banner = NULL;
497 priv->msg_uid = NULL;
498 priv->other_body = NULL;
500 priv->sighandlers = NULL;
503 init_window (MODEST_MSG_VIEW_WINDOW(obj));
508 update_progress_hint (ModestMsgViewWindow *self)
510 ModestMsgViewWindowPrivate *priv;
511 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(self);
513 if (GTK_WIDGET_VISIBLE (self)) {
514 modest_window_show_progress (MODEST_WINDOW (self),
515 (priv->progress_hint || (priv->fetching_images > 0))?1:0);
520 set_progress_hint (ModestMsgViewWindow *self,
523 ModestWindowPrivate *parent_priv;
524 ModestMsgViewWindowPrivate *priv;
526 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self));
528 parent_priv = MODEST_WINDOW_GET_PRIVATE(self);
529 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(self);
531 /* Sets current progress hint */
532 priv->progress_hint = enabled;
534 update_progress_hint (self);
540 init_window (ModestMsgViewWindow *obj)
542 GtkWidget *main_vbox;
543 ModestMsgViewWindowPrivate *priv;
545 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(obj);
547 priv->msg_view = GTK_WIDGET (tny_platform_factory_new_msg_view (modest_tny_platform_factory_get_instance ()));
548 modest_msg_view_set_shadow_type (MODEST_MSG_VIEW (priv->msg_view), GTK_SHADOW_NONE);
549 main_vbox = gtk_vbox_new (FALSE, 6);
550 /****** HILDON2:START
551 * create panning widget, and set parameters
553 priv->main_scroll = hildon_pannable_area_new ();
554 g_object_set (G_OBJECT (priv->main_scroll),
555 "mov-mode", HILDON_MOVEMENT_MODE_BOTH,
558 gtk_container_add (GTK_CONTAINER (priv->main_scroll), priv->msg_view);
559 gtk_box_pack_start (GTK_BOX(main_vbox), priv->main_scroll, TRUE, TRUE, 0);
560 /****** HILDON2:END */
561 gtk_container_add (GTK_CONTAINER(obj), main_vbox);
563 /* NULL-ize fields if the window is destroyed */
564 g_signal_connect (priv->msg_view, "destroy", G_CALLBACK (gtk_widget_destroyed), &(priv->msg_view));
566 gtk_widget_show_all (GTK_WIDGET(main_vbox));
570 modest_msg_view_window_disconnect_signals (ModestWindow *self)
572 ModestMsgViewWindowPrivate *priv;
573 GtkWidget *header_view = NULL;
574 GtkWindow *parent_window = NULL;
576 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
578 if (gtk_clipboard_get (GDK_SELECTION_PRIMARY) &&
579 g_signal_handler_is_connected (gtk_clipboard_get (GDK_SELECTION_PRIMARY),
580 priv->clipboard_change_handler))
581 g_signal_handler_disconnect (gtk_clipboard_get (GDK_SELECTION_PRIMARY),
582 priv->clipboard_change_handler);
584 if (g_signal_handler_is_connected (G_OBJECT (modest_runtime_get_mail_operation_queue ()),
585 priv->queue_change_handler))
586 g_signal_handler_disconnect (G_OBJECT (modest_runtime_get_mail_operation_queue ()),
587 priv->queue_change_handler);
589 if (g_signal_handler_is_connected (G_OBJECT (modest_runtime_get_account_store ()),
590 priv->account_removed_handler))
591 g_signal_handler_disconnect (G_OBJECT (modest_runtime_get_account_store ()),
592 priv->account_removed_handler);
594 if (priv->header_model) {
595 if (g_signal_handler_is_connected(G_OBJECT (priv->header_model),
596 priv->row_changed_handler))
597 g_signal_handler_disconnect(G_OBJECT (priv->header_model),
598 priv->row_changed_handler);
600 if (g_signal_handler_is_connected(G_OBJECT (priv->header_model),
601 priv->row_deleted_handler))
602 g_signal_handler_disconnect(G_OBJECT (priv->header_model),
603 priv->row_deleted_handler);
605 if (g_signal_handler_is_connected(G_OBJECT (priv->header_model),
606 priv->row_inserted_handler))
607 g_signal_handler_disconnect(G_OBJECT (priv->header_model),
608 priv->row_inserted_handler);
610 if (g_signal_handler_is_connected(G_OBJECT (priv->header_model),
611 priv->rows_reordered_handler))
612 g_signal_handler_disconnect(G_OBJECT (priv->header_model),
613 priv->rows_reordered_handler);
616 modest_signal_mgr_disconnect_all_and_destroy (priv->sighandlers);
617 priv->sighandlers = NULL;
619 parent_window = gtk_window_get_transient_for (GTK_WINDOW (self));
620 if (parent_window && MODEST_IS_HEADER_WINDOW (parent_window)) {
621 header_view = GTK_WIDGET (modest_header_window_get_header_view (MODEST_HEADER_WINDOW (parent_window)));
623 modest_header_view_remove_observer(MODEST_HEADER_VIEW (header_view),
624 MODEST_HEADER_VIEW_OBSERVER(self));
630 modest_msg_view_window_finalize (GObject *obj)
632 ModestMsgViewWindowPrivate *priv;
634 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (obj);
636 /* Sanity check: shouldn't be needed, the window mgr should
637 call this function before */
638 modest_msg_view_window_disconnect_signals (MODEST_WINDOW (obj));
640 if (priv->other_body != NULL) {
641 g_object_unref (priv->other_body);
642 priv->other_body = NULL;
645 if (priv->header_model != NULL) {
646 g_object_unref (priv->header_model);
647 priv->header_model = NULL;
650 if (priv->remove_attachment_banner) {
651 gtk_widget_destroy (priv->remove_attachment_banner);
652 g_object_unref (priv->remove_attachment_banner);
653 priv->remove_attachment_banner = NULL;
656 if (priv->purge_timeout > 0) {
657 g_source_remove (priv->purge_timeout);
658 priv->purge_timeout = 0;
661 if (priv->row_reference) {
662 gtk_tree_row_reference_free (priv->row_reference);
663 priv->row_reference = NULL;
666 if (priv->next_row_reference) {
667 gtk_tree_row_reference_free (priv->next_row_reference);
668 priv->next_row_reference = NULL;
672 g_free (priv->msg_uid);
673 priv->msg_uid = NULL;
676 G_OBJECT_CLASS(parent_class)->finalize (obj);
680 select_next_valid_row (GtkTreeModel *model,
681 GtkTreeRowReference **row_reference,
685 GtkTreeIter tmp_iter;
687 GtkTreePath *next = NULL;
688 gboolean retval = FALSE, finished;
690 g_return_val_if_fail (gtk_tree_row_reference_valid (*row_reference), FALSE);
692 path = gtk_tree_row_reference_get_path (*row_reference);
693 gtk_tree_model_get_iter (model, &tmp_iter, path);
694 gtk_tree_row_reference_free (*row_reference);
695 *row_reference = NULL;
699 TnyHeader *header = NULL;
701 if (gtk_tree_model_iter_next (model, &tmp_iter)) {
702 gtk_tree_model_get (model, &tmp_iter,
703 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
707 if (msg_is_visible (header, is_outbox)) {
708 next = gtk_tree_model_get_path (model, &tmp_iter);
709 *row_reference = gtk_tree_row_reference_new (model, next);
710 gtk_tree_path_free (next);
714 g_object_unref (header);
717 } else if (cycle && gtk_tree_model_get_iter_first (model, &tmp_iter)) {
718 next = gtk_tree_model_get_path (model, &tmp_iter);
720 /* Ensure that we are not selecting the same */
721 if (gtk_tree_path_compare (path, next) != 0) {
722 gtk_tree_model_get (model, &tmp_iter,
723 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
726 if (msg_is_visible (header, is_outbox)) {
727 *row_reference = gtk_tree_row_reference_new (model, next);
731 g_object_unref (header);
735 /* If we ended up in the same message
736 then there is no valid next
740 gtk_tree_path_free (next);
742 /* If there are no more messages and we don't
743 want to start again in the first one then
744 there is no valid next message */
750 gtk_tree_path_free (path);
755 /* TODO: This should be in _init(), with the parameters as properties. */
757 modest_msg_view_window_construct (ModestMsgViewWindow *self,
758 const gchar *modest_account_name,
759 const gchar *mailbox,
760 const gchar *msg_uid)
763 ModestMsgViewWindowPrivate *priv = NULL;
764 ModestWindowPrivate *parent_priv = NULL;
765 ModestDimmingRulesGroup *toolbar_rules_group = NULL;
766 ModestDimmingRulesGroup *clipboard_rules_group = NULL;
768 obj = G_OBJECT (self);
769 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(obj);
770 parent_priv = MODEST_WINDOW_GET_PRIVATE(obj);
772 priv->msg_uid = g_strdup (msg_uid);
775 parent_priv->menubar = NULL;
777 toolbar_rules_group = modest_dimming_rules_group_new (MODEST_DIMMING_RULES_TOOLBAR, TRUE);
778 clipboard_rules_group = modest_dimming_rules_group_new (MODEST_DIMMING_RULES_CLIPBOARD, FALSE);
781 /* Add common dimming rules */
782 modest_dimming_rules_group_add_rules (toolbar_rules_group,
783 modest_msg_view_toolbar_dimming_entries,
784 G_N_ELEMENTS (modest_msg_view_toolbar_dimming_entries),
785 MODEST_WINDOW (self));
786 modest_dimming_rules_group_add_rules (clipboard_rules_group,
787 modest_msg_view_clipboard_dimming_entries,
788 G_N_ELEMENTS (modest_msg_view_clipboard_dimming_entries),
789 MODEST_WINDOW (self));
791 /* Insert dimming rules group for this window */
792 modest_ui_dimming_manager_insert_rules_group (parent_priv->ui_dimming_manager, toolbar_rules_group);
793 modest_ui_dimming_manager_insert_rules_group (parent_priv->ui_dimming_manager, clipboard_rules_group);
794 g_object_unref (toolbar_rules_group);
795 g_object_unref (clipboard_rules_group);
797 /* g_signal_connect (G_OBJECT(obj), "delete-event", G_CALLBACK(on_delete_event), obj); */
799 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);
800 g_signal_connect (G_OBJECT(priv->msg_view), "activate_link",
801 G_CALLBACK (modest_ui_actions_on_msg_link_clicked), obj);
802 g_signal_connect (G_OBJECT(priv->msg_view), "link_hover",
803 G_CALLBACK (modest_ui_actions_on_msg_link_hover), obj);
804 g_signal_connect (G_OBJECT(priv->msg_view), "attachment_clicked",
805 G_CALLBACK (modest_ui_actions_on_msg_attachment_clicked), obj);
806 g_signal_connect (G_OBJECT(priv->msg_view), "recpt_activated",
807 G_CALLBACK (modest_ui_actions_on_msg_recpt_activated), obj);
808 g_signal_connect (G_OBJECT(priv->msg_view), "show_details",
809 G_CALLBACK (modest_ui_actions_on_details), obj);
810 g_signal_connect (G_OBJECT(priv->msg_view), "link_contextual",
811 G_CALLBACK (modest_ui_actions_on_msg_link_contextual), obj);
812 g_signal_connect (G_OBJECT(priv->msg_view), "limit_error",
813 G_CALLBACK (modest_ui_actions_on_limit_error), obj);
814 g_signal_connect (G_OBJECT (priv->msg_view), "fetch_image",
815 G_CALLBACK (on_fetch_image), obj);
817 g_signal_connect (G_OBJECT (obj), "key-release-event",
818 G_CALLBACK (modest_msg_view_window_key_event),
821 g_signal_connect (G_OBJECT (obj), "key-press-event",
822 G_CALLBACK (modest_msg_view_window_key_event),
825 g_signal_connect (G_OBJECT (obj), "move-focus",
826 G_CALLBACK (on_move_focus), obj);
828 g_signal_connect (G_OBJECT (obj), "map-event",
829 G_CALLBACK (_modest_msg_view_window_map_event),
832 /* Mail Operation Queue */
833 priv->queue_change_handler = g_signal_connect (G_OBJECT (modest_runtime_get_mail_operation_queue ()),
835 G_CALLBACK (on_queue_changed),
838 /* Account manager */
839 priv->account_removed_handler = g_signal_connect (G_OBJECT (modest_runtime_get_account_store ()),
841 G_CALLBACK(on_account_removed),
844 modest_window_set_active_account (MODEST_WINDOW(obj), modest_account_name);
845 modest_window_set_active_mailbox (MODEST_WINDOW(obj), mailbox);
847 /* First add out toolbar ... */
848 modest_msg_view_window_show_toolbar (MODEST_WINDOW (obj), TRUE);
850 /****** HILDON2:START
853 /* ... and later the find toolbar. This way find toolbar will
854 be shown over the other */
855 priv->find_toolbar = hildon_find_toolbar_new (NULL);
856 hildon_window_add_toolbar (HILDON_WINDOW (obj), GTK_TOOLBAR (priv->find_toolbar));
857 gtk_widget_set_no_show_all (priv->find_toolbar, TRUE);
858 g_signal_connect (G_OBJECT (priv->find_toolbar), "close",
859 G_CALLBACK (modest_msg_view_window_find_toolbar_close), obj);
860 g_signal_connect (G_OBJECT (priv->find_toolbar), "search",
861 G_CALLBACK (modest_msg_view_window_find_toolbar_search), obj);
862 priv->last_search = NULL;
863 /****** HILDON2:END */
865 /* Init the clipboard actions dim status */
866 modest_msg_view_grab_focus(MODEST_MSG_VIEW (priv->msg_view));
868 update_window_title (MODEST_MSG_VIEW_WINDOW (obj));
873 /* FIXME: parameter checks */
875 modest_msg_view_window_new_with_header_model (TnyMsg *msg,
876 const gchar *modest_account_name,
877 const gchar *mailbox,
878 const gchar *msg_uid,
880 GtkTreeRowReference *row_reference)
882 ModestMsgViewWindow *window = NULL;
883 ModestMsgViewWindowPrivate *priv = NULL;
884 TnyFolder *header_folder = NULL;
885 ModestHeaderView *header_view = NULL;
886 ModestWindowMgr *mgr = NULL;
889 modest_tny_mime_part_to_string (TNY_MIME_PART (msg), 0);
892 mgr = modest_runtime_get_window_mgr ();
893 window = MODEST_MSG_VIEW_WINDOW (modest_window_mgr_get_msg_view_window (mgr));
894 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), NULL);
896 modest_msg_view_window_construct (window, modest_account_name, mailbox, msg_uid);
898 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
900 /* Remember the message list's TreeModel so we can detect changes
901 * and change the list selection when necessary: */
902 header_folder = modest_header_view_get_folder (header_view);
904 priv->is_outbox = (modest_tny_folder_guess_folder_type (header_folder) ==
905 TNY_FOLDER_TYPE_OUTBOX);
906 priv->header_folder_id = tny_folder_get_id (header_folder);
907 g_object_unref(header_folder);
910 /* Setup row references and connect signals */
911 priv->header_model = g_object_ref (model);
913 if (row_reference && gtk_tree_row_reference_valid (row_reference)) {
914 priv->row_reference = gtk_tree_row_reference_copy (row_reference);
915 priv->next_row_reference = gtk_tree_row_reference_copy (row_reference);
916 select_next_valid_row (model, &(priv->next_row_reference), TRUE, priv->is_outbox);
918 priv->row_reference = NULL;
919 priv->next_row_reference = NULL;
922 /* Connect signals */
923 priv->row_changed_handler =
924 g_signal_connect (GTK_TREE_MODEL(model), "row-changed",
925 G_CALLBACK(modest_msg_view_window_on_row_changed),
927 priv->row_deleted_handler =
928 g_signal_connect (GTK_TREE_MODEL(model), "row-deleted",
929 G_CALLBACK(modest_msg_view_window_on_row_deleted),
931 priv->row_inserted_handler =
932 g_signal_connect (GTK_TREE_MODEL(model), "row-inserted",
933 G_CALLBACK(modest_msg_view_window_on_row_inserted),
935 priv->rows_reordered_handler =
936 g_signal_connect(GTK_TREE_MODEL(model), "rows-reordered",
937 G_CALLBACK(modest_msg_view_window_on_row_reordered),
940 if (header_view != NULL){
941 modest_header_view_add_observer(header_view,
942 MODEST_HEADER_VIEW_OBSERVER(window));
945 tny_msg_view_set_msg (TNY_MSG_VIEW (priv->msg_view), msg);
946 update_window_title (MODEST_MSG_VIEW_WINDOW (window));
947 update_branding (MODEST_MSG_VIEW_WINDOW (window));
949 /* gtk_widget_show_all (GTK_WIDGET (window)); */
950 modest_msg_view_window_update_priority (window);
951 /* Check dimming rules */
952 modest_ui_actions_check_toolbar_dimming_rules (MODEST_WINDOW (window));
953 modest_ui_actions_check_menu_dimming_rules (MODEST_WINDOW (window));
954 modest_window_check_dimming_rules_group (MODEST_WINDOW (window), MODEST_DIMMING_RULES_CLIPBOARD);
956 return MODEST_WINDOW(window);
960 modest_msg_view_window_new_from_uid (const gchar *modest_account_name,
961 const gchar *mailbox,
962 const gchar *msg_uid)
964 ModestMsgViewWindow *window = NULL;
965 ModestMsgViewWindowPrivate *priv = NULL;
966 ModestWindowMgr *mgr = NULL;
968 TnyAccount *account = NULL;
970 mgr = modest_runtime_get_window_mgr ();
971 window = MODEST_MSG_VIEW_WINDOW (modest_window_mgr_get_msg_view_window (mgr));
972 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), NULL);
974 modest_msg_view_window_construct (window, modest_account_name, mailbox, msg_uid);
976 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
978 is_merge = g_str_has_prefix (msg_uid, "merge:");
980 /* Get the account */
982 account = tny_account_store_find_account (TNY_ACCOUNT_STORE (modest_runtime_get_account_store ()),
985 if (is_merge || account) {
986 TnyFolder *folder = NULL;
988 /* Try to get the message, if it's already downloaded
989 we don't need to connect */
991 folder = modest_tny_folder_store_find_folder_from_uri (TNY_FOLDER_STORE (account), msg_uid);
993 ModestTnyAccountStore *account_store;
994 ModestTnyLocalFoldersAccount *local_folders_account;
996 account_store = modest_runtime_get_account_store ();
997 local_folders_account = MODEST_TNY_LOCAL_FOLDERS_ACCOUNT (
998 modest_tny_account_store_get_local_folders_account (account_store));
999 folder = modest_tny_local_folders_account_get_merged_outbox (local_folders_account);
1000 g_object_unref (local_folders_account);
1004 gboolean device_online;
1006 device = modest_runtime_get_device();
1007 device_online = tny_device_is_online (device);
1008 if (device_online) {
1009 message_reader (window, priv, NULL, msg_uid, folder, NULL);
1011 TnyMsg *msg = tny_folder_find_msg (folder, msg_uid, NULL);
1013 tny_msg_view_set_msg (TNY_MSG_VIEW (priv->msg_view), msg);
1014 update_window_title (MODEST_MSG_VIEW_WINDOW (window));
1015 update_branding (MODEST_MSG_VIEW_WINDOW (window));
1016 g_object_unref (msg);
1017 /* Sync flags to server */
1018 sync_flags (MODEST_MSG_VIEW_WINDOW (window));
1020 message_reader (window, priv, NULL, msg_uid, folder, NULL);
1023 g_object_unref (folder);
1028 /* Check dimming rules */
1029 modest_ui_actions_check_toolbar_dimming_rules (MODEST_WINDOW (window));
1030 modest_ui_actions_check_menu_dimming_rules (MODEST_WINDOW (window));
1031 modest_window_check_dimming_rules_group (MODEST_WINDOW (window), MODEST_DIMMING_RULES_CLIPBOARD);
1033 return MODEST_WINDOW(window);
1037 modest_msg_view_window_new_from_header_view (ModestHeaderView *header_view,
1038 const gchar *modest_account_name,
1039 const gchar *mailbox,
1040 const gchar *msg_uid,
1041 GtkTreeRowReference *row_reference)
1043 ModestMsgViewWindow *window = NULL;
1044 ModestMsgViewWindowPrivate *priv = NULL;
1045 TnyFolder *header_folder = NULL;
1046 ModestWindowMgr *mgr = NULL;
1050 mgr = modest_runtime_get_window_mgr ();
1051 window = MODEST_MSG_VIEW_WINDOW (modest_window_mgr_get_msg_view_window (mgr));
1052 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), NULL);
1054 modest_msg_view_window_construct (window, modest_account_name, mailbox, msg_uid);
1056 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1058 /* Remember the message list's TreeModel so we can detect changes
1059 * and change the list selection when necessary: */
1061 if (header_view != NULL){
1062 header_folder = modest_header_view_get_folder(header_view);
1063 /* This could happen if the header folder was
1064 unseleted before opening this msg window (for
1065 example if the user selects an account in the
1066 folder view of the main window */
1067 if (header_folder) {
1068 priv->is_outbox = (modest_tny_folder_guess_folder_type (header_folder) ==
1069 TNY_FOLDER_TYPE_OUTBOX);
1070 priv->header_folder_id = tny_folder_get_id(header_folder);
1071 g_object_unref(header_folder);
1075 /* Setup row references and connect signals */
1076 priv->header_model = gtk_tree_view_get_model (GTK_TREE_VIEW (header_view));
1077 g_object_ref (priv->header_model);
1079 if (row_reference && gtk_tree_row_reference_valid (row_reference)) {
1080 priv->row_reference = gtk_tree_row_reference_copy (row_reference);
1081 priv->next_row_reference = gtk_tree_row_reference_copy (row_reference);
1082 select_next_valid_row (priv->header_model, &(priv->next_row_reference), TRUE, priv->is_outbox);
1084 priv->row_reference = NULL;
1085 priv->next_row_reference = NULL;
1088 /* Connect signals */
1089 priv->row_changed_handler =
1090 g_signal_connect (GTK_TREE_MODEL(priv->header_model), "row-changed",
1091 G_CALLBACK(modest_msg_view_window_on_row_changed),
1093 priv->row_deleted_handler =
1094 g_signal_connect (GTK_TREE_MODEL(priv->header_model), "row-deleted",
1095 G_CALLBACK(modest_msg_view_window_on_row_deleted),
1097 priv->row_inserted_handler =
1098 g_signal_connect (GTK_TREE_MODEL(priv->header_model), "row-inserted",
1099 G_CALLBACK(modest_msg_view_window_on_row_inserted),
1101 priv->rows_reordered_handler =
1102 g_signal_connect(GTK_TREE_MODEL(priv->header_model), "rows-reordered",
1103 G_CALLBACK(modest_msg_view_window_on_row_reordered),
1106 if (header_view != NULL){
1107 modest_header_view_add_observer(header_view,
1108 MODEST_HEADER_VIEW_OBSERVER(window));
1111 tny_msg_view_set_msg (TNY_MSG_VIEW (priv->msg_view), NULL);
1112 update_branding (MODEST_MSG_VIEW_WINDOW (window));
1114 if (priv->row_reference) {
1115 path = gtk_tree_row_reference_get_path (priv->row_reference);
1116 if (gtk_tree_model_get_iter (priv->header_model, &iter, path)) {
1118 gtk_tree_model_get (priv->header_model, &iter,
1119 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
1121 message_reader (window, priv, header, NULL, NULL, priv->row_reference);
1122 g_object_unref (header);
1124 gtk_tree_path_free (path);
1126 /* Check dimming rules */
1127 modest_ui_actions_check_toolbar_dimming_rules (MODEST_WINDOW (window));
1128 modest_ui_actions_check_menu_dimming_rules (MODEST_WINDOW (window));
1129 modest_window_check_dimming_rules_group (MODEST_WINDOW (window), MODEST_DIMMING_RULES_CLIPBOARD);
1131 return MODEST_WINDOW(window);
1135 modest_msg_view_window_new_for_search_result (TnyMsg *msg,
1136 const gchar *modest_account_name,
1137 const gchar *mailbox,
1138 const gchar *msg_uid)
1140 ModestMsgViewWindow *window = NULL;
1141 ModestMsgViewWindowPrivate *priv = NULL;
1142 ModestWindowMgr *mgr = NULL;
1144 mgr = modest_runtime_get_window_mgr ();
1145 window = MODEST_MSG_VIEW_WINDOW (modest_window_mgr_get_msg_view_window (mgr));
1146 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), NULL);
1147 modest_msg_view_window_construct (window, modest_account_name, mailbox, msg_uid);
1149 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1151 /* Remember that this is a search result,
1152 * so we can disable some UI appropriately: */
1153 priv->is_search_result = TRUE;
1155 tny_msg_view_set_msg (TNY_MSG_VIEW (priv->msg_view), msg);
1156 update_branding (MODEST_MSG_VIEW_WINDOW (window));
1158 update_window_title (window);
1159 /* gtk_widget_show_all (GTK_WIDGET (window));*/
1160 modest_msg_view_window_update_priority (window);
1162 /* Check dimming rules */
1163 modest_ui_actions_check_toolbar_dimming_rules (MODEST_WINDOW (window));
1164 modest_ui_actions_check_menu_dimming_rules (MODEST_WINDOW (window));
1165 modest_window_check_dimming_rules_group (MODEST_WINDOW (window), MODEST_DIMMING_RULES_CLIPBOARD);
1167 return MODEST_WINDOW(window);
1171 modest_msg_view_window_is_other_body (ModestMsgViewWindow *self)
1173 ModestMsgViewWindowPrivate *priv = NULL;
1175 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self), FALSE);
1176 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
1178 return (priv->other_body != NULL);
1182 modest_msg_view_window_new_with_other_body (TnyMsg *msg,
1183 TnyMimePart *other_body,
1184 const gchar *modest_account_name,
1185 const gchar *mailbox,
1186 const gchar *msg_uid)
1188 GObject *obj = NULL;
1189 ModestMsgViewWindowPrivate *priv;
1190 ModestWindowMgr *mgr = NULL;
1192 g_return_val_if_fail (msg, NULL);
1193 mgr = modest_runtime_get_window_mgr ();
1194 obj = G_OBJECT (modest_window_mgr_get_msg_view_window (mgr));
1195 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (obj);
1196 modest_msg_view_window_construct (MODEST_MSG_VIEW_WINDOW (obj),
1197 modest_account_name, mailbox, msg_uid);
1200 priv->other_body = g_object_ref (other_body);
1201 modest_msg_view_set_msg_with_other_body (MODEST_MSG_VIEW (priv->msg_view), msg, other_body);
1203 tny_msg_view_set_msg (TNY_MSG_VIEW (priv->msg_view), msg);
1205 update_window_title (MODEST_MSG_VIEW_WINDOW (obj));
1206 update_branding (MODEST_MSG_VIEW_WINDOW (obj));
1208 /* gtk_widget_show_all (GTK_WIDGET (obj)); */
1210 /* Check dimming rules */
1211 modest_ui_actions_check_toolbar_dimming_rules (MODEST_WINDOW (obj));
1212 modest_ui_actions_check_menu_dimming_rules (MODEST_WINDOW (obj));
1213 modest_window_check_dimming_rules_group (MODEST_WINDOW (obj), MODEST_DIMMING_RULES_CLIPBOARD);
1215 return MODEST_WINDOW(obj);
1219 modest_msg_view_window_new_for_attachment (TnyMsg *msg,
1220 const gchar *modest_account_name,
1221 const gchar *mailbox,
1222 const gchar *msg_uid)
1224 return modest_msg_view_window_new_with_other_body (msg, NULL, modest_account_name, mailbox, msg_uid);
1228 modest_msg_view_window_on_row_changed (GtkTreeModel *header_model,
1231 ModestMsgViewWindow *window)
1233 check_dimming_rules_after_change (window);
1237 modest_msg_view_window_on_row_deleted(GtkTreeModel *header_model,
1239 ModestMsgViewWindow *window)
1241 check_dimming_rules_after_change (window);
1243 /* The window could have dissapeared */
1246 check_dimming_rules_after_change (ModestMsgViewWindow *window)
1248 modest_ui_actions_check_menu_dimming_rules (MODEST_WINDOW (window));
1249 modest_ui_actions_check_toolbar_dimming_rules (MODEST_WINDOW (window));
1253 /* On insertions we check if the folder still has the message we are
1254 * showing or do not. If do not, we do nothing. Which means we are still
1255 * not attached to any header folder and thus next/prev buttons are
1256 * still dimmed. Once the message that is shown by msg-view is found, the
1257 * new model of header-view will be attached and the references will be set.
1258 * On each further insertions dimming rules will be checked. However
1259 * this requires extra CPU time at least works.
1260 * (An message might be deleted from TnyFolder and thus will not be
1261 * inserted into the model again for example if it is removed by the
1262 * imap server and the header view is refreshed.)
1265 modest_msg_view_window_on_row_inserted (GtkTreeModel *model,
1266 GtkTreePath *tree_path,
1267 GtkTreeIter *tree_iter,
1268 ModestMsgViewWindow *window)
1270 ModestMsgViewWindowPrivate *priv = NULL;
1271 TnyHeader *header = NULL;
1273 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window));
1274 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1276 g_assert (model == priv->header_model);
1278 /* Check if the newly inserted message is the same we are actually
1279 * showing. IF not, we should remain detached from the header model
1280 * and thus prev and next toolbar buttons should remain dimmed. */
1281 gtk_tree_model_get (model, tree_iter,
1282 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
1285 if (TNY_IS_HEADER (header)) {
1288 uid = modest_tny_folder_get_header_unique_id (header);
1289 if (!g_str_equal(priv->msg_uid, uid)) {
1290 check_dimming_rules_after_change (window);
1292 g_object_unref (G_OBJECT(header));
1296 g_object_unref(G_OBJECT(header));
1299 if (priv->row_reference) {
1300 gtk_tree_row_reference_free (priv->row_reference);
1303 /* Setup row_reference for the actual msg. */
1304 priv->row_reference = gtk_tree_row_reference_new (priv->header_model, tree_path);
1305 if (priv->row_reference == NULL) {
1306 g_warning("%s: No reference for msg header item.", __FUNCTION__);
1310 /* Now set up next_row_reference. */
1311 if (priv->next_row_reference) {
1312 gtk_tree_row_reference_free (priv->next_row_reference);
1315 priv->next_row_reference = gtk_tree_row_reference_copy (priv->row_reference);
1316 select_next_valid_row (priv->header_model,
1317 &(priv->next_row_reference), FALSE, priv->is_outbox);
1319 /* Connect the remaining callbacks to become able to detect
1320 * changes in header-view. */
1321 priv->row_changed_handler =
1322 g_signal_connect (priv->header_model, "row-changed",
1323 G_CALLBACK (modest_msg_view_window_on_row_changed),
1325 priv->row_deleted_handler =
1326 g_signal_connect (priv->header_model, "row-deleted",
1327 G_CALLBACK (modest_msg_view_window_on_row_deleted),
1329 priv->rows_reordered_handler =
1330 g_signal_connect (priv->header_model, "rows-reordered",
1331 G_CALLBACK (modest_msg_view_window_on_row_reordered),
1334 check_dimming_rules_after_change (window);
1338 modest_msg_view_window_on_row_reordered (GtkTreeModel *header_model,
1342 ModestMsgViewWindow *window)
1344 ModestMsgViewWindowPrivate *priv = NULL;
1345 gboolean already_changed = FALSE;
1347 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(window);
1349 /* If the current row was reordered select the proper next
1350 valid row. The same if the next row reference changes */
1351 if (!priv->row_reference ||
1352 !gtk_tree_row_reference_valid (priv->row_reference))
1355 if (priv->next_row_reference &&
1356 gtk_tree_row_reference_valid (priv->next_row_reference)) {
1357 GtkTreePath *cur, *next;
1358 /* Check that the order is still the correct one */
1359 cur = gtk_tree_row_reference_get_path (priv->row_reference);
1360 next = gtk_tree_row_reference_get_path (priv->next_row_reference);
1361 gtk_tree_path_next (cur);
1362 if (gtk_tree_path_compare (cur, next) != 0) {
1363 gtk_tree_row_reference_free (priv->next_row_reference);
1364 priv->next_row_reference = gtk_tree_row_reference_copy (priv->row_reference);
1365 select_next_valid_row (header_model, &(priv->next_row_reference), FALSE, priv->is_outbox);
1366 already_changed = TRUE;
1368 gtk_tree_path_free (cur);
1369 gtk_tree_path_free (next);
1371 if (priv->next_row_reference)
1372 gtk_tree_row_reference_free (priv->next_row_reference);
1373 /* Update next row reference */
1374 priv->next_row_reference = gtk_tree_row_reference_copy (priv->row_reference);
1375 select_next_valid_row (header_model, &(priv->next_row_reference), FALSE, priv->is_outbox);
1376 already_changed = TRUE;
1379 check_dimming_rules_after_change (window);
1382 /* The modest_msg_view_window_update_model_replaced implements update
1383 * function for ModestHeaderViewObserver. Checks whether the TnyFolder
1384 * actually belongs to the header-view is the same as the TnyFolder of
1385 * the message of msg-view or not. If they are different, there is
1386 * nothing to do. If they are the same, then the model has replaced and
1387 * the reference in msg-view shall be replaced from the old model to
1388 * the new model. In this case the view will be detached from it's
1389 * header folder. From this point the next/prev buttons are dimmed.
1392 modest_msg_view_window_update_model_replaced (ModestHeaderViewObserver *observer,
1393 GtkTreeModel *model,
1394 const gchar *tny_folder_id)
1396 ModestMsgViewWindowPrivate *priv = NULL;
1397 ModestMsgViewWindow *window = NULL;
1399 g_assert(MODEST_IS_HEADER_VIEW_OBSERVER(observer));
1400 g_assert(MODEST_IS_MSG_VIEW_WINDOW(observer));
1402 window = MODEST_MSG_VIEW_WINDOW(observer);
1403 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(window);
1405 /* If there is an other folder in the header-view then we do
1406 * not care about it's model (msg list). Else if the
1407 * header-view shows the folder the msg shown by us is in, we
1408 * shall replace our model reference and make some check. */
1409 if(model == NULL || tny_folder_id == NULL ||
1410 (priv->header_folder_id && !g_str_equal(tny_folder_id, priv->header_folder_id)))
1413 /* Model is changed(replaced), so we should forget the old
1414 * one. Because there might be other references and there
1415 * might be some change on the model even if we unreferenced
1416 * it, we need to disconnect our signals here. */
1417 if (priv->header_model) {
1418 if (g_signal_handler_is_connected(G_OBJECT (priv->header_model),
1419 priv->row_changed_handler))
1420 g_signal_handler_disconnect(G_OBJECT (priv->header_model),
1421 priv->row_changed_handler);
1422 if (g_signal_handler_is_connected(G_OBJECT (priv->header_model),
1423 priv->row_deleted_handler))
1424 g_signal_handler_disconnect(G_OBJECT (priv->header_model),
1425 priv->row_deleted_handler);
1426 if (g_signal_handler_is_connected(G_OBJECT (priv->header_model),
1427 priv->row_inserted_handler))
1428 g_signal_handler_disconnect(G_OBJECT (priv->header_model),
1429 priv->row_inserted_handler);
1430 if (g_signal_handler_is_connected(G_OBJECT (priv->header_model),
1431 priv->rows_reordered_handler))
1432 g_signal_handler_disconnect(G_OBJECT (priv->header_model),
1433 priv->rows_reordered_handler);
1436 if (priv->row_reference)
1437 gtk_tree_row_reference_free (priv->row_reference);
1438 if (priv->next_row_reference)
1439 gtk_tree_row_reference_free (priv->next_row_reference);
1440 g_object_unref(priv->header_model);
1443 priv->row_changed_handler = 0;
1444 priv->row_deleted_handler = 0;
1445 priv->row_inserted_handler = 0;
1446 priv->rows_reordered_handler = 0;
1447 priv->next_row_reference = NULL;
1448 priv->row_reference = NULL;
1449 priv->header_model = NULL;
1452 priv->header_model = g_object_ref (model);
1454 /* Also we must connect to the new model for row insertions.
1455 * Only for insertions now. We will need other ones only after
1456 * the msg is show by msg-view is added to the new model. */
1457 priv->row_inserted_handler =
1458 g_signal_connect (priv->header_model, "row-inserted",
1459 G_CALLBACK(modest_msg_view_window_on_row_inserted),
1462 modest_ui_actions_check_menu_dimming_rules(MODEST_WINDOW(window));
1463 modest_ui_actions_check_toolbar_dimming_rules(MODEST_WINDOW(window));
1467 modest_msg_view_window_toolbar_on_transfer_mode (ModestMsgViewWindow *self)
1469 ModestMsgViewWindowPrivate *priv= NULL;
1471 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self), FALSE);
1472 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
1474 return priv->progress_hint;
1478 modest_msg_view_window_get_header (ModestMsgViewWindow *self)
1480 ModestMsgViewWindowPrivate *priv= NULL;
1482 TnyHeader *header = NULL;
1483 GtkTreePath *path = NULL;
1486 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self), NULL);
1487 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
1489 /* If the message was not obtained from a treemodel,
1490 * for instance if it was opened directly by the search UI:
1492 if (priv->header_model == NULL ||
1493 priv->row_reference == NULL ||
1494 !gtk_tree_row_reference_valid (priv->row_reference)) {
1495 msg = modest_msg_view_window_get_message (self);
1497 header = tny_msg_get_header (msg);
1498 g_object_unref (msg);
1503 /* Get iter of the currently selected message in the header view: */
1504 path = gtk_tree_row_reference_get_path (priv->row_reference);
1505 g_return_val_if_fail (path != NULL, NULL);
1506 gtk_tree_model_get_iter (priv->header_model,
1510 /* Get current message header */
1511 gtk_tree_model_get (priv->header_model, &iter,
1512 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
1515 gtk_tree_path_free (path);
1520 modest_msg_view_window_get_message (ModestMsgViewWindow *self)
1522 ModestMsgViewWindowPrivate *priv;
1524 g_return_val_if_fail (self, NULL);
1526 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(self);
1528 return tny_msg_view_get_msg (TNY_MSG_VIEW (priv->msg_view));
1532 modest_msg_view_window_get_message_uid (ModestMsgViewWindow *self)
1534 ModestMsgViewWindowPrivate *priv;
1536 g_return_val_if_fail (self, NULL);
1538 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
1540 return (const gchar*) priv->msg_uid;
1543 /* Used for the Ctrl+F accelerator */
1545 modest_msg_view_window_toggle_find_toolbar (GtkWidget *obj,
1548 ModestMsgViewWindow *window = MODEST_MSG_VIEW_WINDOW (data);
1549 ModestMsgViewWindowPrivate *priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1551 if (GTK_WIDGET_VISIBLE (priv->find_toolbar)) {
1552 modest_msg_view_window_find_toolbar_close (obj, data);
1554 modest_msg_view_window_show_find_toolbar (obj, data);
1558 /* Handler for menu option */
1560 modest_msg_view_window_show_find_toolbar (GtkWidget *obj,
1563 ModestMsgViewWindow *window = MODEST_MSG_VIEW_WINDOW (data);
1564 ModestMsgViewWindowPrivate *priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1566 gtk_widget_show (priv->find_toolbar);
1567 /****** HILDON2:START */
1568 hildon_find_toolbar_highlight_entry (HILDON_FIND_TOOLBAR (priv->find_toolbar), TRUE);
1569 /****** HILDON2:END */
1572 /* Handler for click on the "X" close button in find toolbar */
1574 modest_msg_view_window_find_toolbar_close (GtkWidget *widget,
1575 ModestMsgViewWindow *obj)
1577 ModestMsgViewWindowPrivate *priv;
1579 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (obj);
1582 gtk_widget_hide (priv->find_toolbar);
1583 modest_msg_view_grab_focus (MODEST_MSG_VIEW (priv->msg_view));
1587 modest_msg_view_window_find_toolbar_search (GtkWidget *widget,
1588 ModestMsgViewWindow *obj)
1590 gchar *current_search;
1591 ModestMsgViewWindowPrivate *priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (obj);
1593 if (modest_mime_part_view_is_empty (MODEST_MIME_PART_VIEW (priv->msg_view))) {
1594 modest_platform_system_banner (NULL, NULL, _("mail_ib_nothing_to_find"));
1598 g_object_get (G_OBJECT (widget), "prefix", ¤t_search, NULL);
1600 if ((current_search == NULL) || (strcmp (current_search, "") == 0)) {
1601 g_free (current_search);
1602 modest_platform_system_banner (NULL, NULL, _CS("ecdg_ib_find_rep_enter_text"));
1606 if ((priv->last_search == NULL) || (strcmp (priv->last_search, current_search) != 0)) {
1608 g_free (priv->last_search);
1609 priv->last_search = g_strdup (current_search);
1610 result = modest_isearch_view_search (MODEST_ISEARCH_VIEW (priv->msg_view),
1613 modest_platform_system_banner (NULL, NULL,
1614 _HL("ckct_ib_find_no_matches"));
1615 g_free (priv->last_search);
1616 priv->last_search = NULL;
1618 /****** HILDON2:START */
1619 hildon_find_toolbar_highlight_entry (HILDON_FIND_TOOLBAR (priv->find_toolbar), TRUE);
1620 /****** HILDON2:END */
1623 if (!modest_isearch_view_search_next (MODEST_ISEARCH_VIEW (priv->msg_view))) {
1624 modest_platform_system_banner (NULL, NULL,
1625 _HL("ckct_ib_find_search_complete"));
1626 g_free (priv->last_search);
1627 priv->last_search = NULL;
1629 /****** HILDON2:START */
1630 hildon_find_toolbar_highlight_entry (HILDON_FIND_TOOLBAR (priv->find_toolbar), TRUE);
1631 /****** HILDON2:END */
1635 g_free (current_search);
1640 modest_msg_view_window_set_zoom (ModestWindow *window,
1643 ModestMsgViewWindowPrivate *priv;
1644 ModestWindowPrivate *parent_priv;
1646 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window));
1648 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1649 parent_priv = MODEST_WINDOW_GET_PRIVATE (window);
1650 modest_zoomable_set_zoom (MODEST_ZOOMABLE (priv->msg_view), zoom);
1655 modest_msg_view_window_get_zoom (ModestWindow *window)
1657 ModestMsgViewWindowPrivate *priv;
1659 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), 1.0);
1661 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1662 return modest_zoomable_get_zoom (MODEST_ZOOMABLE (priv->msg_view));
1666 modest_msg_view_window_zoom_plus (ModestWindow *window)
1669 ModestMsgViewWindowPrivate *priv;
1673 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), 1.0);
1674 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1676 zoom_level = modest_zoomable_get_zoom (MODEST_ZOOMABLE (priv->msg_view));
1678 if (zoom_level >= 2.0) {
1679 modest_platform_system_banner (NULL, NULL,
1680 _CS("ckct_ib_max_zoom_level_reached"));
1682 } else if (zoom_level >= 1.5) {
1684 } else if (zoom_level >= 1.2) {
1686 } else if (zoom_level >= 1.0) {
1688 } else if (zoom_level >= 0.8) {
1690 } else if (zoom_level >= 0.5) {
1696 /* set zoom level */
1697 int_zoom = (gint) rint (zoom_level*100.0+0.1);
1698 banner_text = g_strdup_printf (_HL("wdgt_ib_zoom"), int_zoom);
1699 modest_platform_information_banner (GTK_WIDGET (window), NULL, banner_text);
1700 g_free (banner_text);
1701 modest_zoomable_set_zoom (MODEST_ZOOMABLE (priv->msg_view), zoom_level);
1707 modest_msg_view_window_zoom_minus (ModestWindow *window)
1710 ModestMsgViewWindowPrivate *priv;
1714 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), 1.0);
1715 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1717 zoom_level = modest_zoomable_get_zoom (MODEST_ZOOMABLE (priv->msg_view));
1719 if (zoom_level <= 0.5) {
1720 modest_platform_system_banner (NULL, NULL,
1721 _CS("ckct_ib_min_zoom_level_reached"));
1723 } else if (zoom_level <= 0.8) {
1725 } else if (zoom_level <= 1.0) {
1727 } else if (zoom_level <= 1.2) {
1729 } else if (zoom_level <= 1.5) {
1731 } else if (zoom_level <= 2.0) {
1737 /* set zoom level */
1738 int_zoom = (gint) rint (zoom_level*100.0+0.1);
1739 banner_text = g_strdup_printf (_HL("wdgt_ib_zoom"), int_zoom);
1740 modest_platform_information_banner (GTK_WIDGET (window), NULL, banner_text);
1741 g_free (banner_text);
1742 modest_zoomable_set_zoom (MODEST_ZOOMABLE (priv->msg_view), zoom_level);
1748 modest_msg_view_window_key_event (GtkWidget *window,
1754 focus = gtk_window_get_focus (GTK_WINDOW (window));
1756 /* for the find toolbar case */
1757 if (focus && GTK_IS_ENTRY (focus)) {
1758 if (event->keyval == GDK_BackSpace) {
1760 copy = gdk_event_copy ((GdkEvent *) event);
1761 gtk_widget_event (focus, copy);
1762 gdk_event_free (copy);
1772 modest_msg_view_window_last_message_selected (ModestMsgViewWindow *window)
1775 ModestMsgViewWindowPrivate *priv;
1776 GtkTreeIter tmp_iter;
1777 gboolean is_last_selected;
1779 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), TRUE);
1780 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1782 /*if no model (so no rows at all), then virtually we are the last*/
1783 if (!priv->header_model || !priv->row_reference)
1786 if (!gtk_tree_row_reference_valid (priv->row_reference))
1789 path = gtk_tree_row_reference_get_path (priv->row_reference);
1793 is_last_selected = TRUE;
1794 while (is_last_selected) {
1796 gtk_tree_path_next (path);
1797 if (!gtk_tree_model_get_iter (priv->header_model, &tmp_iter, path))
1799 gtk_tree_model_get (priv->header_model, &tmp_iter,
1800 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
1803 if (msg_is_visible (header, priv->is_outbox))
1804 is_last_selected = FALSE;
1805 g_object_unref(G_OBJECT(header));
1808 gtk_tree_path_free (path);
1809 return is_last_selected;
1813 modest_msg_view_window_has_headers_model (ModestMsgViewWindow *window)
1815 ModestMsgViewWindowPrivate *priv;
1817 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), TRUE);
1818 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1820 return priv->header_model != NULL;
1824 modest_msg_view_window_is_search_result (ModestMsgViewWindow *window)
1826 ModestMsgViewWindowPrivate *priv;
1828 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), TRUE);
1829 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1831 return priv->is_search_result;
1835 msg_is_visible (TnyHeader *header, gboolean check_outbox)
1837 if ((tny_header_get_flags(header) & TNY_HEADER_FLAG_DELETED))
1839 if (!check_outbox) {
1842 ModestTnySendQueueStatus status;
1843 status = modest_tny_all_send_queues_get_msg_status (header);
1844 return ((status != MODEST_TNY_SEND_QUEUE_FAILED) &&
1845 (status != MODEST_TNY_SEND_QUEUE_SENDING));
1850 modest_msg_view_window_first_message_selected (ModestMsgViewWindow *window)
1853 ModestMsgViewWindowPrivate *priv;
1854 gboolean is_first_selected;
1855 GtkTreeIter tmp_iter;
1857 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), TRUE);
1858 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1860 /*if no model (so no rows at all), then virtually we are the first*/
1861 if (!priv->header_model || !priv->row_reference)
1864 if (!gtk_tree_row_reference_valid (priv->row_reference))
1867 path = gtk_tree_row_reference_get_path (priv->row_reference);
1871 is_first_selected = TRUE;
1872 while (is_first_selected) {
1874 if(!gtk_tree_path_prev (path))
1876 /* Here the 'if' is needless for logic, but let make sure
1877 * iter is valid for gtk_tree_model_get. */
1878 if (!gtk_tree_model_get_iter (priv->header_model, &tmp_iter, path))
1880 gtk_tree_model_get (priv->header_model, &tmp_iter,
1881 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
1884 if (msg_is_visible (header, priv->is_outbox))
1885 is_first_selected = FALSE;
1886 g_object_unref(G_OBJECT(header));
1889 gtk_tree_path_free (path);
1890 return is_first_selected;
1897 GtkTreeRowReference *row_reference;
1901 message_reader_performer (gboolean canceled,
1903 GtkWindow *parent_window,
1904 TnyAccount *account,
1907 ModestMailOperation *mail_op = NULL;
1908 MsgReaderInfo *info;
1910 info = (MsgReaderInfo *) user_data;
1911 if (canceled || err) {
1912 update_window_title (MODEST_MSG_VIEW_WINDOW (parent_window));
1916 /* Register the header - it'll be unregistered in the callback */
1918 modest_window_mgr_register_header (modest_runtime_get_window_mgr (), info->header, NULL);
1920 /* New mail operation */
1921 mail_op = modest_mail_operation_new_with_error_handling (G_OBJECT(parent_window),
1922 modest_ui_actions_disk_operations_error_handler,
1925 modest_mail_operation_queue_add (modest_runtime_get_mail_operation_queue (), mail_op);
1927 modest_mail_operation_get_msg (mail_op, info->header, TRUE, view_msg_cb, info->row_reference);
1929 modest_mail_operation_find_msg (mail_op, info->folder, info->msg_uid, TRUE, view_msg_cb, NULL);
1930 g_object_unref (mail_op);
1932 /* Update dimming rules */
1933 modest_ui_actions_check_toolbar_dimming_rules (MODEST_WINDOW (parent_window));
1934 modest_ui_actions_check_menu_dimming_rules (MODEST_WINDOW (parent_window));
1937 /* Frees. The row_reference will be freed by the view_msg_cb callback */
1938 g_free (info->msg_uid);
1940 g_object_unref (info->folder);
1942 g_object_unref (info->header);
1943 g_slice_free (MsgReaderInfo, info);
1948 * Reads the message whose summary item is @header. It takes care of
1949 * several things, among others:
1951 * If the message was not previously downloaded then ask the user
1952 * before downloading. If there is no connection launch the connection
1953 * dialog. Update toolbar dimming rules.
1955 * Returns: TRUE if the mail operation was started, otherwise if the
1956 * user do not want to download the message, or if the user do not
1957 * want to connect, then the operation is not issued
1960 message_reader (ModestMsgViewWindow *window,
1961 ModestMsgViewWindowPrivate *priv,
1963 const gchar *msg_uid,
1965 GtkTreeRowReference *row_reference)
1967 ModestWindowMgr *mgr;
1968 TnyAccount *account = NULL;
1969 MsgReaderInfo *info;
1971 /* We set the header from model while we're loading */
1972 tny_header_view_set_header (TNY_HEADER_VIEW (priv->msg_view), header);
1973 gtk_window_set_title (GTK_WINDOW (window), _CS("ckdg_pb_updating"));
1979 g_object_ref (folder);
1981 mgr = modest_runtime_get_window_mgr ();
1982 /* Msg download completed */
1983 if (!header || !(tny_header_get_flags (header) & TNY_HEADER_FLAG_CACHED)) {
1985 /* Ask the user if he wants to download the message if
1987 if (!tny_device_is_online (modest_runtime_get_device())) {
1988 GtkResponseType response;
1990 response = modest_platform_run_confirmation_dialog (GTK_WINDOW (window),
1991 _("mcen_nc_get_msg"));
1992 if (response == GTK_RESPONSE_CANCEL) {
1993 update_window_title (window);
1998 folder = tny_header_get_folder (header);
2000 info = g_slice_new (MsgReaderInfo);
2001 info->msg_uid = g_strdup (msg_uid);
2003 info->header = g_object_ref (header);
2005 info->header = NULL;
2007 info->folder = g_object_ref (folder);
2009 info->folder = NULL;
2010 if (row_reference) {
2011 info->row_reference = gtk_tree_row_reference_copy (row_reference);
2013 info->row_reference = NULL;
2016 /* Offer the connection dialog if necessary */
2017 modest_platform_connect_if_remote_and_perform ((GtkWindow *) window,
2019 TNY_FOLDER_STORE (folder),
2020 message_reader_performer,
2023 g_object_unref (folder);
2029 folder = tny_header_get_folder (header);
2032 account = tny_folder_get_account (folder);
2034 info = g_slice_new (MsgReaderInfo);
2035 info->msg_uid = g_strdup (msg_uid);
2037 info->folder = g_object_ref (folder);
2039 info->folder = NULL;
2041 info->header = g_object_ref (header);
2043 info->header = NULL;
2045 info->row_reference = gtk_tree_row_reference_copy (row_reference);
2047 info->row_reference = NULL;
2049 message_reader_performer (FALSE, NULL, (GtkWindow *) window, account, info);
2051 g_object_unref (account);
2053 g_object_unref (folder);
2059 modest_msg_view_window_select_next_message (ModestMsgViewWindow *window)
2061 ModestMsgViewWindowPrivate *priv;
2062 GtkTreePath *path= NULL;
2063 GtkTreeIter tmp_iter;
2065 gboolean retval = TRUE;
2066 GtkTreeRowReference *row_reference = NULL;
2068 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), FALSE);
2069 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
2071 if (!priv->row_reference)
2074 /* Update the next row reference if it's not valid. This could
2075 happen if for example the header which it was pointing to,
2076 was deleted. The best place to do it is in the row-deleted
2077 handler but the tinymail model do not work like the glib
2078 tree models and reports the deletion when the row is still
2080 if (!gtk_tree_row_reference_valid (priv->next_row_reference)) {
2081 if (priv->next_row_reference) {
2082 gtk_tree_row_reference_free (priv->next_row_reference);
2084 if (gtk_tree_row_reference_valid (priv->row_reference)) {
2085 priv->next_row_reference = gtk_tree_row_reference_copy (priv->row_reference);
2086 select_next_valid_row (priv->header_model, &(priv->next_row_reference), FALSE, priv->is_outbox);
2088 priv->next_row_reference = NULL;
2091 if (priv->next_row_reference)
2092 path = gtk_tree_row_reference_get_path (priv->next_row_reference);
2096 row_reference = gtk_tree_row_reference_copy (priv->next_row_reference);
2098 gtk_tree_model_get_iter (priv->header_model,
2101 gtk_tree_path_free (path);
2103 gtk_tree_model_get (priv->header_model, &tmp_iter,
2104 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
2107 /* Read the message & show it */
2108 if (!message_reader (window, priv, header, NULL, NULL, row_reference)) {
2111 gtk_tree_row_reference_free (row_reference);
2114 g_object_unref (header);
2120 modest_msg_view_window_select_previous_message (ModestMsgViewWindow *window)
2122 ModestMsgViewWindowPrivate *priv = NULL;
2124 gboolean finished = FALSE;
2125 gboolean retval = FALSE;
2127 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), FALSE);
2128 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
2130 if (priv->row_reference && !gtk_tree_row_reference_valid (priv->row_reference)) {
2131 gtk_tree_row_reference_free (priv->row_reference);
2132 priv->row_reference = NULL;
2135 /* Return inmediatly if there is no header model */
2136 if (!priv->header_model || !priv->row_reference)
2139 path = gtk_tree_row_reference_get_path (priv->row_reference);
2140 while (!finished && gtk_tree_path_prev (path)) {
2144 gtk_tree_model_get_iter (priv->header_model, &iter, path);
2145 gtk_tree_model_get (priv->header_model, &iter,
2146 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
2150 if (msg_is_visible (header, priv->is_outbox)) {
2151 GtkTreeRowReference *row_reference;
2152 row_reference = gtk_tree_row_reference_new (priv->header_model, path);
2153 /* Read the message & show it */
2154 retval = message_reader (window, priv, header, NULL, NULL, row_reference);
2155 gtk_tree_row_reference_free (row_reference);
2159 g_object_unref (header);
2163 gtk_tree_path_free (path);
2168 view_msg_cb (ModestMailOperation *mail_op,
2175 ModestMsgViewWindow *self = NULL;
2176 ModestMsgViewWindowPrivate *priv = NULL;
2177 GtkTreeRowReference *row_reference = NULL;
2179 /* Unregister the header (it was registered before creating the mail operation) */
2180 modest_window_mgr_unregister_header (modest_runtime_get_window_mgr (), header);
2182 row_reference = (GtkTreeRowReference *) user_data;
2185 gtk_tree_row_reference_free (row_reference);
2186 self = (ModestMsgViewWindow *) modest_mail_operation_get_source (mail_op);
2188 /* Restore window title */
2189 update_window_title (self);
2190 modest_ui_actions_on_close_window (NULL, MODEST_WINDOW (self));
2191 g_object_unref (self);
2196 /* If there was any error */
2197 if (!modest_ui_actions_msg_retrieval_check (mail_op, header, msg)) {
2199 gtk_tree_row_reference_free (row_reference);
2200 self = (ModestMsgViewWindow *) modest_mail_operation_get_source (mail_op);
2202 /* Restore window title */
2203 update_window_title (self);
2204 modest_ui_actions_on_close_window (NULL, MODEST_WINDOW (self));
2205 g_object_unref (self);
2210 /* Get the window */
2211 self = (ModestMsgViewWindow *) modest_mail_operation_get_source (mail_op);
2212 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self));
2213 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
2215 /* Update the row reference */
2216 if (priv->row_reference != NULL) {
2217 gtk_tree_row_reference_free (priv->row_reference);
2218 priv->row_reference = (row_reference && gtk_tree_row_reference_valid (row_reference))?gtk_tree_row_reference_copy (row_reference):NULL;
2219 if (priv->next_row_reference != NULL) {
2220 gtk_tree_row_reference_free (priv->next_row_reference);
2222 if (priv->row_reference) {
2223 priv->next_row_reference = gtk_tree_row_reference_copy (priv->row_reference);
2224 select_next_valid_row (priv->header_model, &(priv->next_row_reference), TRUE, priv->is_outbox);
2226 priv->next_row_reference = NULL;
2230 /* Mark header as read */
2231 if (!(tny_header_get_flags (header) & TNY_HEADER_FLAG_SEEN))
2232 tny_header_set_flag (header, TNY_HEADER_FLAG_SEEN);
2234 /* Set new message */
2235 if (priv->msg_view != NULL && TNY_IS_MSG_VIEW (priv->msg_view)) {
2236 tny_msg_view_set_msg (TNY_MSG_VIEW (priv->msg_view), msg);
2237 modest_msg_view_window_update_priority (self);
2238 update_window_title (MODEST_MSG_VIEW_WINDOW (self));
2239 update_branding (MODEST_MSG_VIEW_WINDOW (self));
2240 modest_msg_view_grab_focus (MODEST_MSG_VIEW (priv->msg_view));
2243 /* Set the new message uid of the window */
2244 if (priv->msg_uid) {
2245 g_free (priv->msg_uid);
2246 priv->msg_uid = modest_tny_folder_get_header_unique_id (header);
2249 /* Notify the observers */
2250 g_signal_emit (G_OBJECT (self), signals[MSG_CHANGED_SIGNAL],
2251 0, priv->header_model, priv->row_reference);
2253 /* Sync the flags if the message is not opened from a header
2254 model, i.e, if it's opened from a notification */
2255 if (!priv->header_model)
2259 g_object_unref (self);
2261 gtk_tree_row_reference_free (row_reference);
2265 modest_msg_view_window_get_folder_type (ModestMsgViewWindow *window)
2267 ModestMsgViewWindowPrivate *priv;
2269 TnyFolderType folder_type;
2271 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
2273 folder_type = TNY_FOLDER_TYPE_UNKNOWN;
2275 msg = tny_msg_view_get_msg (TNY_MSG_VIEW (priv->msg_view));
2279 folder = tny_msg_get_folder (msg);
2281 folder_type = modest_tny_folder_guess_folder_type (folder);
2282 g_object_unref (folder);
2284 g_object_unref (msg);
2292 modest_msg_view_window_update_priority (ModestMsgViewWindow *window)
2294 ModestMsgViewWindowPrivate *priv;
2295 TnyHeader *header = NULL;
2296 TnyHeaderFlags flags = 0;
2298 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
2300 if (priv->header_model && priv->row_reference && gtk_tree_row_reference_valid (priv->row_reference)) {
2302 GtkTreePath *path = NULL;
2304 path = gtk_tree_row_reference_get_path (priv->row_reference);
2305 g_return_if_fail (path != NULL);
2306 gtk_tree_model_get_iter (priv->header_model,
2308 gtk_tree_row_reference_get_path (priv->row_reference));
2310 gtk_tree_model_get (priv->header_model, &iter, TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
2312 gtk_tree_path_free (path);
2315 msg = tny_msg_view_get_msg (TNY_MSG_VIEW (priv->msg_view));
2317 header = tny_msg_get_header (msg);
2318 g_object_unref (msg);
2323 flags = tny_header_get_flags (header);
2324 g_object_unref(G_OBJECT(header));
2327 modest_msg_view_set_priority (MODEST_MSG_VIEW(priv->msg_view), flags);
2332 toolbar_resize (ModestMsgViewWindow *self)
2334 ModestMsgViewWindowPrivate *priv = NULL;
2335 ModestWindowPrivate *parent_priv = NULL;
2337 gint static_button_size;
2338 ModestWindowMgr *mgr;
2340 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self));
2341 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
2342 parent_priv = MODEST_WINDOW_GET_PRIVATE(self);
2344 mgr = modest_runtime_get_window_mgr ();
2345 static_button_size = modest_window_mgr_get_fullscreen_mode (mgr)?120:120;
2347 if (parent_priv->toolbar) {
2348 /* Set expandable and homogeneous tool buttons */
2349 widget = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/ToolbarMessageReply");
2350 gtk_tool_item_set_expand (GTK_TOOL_ITEM (widget), TRUE);
2351 gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (widget), TRUE);
2352 widget = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/ToolbarMessageReplyAll");
2353 gtk_tool_item_set_expand (GTK_TOOL_ITEM (widget), TRUE);
2354 gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (widget), TRUE);
2355 widget = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/ToolbarMessageForward");
2356 gtk_tool_item_set_expand (GTK_TOOL_ITEM (widget), TRUE);
2357 gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (widget), TRUE);
2358 widget = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/ToolbarDeleteMessage");
2359 gtk_tool_item_set_expand (GTK_TOOL_ITEM (widget), TRUE);
2360 gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (widget), TRUE);
2361 widget = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/ToolbarDownloadExternalImages");
2362 gtk_tool_item_set_expand (GTK_TOOL_ITEM (widget), TRUE);
2363 gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (widget), TRUE);
2364 gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (priv->next_toolitem), TRUE);
2365 gtk_tool_item_set_expand (GTK_TOOL_ITEM (priv->next_toolitem), TRUE);
2366 gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (priv->prev_toolitem), TRUE);
2367 gtk_tool_item_set_expand (GTK_TOOL_ITEM (priv->prev_toolitem), TRUE);
2372 modest_msg_view_window_show_toolbar (ModestWindow *self,
2373 gboolean show_toolbar)
2375 ModestMsgViewWindowPrivate *priv = NULL;
2376 ModestWindowPrivate *parent_priv;
2378 parent_priv = MODEST_WINDOW_GET_PRIVATE(self);
2379 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(self);
2381 /* Set optimized view status */
2382 priv->optimized_view = !show_toolbar;
2384 if (!parent_priv->toolbar) {
2385 parent_priv->toolbar = gtk_ui_manager_get_widget (parent_priv->ui_manager,
2387 gtk_toolbar_set_icon_size (GTK_TOOLBAR (parent_priv->toolbar), HILDON_ICON_SIZE_FINGER);
2388 gtk_widget_set_no_show_all (parent_priv->toolbar, TRUE);
2390 priv->next_toolitem = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/ToolbarMessageNext");
2391 priv->prev_toolitem = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/ToolbarMessageBack");
2392 toolbar_resize (MODEST_MSG_VIEW_WINDOW (self));
2394 /****** HILDON2:START
2395 * attach toolbar to window
2398 hildon_window_add_toolbar (HILDON_WINDOW (self),
2399 GTK_TOOLBAR (parent_priv->toolbar));
2400 /****** HILDON2:END */
2405 /* Quick hack: this prevents toolbar icons "dance" when progress bar show status is changed */
2406 /* TODO: resize mode migth be GTK_RESIZE_QUEUE, in order to avoid unneccesary shows */
2407 gtk_container_set_resize_mode (GTK_CONTAINER(parent_priv->toolbar), GTK_RESIZE_IMMEDIATE);
2409 gtk_widget_show (GTK_WIDGET (parent_priv->toolbar));
2410 if (modest_msg_view_window_transfer_mode_enabled (MODEST_MSG_VIEW_WINDOW (self)))
2411 set_progress_hint (MODEST_MSG_VIEW_WINDOW (self), TRUE);
2413 set_progress_hint (MODEST_MSG_VIEW_WINDOW (self), FALSE);
2416 gtk_widget_set_no_show_all (parent_priv->toolbar, TRUE);
2417 gtk_widget_hide (GTK_WIDGET (parent_priv->toolbar));
2422 modest_msg_view_window_clipboard_owner_change (GtkClipboard *clipboard,
2424 ModestMsgViewWindow *window)
2426 if (!GTK_WIDGET_VISIBLE (window))
2429 modest_window_check_dimming_rules_group (MODEST_WINDOW (window), MODEST_DIMMING_RULES_CLIPBOARD);
2433 modest_msg_view_window_transfer_mode_enabled (ModestMsgViewWindow *self)
2435 ModestMsgViewWindowPrivate *priv;
2437 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self), FALSE);
2438 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(self);
2440 return priv->progress_hint;
2444 observers_empty (ModestMsgViewWindow *self)
2447 ModestMsgViewWindowPrivate *priv;
2448 gboolean is_empty = TRUE;
2449 guint pending_ops = 0;
2451 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(self);
2452 tmp = priv->progress_widgets;
2454 /* Check all observers */
2455 while (tmp && is_empty) {
2456 pending_ops = modest_progress_object_num_pending_operations (MODEST_PROGRESS_OBJECT(tmp->data));
2457 is_empty = pending_ops == 0;
2459 tmp = g_slist_next(tmp);
2466 on_account_removed (TnyAccountStore *account_store,
2467 TnyAccount *account,
2470 /* Do nothing if it's a transport account, because we only
2471 show the messages of a store account */
2472 if (tny_account_get_account_type(account) == TNY_ACCOUNT_TYPE_STORE) {
2473 const gchar *parent_acc = NULL;
2474 const gchar *our_acc = NULL;
2476 our_acc = modest_window_get_active_account (MODEST_WINDOW (user_data));
2477 parent_acc = modest_tny_account_get_parent_modest_account_name_for_server_account (account);
2479 /* Close this window if I'm showing a message of the removed account */
2480 if (our_acc && parent_acc && strcmp (parent_acc, our_acc) == 0)
2481 modest_ui_actions_on_close_window (NULL, MODEST_WINDOW (user_data));
2486 on_mail_operation_started (ModestMailOperation *mail_op,
2489 ModestMsgViewWindow *self;
2490 ModestMailOperationTypeOperation op_type;
2492 ModestMsgViewWindowPrivate *priv;
2493 GObject *source = NULL;
2495 self = MODEST_MSG_VIEW_WINDOW (user_data);
2496 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
2497 op_type = modest_mail_operation_get_type_operation (mail_op);
2498 tmp = priv->progress_widgets;
2499 source = modest_mail_operation_get_source(mail_op);
2500 if (G_OBJECT (self) == source) {
2501 if (op_type == MODEST_MAIL_OPERATION_TYPE_RECEIVE ||
2502 op_type == MODEST_MAIL_OPERATION_TYPE_OPEN ||
2503 op_type == MODEST_MAIL_OPERATION_TYPE_DELETE) {
2504 set_progress_hint (self, TRUE);
2506 modest_progress_object_add_operation (
2507 MODEST_PROGRESS_OBJECT (tmp->data),
2509 tmp = g_slist_next (tmp);
2513 g_object_unref (source);
2515 /* Update dimming rules */
2516 check_dimming_rules_after_change (self);
2520 on_mail_operation_finished (ModestMailOperation *mail_op,
2523 ModestMsgViewWindow *self;
2524 ModestMailOperationTypeOperation op_type;
2526 ModestMsgViewWindowPrivate *priv;
2528 self = MODEST_MSG_VIEW_WINDOW (user_data);
2529 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
2530 op_type = modest_mail_operation_get_type_operation (mail_op);
2531 tmp = priv->progress_widgets;
2533 if (op_type == MODEST_MAIL_OPERATION_TYPE_RECEIVE ||
2534 op_type == MODEST_MAIL_OPERATION_TYPE_OPEN ||
2535 op_type == MODEST_MAIL_OPERATION_TYPE_DELETE) {
2537 modest_progress_object_remove_operation (MODEST_PROGRESS_OBJECT (tmp->data),
2539 tmp = g_slist_next (tmp);
2542 /* If no more operations are being observed, NORMAL mode is enabled again */
2543 if (observers_empty (self)) {
2544 set_progress_hint (self, FALSE);
2548 /* Update dimming rules. We have to do this right here
2549 and not in view_msg_cb because at that point the
2550 transfer mode is still enabled so the dimming rule
2551 won't let the user delete the message that has been
2552 readed for example */
2553 check_dimming_rules_after_change (self);
2557 on_queue_changed (ModestMailOperationQueue *queue,
2558 ModestMailOperation *mail_op,
2559 ModestMailOperationQueueNotification type,
2560 ModestMsgViewWindow *self)
2562 ModestMsgViewWindowPrivate *priv;
2564 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
2566 /* If this operations was created by another window, do nothing */
2567 if (!modest_mail_operation_is_mine (mail_op, G_OBJECT(self)))
2570 if (type == MODEST_MAIL_OPERATION_QUEUE_OPERATION_ADDED) {
2571 priv->sighandlers = modest_signal_mgr_connect (priv->sighandlers,
2573 "operation-started",
2574 G_CALLBACK (on_mail_operation_started),
2576 priv->sighandlers = modest_signal_mgr_connect (priv->sighandlers,
2578 "operation-finished",
2579 G_CALLBACK (on_mail_operation_finished),
2581 } else if (type == MODEST_MAIL_OPERATION_QUEUE_OPERATION_REMOVED) {
2582 priv->sighandlers = modest_signal_mgr_disconnect (priv->sighandlers,
2584 "operation-started");
2585 priv->sighandlers = modest_signal_mgr_disconnect (priv->sighandlers,
2587 "operation-finished");
2592 modest_msg_view_window_get_attachments (ModestMsgViewWindow *win)
2594 ModestMsgViewWindowPrivate *priv;
2595 TnyList *selected_attachments = NULL;
2597 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (win), NULL);
2598 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (win);
2600 /* In Hildon 2.2 as there's no selection we assume we have all attachments selected */
2601 selected_attachments = modest_msg_view_get_attachments (MODEST_MSG_VIEW (priv->msg_view));
2603 return selected_attachments;
2607 ModestMsgViewWindow *self;
2609 gchar *attachment_uid;
2610 } DecodeAsyncHelper;
2613 on_decode_to_stream_async_handler (TnyMimePart *mime_part,
2619 DecodeAsyncHelper *helper = (DecodeAsyncHelper *) user_data;
2620 const gchar *content_type;
2622 if (cancelled || err) {
2625 if ((err->domain == TNY_ERROR_DOMAIN) &&
2626 (err->code == TNY_IO_ERROR_WRITE) &&
2627 (errno == ENOSPC)) {
2628 msg = g_strdup_printf (_KR("cerm_device_memory_full"), "");
2630 msg = g_strdup (_("mail_ib_file_operation_failed"));
2632 modest_platform_information_banner (NULL, NULL, msg);
2638 /* It could happen that the window was closed. So we
2639 assume it is a cancelation */
2640 if (!GTK_WIDGET_VISIBLE (helper->self))
2643 /* Remove the progress hint */
2644 set_progress_hint (helper->self, FALSE);
2646 content_type = tny_mime_part_get_content_type (mime_part);
2647 if (g_str_has_prefix (content_type, "message/rfc822")) {
2648 ModestWindowMgr *mgr;
2649 ModestWindow *msg_win = NULL;
2652 const gchar *mailbox;
2653 TnyStream *file_stream;
2656 fd = g_open (helper->file_path, O_RDONLY, 0644);
2658 file_stream = tny_fs_stream_new (fd);
2660 mgr = modest_runtime_get_window_mgr ();
2662 account = g_strdup (modest_window_get_active_account (MODEST_WINDOW (helper->self)));
2663 mailbox = modest_window_get_active_mailbox (MODEST_WINDOW (helper->self));
2666 account = modest_account_mgr_get_default_account (modest_runtime_get_account_mgr ());
2668 msg = tny_camel_msg_new ();
2669 tny_camel_msg_parse (msg, file_stream);
2670 msg_win = modest_msg_view_window_new_for_attachment (TNY_MSG (msg), account, mailbox, helper->attachment_uid);
2671 modest_window_set_zoom (MODEST_WINDOW (msg_win),
2672 modest_window_get_zoom (MODEST_WINDOW (helper->self)));
2673 if (modest_window_mgr_register_window (mgr, msg_win, MODEST_WINDOW (helper->self)))
2674 gtk_widget_show_all (GTK_WIDGET (msg_win));
2676 gtk_widget_destroy (GTK_WIDGET (msg_win));
2677 g_object_unref (msg);
2678 g_object_unref (file_stream);
2680 modest_platform_information_banner (NULL, NULL, _("mail_ib_file_operation_failed"));
2685 /* make the file read-only */
2686 g_chmod(helper->file_path, 0444);
2688 /* Activate the file */
2689 modest_platform_activate_file (helper->file_path, tny_mime_part_get_content_type (mime_part));
2694 g_object_unref (helper->self);
2695 g_free (helper->file_path);
2696 g_free (helper->attachment_uid);
2697 g_slice_free (DecodeAsyncHelper, helper);
2701 modest_msg_view_window_view_attachment (ModestMsgViewWindow *window,
2702 TnyMimePart *mime_part)
2704 ModestMsgViewWindowPrivate *priv;
2705 const gchar *msg_uid;
2706 gchar *attachment_uid = NULL;
2707 gint attachment_index = 0;
2708 TnyList *attachments;
2710 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window));
2711 g_return_if_fail (TNY_IS_MIME_PART (mime_part) || (mime_part == NULL));
2712 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
2714 msg_uid = modest_msg_view_window_get_message_uid (MODEST_MSG_VIEW_WINDOW (window));
2715 attachments = modest_msg_view_get_attachments (MODEST_MSG_VIEW (priv->msg_view));
2716 attachment_index = modest_list_index (attachments, (GObject *) mime_part);
2717 g_object_unref (attachments);
2719 if (msg_uid && attachment_index >= 0) {
2720 attachment_uid = g_strdup_printf ("%s/%d", msg_uid, attachment_index);
2723 if (mime_part == NULL) {
2724 gboolean error = FALSE;
2725 TnyList *selected_attachments = modest_msg_view_get_selected_attachments (MODEST_MSG_VIEW (priv->msg_view));
2726 if (selected_attachments == NULL || tny_list_get_length (selected_attachments) == 0) {
2728 } else if (tny_list_get_length (selected_attachments) > 1) {
2729 modest_platform_system_banner (NULL, NULL, _("mcen_ib_unable_to_display_more"));
2733 iter = tny_list_create_iterator (selected_attachments);
2734 mime_part = (TnyMimePart *) tny_iterator_get_current (iter);
2735 g_object_unref (iter);
2737 if (selected_attachments)
2738 g_object_unref (selected_attachments);
2743 g_object_ref (mime_part);
2746 if (tny_mime_part_is_purged (mime_part))
2749 if (!modest_tny_mime_part_is_msg (mime_part) && tny_mime_part_get_filename (mime_part)) {
2750 gchar *filepath = NULL;
2751 const gchar *att_filename = tny_mime_part_get_filename (mime_part);
2752 gboolean show_error_banner = FALSE;
2753 TnyFsStream *temp_stream = NULL;
2754 temp_stream = modest_utils_create_temp_stream (att_filename, attachment_uid,
2757 if (temp_stream != NULL) {
2758 ModestAccountMgr *mgr;
2759 DecodeAsyncHelper *helper;
2760 gboolean decode_in_provider;
2761 ModestProtocol *protocol;
2762 const gchar *account;
2764 /* Activate progress hint */
2765 set_progress_hint (window, TRUE);
2767 helper = g_slice_new0 (DecodeAsyncHelper);
2768 helper->self = g_object_ref (window);
2769 helper->file_path = g_strdup (filepath);
2770 helper->attachment_uid = g_strdup (attachment_uid);
2772 decode_in_provider = FALSE;
2773 mgr = modest_runtime_get_account_mgr ();
2774 account = modest_window_get_active_account (MODEST_WINDOW (window));
2775 if (modest_account_mgr_account_is_multimailbox (mgr, account, &protocol)) {
2776 if (MODEST_IS_ACCOUNT_PROTOCOL (protocol)) {
2778 uri = g_strconcat ("file://", filepath, NULL);
2779 decode_in_provider =
2780 modest_account_protocol_decode_part_to_stream_async (
2781 MODEST_ACCOUNT_PROTOCOL (protocol),
2784 TNY_STREAM (temp_stream),
2785 on_decode_to_stream_async_handler,
2792 if (!decode_in_provider)
2793 tny_mime_part_decode_to_stream_async (mime_part, TNY_STREAM (temp_stream),
2794 on_decode_to_stream_async_handler,
2797 g_object_unref (temp_stream);
2798 /* NOTE: files in the temporary area will be automatically
2799 * cleaned after some time if they are no longer in use */
2802 const gchar *content_type;
2803 /* the file may already exist but it isn't writable,
2804 * let's try to open it anyway */
2805 content_type = tny_mime_part_get_content_type (mime_part);
2806 modest_platform_activate_file (filepath, content_type);
2808 g_warning ("%s: modest_utils_create_temp_stream failed", __FUNCTION__);
2809 show_error_banner = TRUE;
2814 if (show_error_banner)
2815 modest_platform_information_banner (NULL, NULL, _("mail_ib_file_operation_failed"));
2816 } else if (!modest_tny_mime_part_is_msg (mime_part)) {
2817 ModestWindowMgr *mgr;
2818 ModestWindow *msg_win = NULL;
2819 TnyMsg *current_msg;
2823 current_msg = modest_msg_view_window_get_message (MODEST_MSG_VIEW_WINDOW (window));
2824 mgr = modest_runtime_get_window_mgr ();
2825 header = tny_msg_get_header (TNY_MSG (current_msg));
2826 found = modest_window_mgr_find_registered_message_uid (mgr,
2831 g_debug ("window for this body is already being created");
2834 /* it's not found, so create a new window for it */
2835 modest_window_mgr_register_header (mgr, header, attachment_uid); /* register the uid before building the window */
2836 gchar *account = g_strdup (modest_window_get_active_account (MODEST_WINDOW (window)));
2837 const gchar *mailbox = modest_window_get_active_mailbox (MODEST_WINDOW (window));
2839 account = modest_account_mgr_get_default_account (modest_runtime_get_account_mgr ());
2841 msg_win = modest_msg_view_window_new_with_other_body (TNY_MSG (current_msg), TNY_MIME_PART (mime_part),
2842 account, mailbox, attachment_uid);
2844 modest_window_set_zoom (MODEST_WINDOW (msg_win),
2845 modest_window_get_zoom (MODEST_WINDOW (window)));
2846 if (modest_window_mgr_register_window (mgr, msg_win, MODEST_WINDOW (window)))
2847 gtk_widget_show_all (GTK_WIDGET (msg_win));
2849 gtk_widget_destroy (GTK_WIDGET (msg_win));
2851 g_object_unref (current_msg);
2853 /* message attachment */
2854 TnyHeader *header = NULL;
2855 ModestWindowMgr *mgr;
2856 ModestWindow *msg_win = NULL;
2859 header = tny_msg_get_header (TNY_MSG (mime_part));
2860 mgr = modest_runtime_get_window_mgr ();
2861 found = modest_window_mgr_find_registered_header (mgr, header, &msg_win);
2864 /* if it's found, but there is no msg_win, it's probably in the process of being created;
2865 * thus, we don't do anything */
2866 g_debug ("window for is already being created");
2868 /* it's not found, so create a new window for it */
2869 modest_window_mgr_register_header (mgr, header, attachment_uid); /* register the uid before building the window */
2870 gchar *account = g_strdup (modest_window_get_active_account (MODEST_WINDOW (window)));
2871 const gchar *mailbox = modest_window_get_active_mailbox (MODEST_WINDOW (window));
2873 account = modest_account_mgr_get_default_account (modest_runtime_get_account_mgr ());
2874 msg_win = modest_msg_view_window_new_for_attachment (TNY_MSG (mime_part), account,
2875 mailbox, attachment_uid);
2876 modest_window_set_zoom (MODEST_WINDOW (msg_win),
2877 modest_window_get_zoom (MODEST_WINDOW (window)));
2878 if (modest_window_mgr_register_window (mgr, msg_win, MODEST_WINDOW (window)))
2879 gtk_widget_show_all (GTK_WIDGET (msg_win));
2881 gtk_widget_destroy (GTK_WIDGET (msg_win));
2887 g_free (attachment_uid);
2889 g_object_unref (mime_part);
2901 GnomeVFSResult result;
2903 ModestMsgViewWindow *window;
2906 static void save_mime_part_info_free (SaveMimePartInfo *info, gboolean with_struct);
2907 static gboolean idle_save_mime_part_show_result (SaveMimePartInfo *info);
2908 static gpointer save_mime_part_to_file (SaveMimePartInfo *info);
2909 static void save_mime_parts_to_file_with_checks (GtkWindow *parent, SaveMimePartInfo *info);
2912 save_mime_part_info_free (SaveMimePartInfo *info, gboolean with_struct)
2916 for (node = info->pairs; node != NULL; node = g_list_next (node)) {
2917 SaveMimePartPair *pair = (SaveMimePartPair *) node->data;
2918 g_free (pair->filename);
2919 g_object_unref (pair->part);
2920 g_slice_free (SaveMimePartPair, pair);
2922 g_list_free (info->pairs);
2925 g_object_unref (info->window);
2926 info->window = NULL;
2928 g_slice_free (SaveMimePartInfo, info);
2933 idle_save_mime_part_show_result (SaveMimePartInfo *info)
2935 /* This is a GDK lock because we are an idle callback and
2936 * modest_platform_system_banner is or does Gtk+ code */
2938 gdk_threads_enter (); /* CHECKED */
2939 if (info->result == GNOME_VFS_OK) {
2940 modest_platform_system_banner (NULL, NULL, _CS("sfil_ib_saved"));
2941 } else if (info->result == GNOME_VFS_ERROR_NO_SPACE) {
2944 /* Check if the uri belongs to the external mmc */
2945 if (g_str_has_prefix (info->uri, g_getenv (MODEST_MMC1_VOLUMEPATH_ENV)))
2946 msg = g_strdup_printf (_KR("cerm_device_memory_full"), "");
2948 msg = g_strdup (_KR("cerm_memory_card_full"));
2949 modest_platform_information_banner (NULL, NULL, msg);
2952 modest_platform_system_banner (NULL, NULL, _("mail_ib_file_operation_failed"));
2954 save_mime_part_info_free (info, FALSE);
2955 gdk_threads_leave (); /* CHECKED */
2961 save_mime_part_to_file (SaveMimePartInfo *info)
2963 GnomeVFSHandle *handle;
2965 SaveMimePartPair *pair = (SaveMimePartPair *) info->pairs->data;
2967 info->result = gnome_vfs_create (&handle, pair->filename, GNOME_VFS_OPEN_WRITE, FALSE, 0644);
2968 if (info->result == GNOME_VFS_OK) {
2969 GError *error = NULL;
2970 gboolean decode_in_provider;
2972 ModestAccountMgr *mgr;
2973 const gchar *account;
2974 ModestProtocol *protocol = NULL;
2976 stream = tny_vfs_stream_new (handle);
2978 decode_in_provider = FALSE;
2979 mgr = modest_runtime_get_account_mgr ();
2980 account = modest_window_get_active_account (MODEST_WINDOW (info->window));
2981 if (modest_account_mgr_account_is_multimailbox (mgr, account, &protocol)) {
2982 if (MODEST_IS_ACCOUNT_PROTOCOL (protocol)) {
2983 decode_in_provider =
2984 modest_account_protocol_decode_part_to_stream (
2985 MODEST_ACCOUNT_PROTOCOL (protocol),
2993 if (!decode_in_provider)
2994 written = tny_mime_part_decode_to_stream (pair->part, stream, &error);
2997 g_warning ("modest: could not save attachment %s: %d (%s)\n", pair->filename, error?error->code:-1, error?error->message:"Unknown error");
2999 if ((error->domain == TNY_ERROR_DOMAIN) &&
3000 (error->code == TNY_IO_ERROR_WRITE) &&
3001 (errno == ENOSPC)) {
3002 info->result = GNOME_VFS_ERROR_NO_SPACE;
3004 info->result = GNOME_VFS_ERROR_IO;
3007 g_object_unref (G_OBJECT (stream));
3009 g_warning ("Could not create save attachment %s: %s\n",
3010 pair->filename, gnome_vfs_result_to_string (info->result));
3013 /* Go on saving remaining files */
3014 info->pairs = g_list_remove_link (info->pairs, info->pairs);
3015 if (info->pairs != NULL) {
3016 save_mime_part_to_file (info);
3018 g_idle_add ((GSourceFunc) idle_save_mime_part_show_result, info);
3025 save_mime_parts_to_file_with_checks (GtkWindow *parent,
3026 SaveMimePartInfo *info)
3028 gboolean is_ok = TRUE;
3029 gint replaced_files = 0;
3030 const GList *files = info->pairs;
3031 const GList *iter, *to_replace = NULL;
3033 for (iter = files; (iter != NULL) && (replaced_files < 2); iter = g_list_next(iter)) {
3034 SaveMimePartPair *pair = iter->data;
3035 gchar *unescaped = g_uri_unescape_string (pair->filename, NULL);
3037 if (modest_utils_file_exists (unescaped)) {
3039 if (replaced_files == 1)
3044 if (replaced_files) {
3047 if (replaced_files == 1) {
3048 SaveMimePartPair *pair = to_replace->data;
3049 const gchar *basename = strrchr (pair->filename, G_DIR_SEPARATOR) + 1;
3050 gchar *escaped_basename, *message;
3052 escaped_basename = g_uri_unescape_string (basename, NULL);
3053 message = g_strdup_printf ("%s\n%s",
3054 _FM("docm_nc_replace_file"),
3055 (escaped_basename) ? escaped_basename : "");
3056 response = modest_platform_run_confirmation_dialog (parent, message);
3058 g_free (escaped_basename);
3060 response = modest_platform_run_confirmation_dialog (parent,
3061 _FM("docm_nc_replace_multiple"));
3063 if (response != GTK_RESPONSE_OK)
3068 save_mime_part_info_free (info, TRUE);
3070 g_thread_create ((GThreadFunc)save_mime_part_to_file, info, FALSE, NULL);
3075 typedef struct _SaveAttachmentsInfo {
3076 TnyList *attachments_list;
3077 ModestMsgViewWindow *window;
3078 } SaveAttachmentsInfo;
3081 save_attachments_response (GtkDialog *dialog,
3085 TnyList *mime_parts;
3087 GList *files_to_save = NULL;
3088 gchar *current_folder;
3089 SaveAttachmentsInfo *sa_info = (SaveAttachmentsInfo *) user_data;
3091 mime_parts = TNY_LIST (sa_info->attachments_list);
3093 if (arg1 != GTK_RESPONSE_OK)
3096 chooser_uri = gtk_file_chooser_get_uri (GTK_FILE_CHOOSER (dialog));
3097 current_folder = gtk_file_chooser_get_current_folder_uri (GTK_FILE_CHOOSER (dialog));
3098 if (current_folder && *current_folder != '\0') {
3100 modest_conf_set_string (modest_runtime_get_conf (), MODEST_CONF_LATEST_SAVE_ATTACHMENT_PATH,
3101 current_folder,&err);
3103 g_debug ("Error storing latest used folder: %s", err->message);
3107 g_free (current_folder);
3109 if (!modest_utils_folder_writable (chooser_uri)) {
3110 const gchar *err_msg;
3112 #ifdef MODEST_PLATFORM_MAEMO
3113 if (modest_maemo_utils_in_usb_mode ()) {
3114 err_msg = dgettext ("hildon-status-bar-usb", "usbh_ib_mmc_usb_connected");
3116 err_msg = _FM("sfil_ib_readonly_location");
3119 err_msg = _FM("sfil_ib_readonly_location");
3121 modest_platform_system_banner (NULL, NULL, err_msg);
3125 iter = tny_list_create_iterator (mime_parts);
3126 while (!tny_iterator_is_done (iter)) {
3127 TnyMimePart *mime_part = (TnyMimePart *) tny_iterator_get_current (iter);
3129 if ((modest_tny_mime_part_is_attachment_for_modest (mime_part)) &&
3130 !tny_mime_part_is_purged (mime_part) &&
3131 (tny_mime_part_get_filename (mime_part) != NULL)) {
3132 SaveMimePartPair *pair;
3134 pair = g_slice_new0 (SaveMimePartPair);
3136 if (tny_list_get_length (mime_parts) > 1) {
3138 gnome_vfs_escape_slashes (tny_mime_part_get_filename (mime_part));
3139 pair->filename = g_build_filename (chooser_uri, escaped, NULL);
3142 pair->filename = g_strdup (chooser_uri);
3144 pair->part = mime_part;
3145 files_to_save = g_list_prepend (files_to_save, pair);
3147 tny_iterator_next (iter);
3149 g_object_unref (iter);
3152 if (files_to_save != NULL) {
3153 SaveMimePartInfo *info = g_slice_new0 (SaveMimePartInfo);
3154 info->pairs = files_to_save;
3155 info->result = TRUE;
3156 info->uri = g_strdup (chooser_uri);
3157 info->window = g_object_ref (sa_info->window);
3158 save_mime_parts_to_file_with_checks ((GtkWindow *) dialog, info);
3160 g_free (chooser_uri);
3163 /* Free and close the dialog */
3164 g_object_unref (mime_parts);
3165 g_object_unref (sa_info->window);
3166 g_slice_free (SaveAttachmentsInfo, sa_info);
3167 gtk_widget_destroy (GTK_WIDGET (dialog));
3171 modest_msg_view_window_save_attachments (ModestMsgViewWindow *window,
3172 TnyList *mime_parts)
3174 ModestMsgViewWindowPrivate *priv;
3175 GtkWidget *save_dialog = NULL;
3176 gchar *conf_folder = NULL;
3177 gchar *filename = NULL;
3178 gchar *save_multiple_str = NULL;
3179 const gchar *root_folder = "file:///";
3181 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window));
3182 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
3184 if (mime_parts == NULL) {
3185 /* In Hildon 2.2 save and delete operate over all the attachments as there's no
3186 * selection available */
3187 mime_parts = modest_msg_view_get_attachments (MODEST_MSG_VIEW (priv->msg_view));
3188 if (mime_parts && !modest_maemo_utils_select_attachments (GTK_WINDOW (window), mime_parts, FALSE)) {
3189 g_object_unref (mime_parts);
3192 if (mime_parts == NULL || tny_list_get_length (mime_parts) == 0) {
3194 g_object_unref (mime_parts);
3200 g_object_ref (mime_parts);
3203 /* prepare dialog */
3204 if (tny_list_get_length (mime_parts) == 1) {
3206 /* only one attachment selected */
3207 iter = tny_list_create_iterator (mime_parts);
3208 TnyMimePart *mime_part = (TnyMimePart *) tny_iterator_get_current (iter);
3209 g_object_unref (iter);
3210 if (!modest_tny_mime_part_is_msg (mime_part) &&
3211 modest_tny_mime_part_is_attachment_for_modest (mime_part) &&
3212 !tny_mime_part_is_purged (mime_part)) {
3213 filename = g_strdup (tny_mime_part_get_filename (mime_part));
3215 /* TODO: show any error? */
3216 g_warning ("%s: Tried to save a non-file attachment", __FUNCTION__);
3217 g_object_unref (mime_parts);
3220 g_object_unref (mime_part);
3222 gint num = tny_list_get_length (mime_parts);
3223 save_multiple_str = g_strdup_printf (dngettext("hildon-fm",
3224 "sfil_va_number_of_objects_attachment",
3225 "sfil_va_number_of_objects_attachments",
3229 /****** HILDON2:START
3230 * creation of hildon file chooser dialog for saving
3232 save_dialog = hildon_file_chooser_dialog_new (GTK_WINDOW (window),
3233 GTK_FILE_CHOOSER_ACTION_SAVE);
3234 /****** HILDON2:END */
3236 /* Get last used folder */
3237 conf_folder = modest_conf_get_string (modest_runtime_get_conf (),
3238 MODEST_CONF_LATEST_SAVE_ATTACHMENT_PATH, NULL);
3240 /* File chooser stops working if we select "file:///" as current folder */
3241 if (conf_folder && g_ascii_strcasecmp (root_folder, conf_folder) != 0) {
3242 g_free (conf_folder);
3246 if (conf_folder && conf_folder[0] != '\0') {
3247 gtk_file_chooser_set_current_folder_uri (GTK_FILE_CHOOSER (save_dialog), conf_folder);
3250 /* Set the default folder to documents folder */
3251 docs_folder = (gchar *) g_strdup(g_get_user_special_dir (G_USER_DIRECTORY_DOCUMENTS));
3254 docs_folder = g_build_filename (g_getenv (MYDOCS_ENV), DOCS_FOLDER, NULL);
3256 gtk_file_chooser_set_current_folder (GTK_FILE_CHOOSER (save_dialog), docs_folder);
3257 g_free (docs_folder);
3259 g_free (conf_folder);
3263 gtk_file_chooser_set_current_name (GTK_FILE_CHOOSER (save_dialog),
3268 /* if multiple, set multiple string */
3269 if (save_multiple_str) {
3270 g_object_set (G_OBJECT (save_dialog), "save-multiple", save_multiple_str, NULL);
3271 gtk_window_set_title (GTK_WINDOW (save_dialog), _FM("sfil_ti_save_objects_files"));
3272 g_free (save_multiple_str);
3275 /* We must run this asynchronously, because the hildon dialog
3276 performs a gtk_dialog_run by itself which leads to gdk
3278 SaveAttachmentsInfo *sa_info;
3279 sa_info = g_slice_new (SaveAttachmentsInfo);
3280 sa_info->attachments_list = mime_parts;
3281 sa_info->window = g_object_ref (window);
3282 g_signal_connect (save_dialog, "response",
3283 G_CALLBACK (save_attachments_response), sa_info);
3285 gtk_widget_show_all (save_dialog);
3289 show_remove_attachment_information (gpointer userdata)
3291 ModestMsgViewWindow *window = (ModestMsgViewWindow *) userdata;
3292 ModestMsgViewWindowPrivate *priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
3294 /* We're outside the main lock */
3295 gdk_threads_enter ();
3297 if (priv->remove_attachment_banner != NULL) {
3298 gtk_widget_destroy (priv->remove_attachment_banner);
3299 g_object_unref (priv->remove_attachment_banner);
3302 priv->remove_attachment_banner = g_object_ref (
3303 modest_platform_animation_banner (NULL, NULL, _("mcen_me_inbox_remove_attachments")));
3305 gdk_threads_leave ();
3311 modest_msg_view_window_remove_attachments (ModestMsgViewWindow *window, gboolean get_all)
3313 ModestMsgViewWindowPrivate *priv;
3314 TnyList *mime_parts = NULL, *tmp;
3315 gchar *confirmation_message;
3321 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window));
3322 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
3324 /* In hildon 2.2 we ignore the get_all flag as we always get all attachments. This is
3325 * because we don't have selection
3327 mime_parts = modest_msg_view_get_attachments (MODEST_MSG_VIEW (priv->msg_view));
3329 /* Remove already purged messages from mime parts list. We use
3330 a copy of the list to remove items in the original one */
3331 tmp = tny_list_copy (mime_parts);
3332 iter = tny_list_create_iterator (tmp);
3333 while (!tny_iterator_is_done (iter)) {
3334 TnyMimePart *part = TNY_MIME_PART (tny_iterator_get_current (iter));
3335 if (tny_mime_part_is_purged (part))
3336 tny_list_remove (mime_parts, (GObject *) part);
3338 g_object_unref (part);
3339 tny_iterator_next (iter);
3341 g_object_unref (tmp);
3342 g_object_unref (iter);
3344 if (!modest_maemo_utils_select_attachments (GTK_WINDOW (window), mime_parts, TRUE) ||
3345 tny_list_get_length (mime_parts) == 0) {
3346 g_object_unref (mime_parts);
3350 n_attachments = tny_list_get_length (mime_parts);
3351 if (n_attachments == 1) {
3355 iter = tny_list_create_iterator (mime_parts);
3356 part = (TnyMimePart *) tny_iterator_get_current (iter);
3357 g_object_unref (iter);
3358 if (modest_tny_mime_part_is_msg (part)) {
3360 header = tny_msg_get_header (TNY_MSG (part));
3361 filename = tny_header_dup_subject (header);
3362 g_object_unref (header);
3363 if (filename == NULL)
3364 filename = g_strdup (_("mail_va_no_subject"));
3366 filename = g_strdup (tny_mime_part_get_filename (TNY_MIME_PART (part)));
3368 confirmation_message = g_strdup_printf (_("mcen_nc_purge_file_text"), filename);
3370 g_object_unref (part);
3372 confirmation_message = g_strdup_printf (ngettext("mcen_nc_purge_file_text",
3373 "mcen_nc_purge_files_text",
3374 n_attachments), n_attachments);
3376 response = modest_platform_run_confirmation_dialog (GTK_WINDOW (window),
3377 confirmation_message);
3378 g_free (confirmation_message);
3380 if (response != GTK_RESPONSE_OK) {
3381 g_object_unref (mime_parts);
3385 priv->purge_timeout = g_timeout_add (2000, show_remove_attachment_information, window);
3387 iter = tny_list_create_iterator (mime_parts);
3388 while (!tny_iterator_is_done (iter)) {
3391 part = (TnyMimePart *) tny_iterator_get_current (iter);
3392 tny_mime_part_set_purged (TNY_MIME_PART (part));
3393 g_object_unref (part);
3394 tny_iterator_next (iter);
3396 g_object_unref (iter);
3398 msg = tny_msg_view_get_msg (TNY_MSG_VIEW (priv->msg_view));
3399 tny_msg_view_clear (TNY_MSG_VIEW (priv->msg_view));
3400 tny_msg_rewrite_cache (msg);
3401 tny_msg_view_set_msg (TNY_MSG_VIEW (priv->msg_view), msg);
3402 g_object_unref (msg);
3403 update_branding (MODEST_MSG_VIEW_WINDOW (window));
3405 g_object_unref (mime_parts);
3407 if (priv->purge_timeout > 0) {
3408 g_source_remove (priv->purge_timeout);
3409 priv->purge_timeout = 0;
3412 if (priv->remove_attachment_banner) {
3413 gtk_widget_destroy (priv->remove_attachment_banner);
3414 g_object_unref (priv->remove_attachment_banner);
3415 priv->remove_attachment_banner = NULL;
3421 update_window_title (ModestMsgViewWindow *window)
3423 ModestMsgViewWindowPrivate *priv;
3425 TnyHeader *header = NULL;
3426 gchar *subject = NULL;
3428 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
3430 /* Note that if the window is closed while we're retrieving
3431 the message, this widget could de deleted */
3432 if (!priv->msg_view)
3435 msg = tny_msg_view_get_msg (TNY_MSG_VIEW (priv->msg_view));
3437 if (priv->other_body) {
3440 description = modest_tny_mime_part_get_header_value (priv->other_body, "Content-Description");
3442 g_strstrip (description);
3443 subject = description;
3445 } else if (msg != NULL) {
3446 header = tny_msg_get_header (msg);
3447 subject = tny_header_dup_subject (header);
3448 g_object_unref (header);
3449 g_object_unref (msg);
3452 if ((subject == NULL)||(subject[0] == '\0')) {
3454 subject = g_strdup (_("mail_va_no_subject"));
3457 gtk_window_set_title (GTK_WINDOW (window), subject);
3462 on_move_focus (GtkWidget *widget,
3463 GtkDirectionType direction,
3466 g_signal_stop_emission_by_name (G_OBJECT (widget), "move-focus");
3470 fetch_image_open_stream (TnyStreamCache *self, gint64 *expected_size, gchar *uri)
3472 GnomeVFSResult result;
3473 GnomeVFSHandle *handle = NULL;
3474 GnomeVFSFileInfo *info = NULL;
3477 result = gnome_vfs_open (&handle, uri, GNOME_VFS_OPEN_READ);
3478 if (result != GNOME_VFS_OK) {
3483 info = gnome_vfs_file_info_new ();
3484 result = gnome_vfs_get_file_info_from_handle (handle, info, GNOME_VFS_FILE_INFO_DEFAULT);
3485 if (result != GNOME_VFS_OK || ! (info->valid_fields & GNOME_VFS_FILE_INFO_FIELDS_SIZE)) {
3486 /* We put a "safe" default size for going to cache */
3487 *expected_size = (300*1024);
3489 *expected_size = info->size;
3491 gnome_vfs_file_info_unref (info);
3493 stream = tny_vfs_stream_new (handle);
3502 TnyStream *output_stream;
3503 GtkWidget *msg_view;
3508 on_fetch_image_idle_refresh_view (gpointer userdata)
3511 FetchImageData *fidata = (FetchImageData *) userdata;
3513 gdk_threads_enter ();
3514 if (GTK_WIDGET_DRAWABLE (fidata->msg_view)) {
3515 ModestMsgViewWindowPrivate *priv;
3517 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (fidata->window);
3518 priv->fetching_images--;
3519 gtk_widget_queue_draw (fidata->msg_view);
3520 update_progress_hint (MODEST_MSG_VIEW_WINDOW (fidata->window));
3522 gdk_threads_leave ();
3524 g_object_unref (fidata->msg_view);
3525 g_object_unref (fidata->window);
3526 g_slice_free (FetchImageData, fidata);
3531 on_fetch_image_thread (gpointer userdata)
3533 FetchImageData *fidata = (FetchImageData *) userdata;
3534 TnyStreamCache *cache;
3535 TnyStream *cache_stream;
3537 cache = modest_runtime_get_images_cache ();
3539 tny_stream_cache_get_stream (cache,
3541 (TnyStreamCacheOpenStreamFetcher) fetch_image_open_stream,
3542 (gpointer) fidata->uri);
3543 g_free (fidata->cache_id);
3544 g_free (fidata->uri);
3546 if (cache_stream != NULL) {
3549 while (G_LIKELY (!tny_stream_is_eos (cache_stream))) {
3552 nb_read = tny_stream_read (cache_stream, buffer, sizeof (buffer));
3553 if (G_UNLIKELY (nb_read < 0)) {
3555 } else if (G_LIKELY (nb_read > 0)) {
3556 gssize nb_written = 0;
3558 while (G_UNLIKELY (nb_written < nb_read)) {
3561 len = tny_stream_write (fidata->output_stream, buffer + nb_written,
3562 nb_read - nb_written);
3563 if (G_UNLIKELY (len < 0))
3569 tny_stream_close (cache_stream);
3570 g_object_unref (cache_stream);
3573 tny_stream_close (fidata->output_stream);
3574 g_object_unref (fidata->output_stream);
3576 g_idle_add (on_fetch_image_idle_refresh_view, fidata);
3582 on_fetch_image (ModestMsgView *msgview,
3585 ModestMsgViewWindow *window)
3587 const gchar *current_account;
3588 ModestMsgViewWindowPrivate *priv;
3589 FetchImageData *fidata;
3591 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
3593 current_account = modest_window_get_active_account (MODEST_WINDOW (window));
3595 fidata = g_slice_new0 (FetchImageData);
3596 fidata->msg_view = g_object_ref (msgview);
3597 fidata->window = g_object_ref (window);
3598 fidata->uri = g_strdup (uri);
3599 fidata->cache_id = modest_images_cache_get_id (current_account, uri);
3600 fidata->output_stream = g_object_ref (stream);
3602 priv->fetching_images++;
3603 if (g_thread_create (on_fetch_image_thread, fidata, FALSE, NULL) == NULL) {
3604 g_object_unref (fidata->output_stream);
3605 g_free (fidata->cache_id);
3606 g_free (fidata->uri);
3607 g_object_unref (fidata->msg_view);
3608 g_slice_free (FetchImageData, fidata);
3609 tny_stream_close (stream);
3610 priv->fetching_images--;
3611 update_progress_hint (window);
3614 update_progress_hint (window);
3620 setup_menu (ModestMsgViewWindow *self)
3622 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW(self));
3624 /****** HILDON2:START
3625 * add actions to hildon window menu
3628 /* Settings menu buttons */
3629 modest_hildon2_window_add_to_menu (MODEST_HILDON2_WINDOW (self), _("mcen_me_viewer_find"), NULL,
3630 APP_MENU_CALLBACK (modest_msg_view_window_show_find_toolbar),
3631 MODEST_DIMMING_CALLBACK (modest_ui_dimming_rules_on_find_in_msg));
3633 modest_hildon2_window_add_to_menu (MODEST_HILDON2_WINDOW (self),
3634 dngettext(GETTEXT_PACKAGE,
3635 "mcen_me_move_message",
3636 "mcen_me_move_messages",
3639 APP_MENU_CALLBACK (modest_ui_actions_on_move_to),
3640 MODEST_DIMMING_CALLBACK (modest_ui_dimming_rules_on_move_to));
3642 modest_hildon2_window_add_to_menu (MODEST_HILDON2_WINDOW (self), _("mcen_me_inbox_mark_as_read"), NULL,
3643 APP_MENU_CALLBACK (modest_ui_actions_on_mark_as_read),
3644 MODEST_DIMMING_CALLBACK (modest_ui_dimming_rules_on_mark_as_read_msg_in_view));
3646 modest_hildon2_window_add_to_menu (MODEST_HILDON2_WINDOW (self), _("mcen_me_inbox_mark_as_unread"), NULL,
3647 APP_MENU_CALLBACK (modest_ui_actions_on_mark_as_unread),
3648 MODEST_DIMMING_CALLBACK (modest_ui_dimming_rules_on_mark_as_unread_msg_in_view));
3650 modest_hildon2_window_add_to_menu (MODEST_HILDON2_WINDOW (self), _("mcen_me_viewer_save_attachments"), NULL,
3651 APP_MENU_CALLBACK (modest_ui_actions_save_attachments),
3652 MODEST_DIMMING_CALLBACK (modest_ui_dimming_rules_on_save_attachments));
3653 modest_hildon2_window_add_to_menu (MODEST_HILDON2_WINDOW (self), _("mcen_me_inbox_remove_attachments"), NULL,
3654 APP_MENU_CALLBACK (modest_ui_actions_remove_attachments),
3655 MODEST_DIMMING_CALLBACK (modest_ui_dimming_rules_on_remove_attachments));
3657 modest_hildon2_window_add_to_menu (MODEST_HILDON2_WINDOW (self), _("mcen_me_new_message"), "<Control>n",
3658 APP_MENU_CALLBACK (modest_ui_actions_on_new_msg),
3659 MODEST_DIMMING_CALLBACK (modest_ui_dimming_rules_on_new_msg));
3660 modest_hildon2_window_add_to_menu (MODEST_HILDON2_WINDOW (self), _("mcen_me_viewer_addtocontacts"), NULL,
3661 APP_MENU_CALLBACK (modest_ui_actions_add_to_contacts),
3662 MODEST_DIMMING_CALLBACK (modest_ui_dimming_rules_on_add_to_contacts));
3664 modest_hildon2_window_add_to_menu (MODEST_HILDON2_WINDOW (self), _("mcen_ti_message_properties"), NULL,
3665 APP_MENU_CALLBACK (modest_ui_actions_on_details),
3666 MODEST_DIMMING_CALLBACK (modest_ui_dimming_rules_on_details));
3668 /****** HILDON2:END */
3672 modest_msg_view_window_add_to_contacts (ModestMsgViewWindow *self)
3674 ModestMsgViewWindowPrivate *priv;
3675 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
3676 GSList *recipients = NULL;
3678 gboolean contacts_to_add = FALSE;
3680 msg = tny_msg_view_get_msg (TNY_MSG_VIEW (priv->msg_view));
3684 header = modest_msg_view_window_get_header (self);
3687 recipients = modest_tny_msg_header_get_all_recipients_list (header);
3688 g_object_unref (header);
3690 recipients = modest_tny_msg_get_all_recipients_list (msg);
3691 g_object_unref (msg);
3694 if (recipients != NULL) {
3695 /****** HILDON2:START
3696 * shows dialog with addresses not present in addressbook. User can choose one to
3697 * add it to addressbook.
3699 GtkWidget *picker_dialog;
3700 GtkWidget *selector;
3702 gchar *selected = NULL;
3704 selector = hildon_touch_selector_new_text ();
3705 g_object_ref (selector);
3707 for (node = recipients; node != NULL; node = g_slist_next (node)) {
3708 if (!modest_address_book_has_address ((const gchar *) node->data)) {
3709 hildon_touch_selector_append_text (HILDON_TOUCH_SELECTOR (selector),
3710 (const gchar *) node->data);
3711 contacts_to_add = TRUE;
3715 if (contacts_to_add) {
3718 picker_dialog = hildon_picker_dialog_new (GTK_WINDOW (self));
3719 gtk_window_set_title (GTK_WINDOW (picker_dialog), _("mcen_me_viewer_addtocontacts"));
3721 hildon_picker_dialog_set_selector (HILDON_PICKER_DIALOG (picker_dialog),
3722 HILDON_TOUCH_SELECTOR (selector));
3724 picker_result = gtk_dialog_run (GTK_DIALOG (picker_dialog));
3726 if (picker_result == GTK_RESPONSE_OK) {
3727 selected = hildon_touch_selector_get_current_text (HILDON_TOUCH_SELECTOR (selector));
3729 gtk_widget_destroy (picker_dialog);
3732 modest_address_book_add_address (selected, (GtkWindow *) self);
3737 g_object_unref (selector);
3740 /****** HILDON2:END */
3743 if (recipients) {g_slist_foreach (recipients, (GFunc) g_free, NULL); g_slist_free (recipients);}
3747 _modest_msg_view_window_map_event (GtkWidget *widget,
3751 ModestMsgViewWindow *self = (ModestMsgViewWindow *) userdata;
3753 update_progress_hint (self);
3759 modest_msg_view_window_fetch_images (ModestMsgViewWindow *self)
3761 ModestMsgViewWindowPrivate *priv;
3762 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
3764 modest_msg_view_request_fetch_images (MODEST_MSG_VIEW (priv->msg_view));
3768 modest_msg_view_window_has_blocked_external_images (ModestMsgViewWindow *self)
3770 ModestMsgViewWindowPrivate *priv;
3771 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
3773 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self), FALSE);
3775 return modest_msg_view_has_blocked_external_images (MODEST_MSG_VIEW (priv->msg_view));
3779 modest_msg_view_window_reload (ModestMsgViewWindow *self)
3781 ModestMsgViewWindowPrivate *priv;
3782 const gchar *msg_uid;
3783 TnyHeader *header = NULL;
3784 TnyFolder *folder = NULL;
3786 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self));
3788 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
3790 header = modest_msg_view_window_get_header (MODEST_MSG_VIEW_WINDOW (self));
3794 folder = tny_header_get_folder (header);
3795 g_object_unref (header);
3800 msg_uid = modest_msg_view_window_get_message_uid (self);
3802 GtkTreeRowReference *row_reference;
3804 if (priv->row_reference && gtk_tree_row_reference_valid (priv->row_reference)) {
3805 row_reference = priv->row_reference;
3807 row_reference = NULL;
3809 if (!message_reader (self, priv, NULL, msg_uid, folder, row_reference))
3810 g_warning ("Shouldn't happen, trying to reload a message failed");
3813 g_object_unref (folder);
3817 update_branding (ModestMsgViewWindow *self)
3819 const gchar *account;
3820 const gchar *mailbox;
3821 ModestAccountMgr *mgr;
3822 ModestProtocol *protocol = NULL;
3823 gchar *service_name = NULL;
3824 const GdkPixbuf *service_icon = NULL;
3825 ModestMsgViewWindowPrivate *priv;
3827 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
3829 account = modest_window_get_active_account (MODEST_WINDOW (self));
3830 mailbox = modest_window_get_active_mailbox (MODEST_WINDOW (self));
3832 mgr = modest_runtime_get_account_mgr ();
3834 if (modest_account_mgr_account_is_multimailbox (mgr, account, &protocol)) {
3835 if (MODEST_IS_ACCOUNT_PROTOCOL (protocol)) {
3836 service_name = modest_account_protocol_get_service_name (MODEST_ACCOUNT_PROTOCOL (protocol),
3838 service_icon = modest_account_protocol_get_service_icon (MODEST_ACCOUNT_PROTOCOL (protocol),
3839 account, mailbox, MODEST_ICON_SIZE_SMALL);
3843 modest_msg_view_set_branding (MODEST_MSG_VIEW (priv->msg_view), service_name, service_icon);
3844 g_free (service_name);
3848 sync_flags (ModestMsgViewWindow *self)
3850 TnyHeader *header = NULL;
3852 header = modest_msg_view_window_get_header (self);
3854 TnyMsg *msg = modest_msg_view_window_get_message (self);
3856 header = tny_msg_get_header (msg);
3857 g_object_unref (msg);
3862 TnyFolder *folder = tny_header_get_folder (header);
3865 ModestMailOperation *mail_op;
3867 /* Sync folder, we need this to save the seen flag */
3868 mail_op = modest_mail_operation_new (NULL);
3869 modest_mail_operation_queue_add (modest_runtime_get_mail_operation_queue (),
3871 modest_mail_operation_sync_folder (mail_op, folder, FALSE, NULL, NULL);
3872 g_object_unref (mail_op);
3873 g_object_unref (folder);
3875 g_object_unref (header);