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-main-window-ui.h>
44 #include "modest-msg-view-window-ui-dimming.h"
45 #include <modest-widget-memory.h>
46 #include <modest-progress-object.h>
47 #include <modest-runtime.h>
48 #include <modest-window-priv.h>
49 #include <modest-tny-folder.h>
50 #include <modest-text-utils.h>
51 #include <modest-account-mgr-helpers.h>
52 #include <hildon/hildon-pannable-area.h>
53 #include <hildon/hildon-picker-dialog.h>
54 #include <hildon/hildon-app-menu.h>
55 #include "modest-defs.h"
56 #include "modest-hildon-includes.h"
57 #include "modest-ui-dimming-manager.h"
58 #include <gdk/gdkkeysyms.h>
59 #include <modest-tny-account.h>
60 #include <modest-mime-part-view.h>
61 #include <modest-isearch-view.h>
62 #include <modest-tny-mime-part.h>
63 #include <modest-address-book.h>
66 #include <glib/gstdio.h>
67 #include <modest-debug.h>
68 #include <modest-header-window.h>
69 #include <modest-account-protocol.h>
70 #include <tny-camel-msg.h>
72 #define MYDOCS_ENV "MYDOCSDIR"
73 #define DOCS_FOLDER ".documents"
75 typedef struct _ModestMsgViewWindowPrivate ModestMsgViewWindowPrivate;
76 struct _ModestMsgViewWindowPrivate {
79 GtkWidget *main_scroll;
80 GtkWidget *find_toolbar;
83 /* Progress observers */
84 GSList *progress_widgets;
87 GtkWidget *prev_toolitem;
88 GtkWidget *next_toolitem;
89 gboolean progress_hint;
92 /* Optimized view enabled */
93 gboolean optimized_view;
95 /* Whether this was created via the *_new_for_search_result() function. */
96 gboolean is_search_result;
98 /* Whether the message is in outbox */
101 /* A reference to the @model of the header view
102 * to allow selecting previous/next messages,
103 * if the message is currently selected in the header view.
105 const gchar *header_folder_id;
106 GtkTreeModel *header_model;
107 GtkTreeRowReference *row_reference;
108 GtkTreeRowReference *next_row_reference;
110 gulong clipboard_change_handler;
111 gulong queue_change_handler;
112 gulong account_removed_handler;
113 gulong row_changed_handler;
114 gulong row_deleted_handler;
115 gulong row_inserted_handler;
116 gulong rows_reordered_handler;
119 GtkWidget *remove_attachment_banner;
122 TnyMimePart *other_body;
127 static void modest_msg_view_window_class_init (ModestMsgViewWindowClass *klass);
128 static void modest_msg_view_window_init (ModestMsgViewWindow *obj);
129 static void modest_header_view_observer_init (ModestHeaderViewObserverIface *iface_class);
130 static void modest_msg_view_window_finalize (GObject *obj);
131 static void modest_msg_view_window_show_find_toolbar (GtkWidget *obj, gpointer data);
132 static void modest_msg_view_window_find_toolbar_close (GtkWidget *widget,
133 ModestMsgViewWindow *obj);
134 static void modest_msg_view_window_find_toolbar_search (GtkWidget *widget,
135 ModestMsgViewWindow *obj);
136 static void modest_msg_view_window_toggle_find_toolbar (GtkWidget *obj,
138 static void modest_msg_view_window_disconnect_signals (ModestWindow *self);
140 static gdouble modest_msg_view_window_get_zoom (ModestWindow *window);
141 static void modest_msg_view_window_set_zoom (ModestWindow *window,
143 static gboolean modest_msg_view_window_zoom_minus (ModestWindow *window);
144 static gboolean modest_msg_view_window_zoom_plus (ModestWindow *window);
145 static gboolean modest_msg_view_window_key_event (GtkWidget *window,
148 static void modest_msg_view_window_update_priority (ModestMsgViewWindow *window);
150 static void modest_msg_view_window_show_toolbar (ModestWindow *window,
151 gboolean show_toolbar);
153 static void modest_msg_view_window_clipboard_owner_change (GtkClipboard *clipboard,
155 ModestMsgViewWindow *window);
157 static void modest_msg_view_window_on_row_changed (GtkTreeModel *header_model,
160 ModestMsgViewWindow *window);
162 static void modest_msg_view_window_on_row_deleted (GtkTreeModel *header_model,
164 ModestMsgViewWindow *window);
166 static void modest_msg_view_window_on_row_inserted (GtkTreeModel *header_model,
167 GtkTreePath *tree_path,
168 GtkTreeIter *tree_iter,
169 ModestMsgViewWindow *window);
171 static void modest_msg_view_window_on_row_reordered (GtkTreeModel *header_model,
175 ModestMsgViewWindow *window);
177 static void modest_msg_view_window_update_model_replaced (ModestHeaderViewObserver *window,
179 const gchar *tny_folder_id);
181 static void on_queue_changed (ModestMailOperationQueue *queue,
182 ModestMailOperation *mail_op,
183 ModestMailOperationQueueNotification type,
184 ModestMsgViewWindow *self);
186 static void on_account_removed (TnyAccountStore *account_store,
190 static void on_move_focus (GtkWidget *widget,
191 GtkDirectionType direction,
194 static void view_msg_cb (ModestMailOperation *mail_op,
201 static void set_progress_hint (ModestMsgViewWindow *self,
204 static void update_window_title (ModestMsgViewWindow *window);
206 static void init_window (ModestMsgViewWindow *obj);
208 static gboolean msg_is_visible (TnyHeader *header, gboolean check_outbox);
210 static void check_dimming_rules_after_change (ModestMsgViewWindow *window);
212 static gboolean on_fetch_image (ModestMsgView *msgview,
215 ModestMsgViewWindow *window);
217 static gboolean modest_msg_view_window_scroll_child (ModestMsgViewWindow *self,
218 GtkScrollType scroll_type,
221 static gboolean message_reader (ModestMsgViewWindow *window,
222 ModestMsgViewWindowPrivate *priv,
224 const gchar *msg_uid,
226 GtkTreeRowReference *row_reference);
228 static void setup_menu (ModestMsgViewWindow *self);
229 static gboolean _modest_msg_view_window_map_event (GtkWidget *widget,
232 static void update_branding (ModestMsgViewWindow *self);
234 /* list my signals */
241 static const GtkToggleActionEntry msg_view_toggle_action_entries [] = {
242 { "FindInMessage", MODEST_TOOLBAR_ICON_FIND, N_("qgn_toolb_gene_find"), "<CTRL>F", NULL, G_CALLBACK (modest_msg_view_window_toggle_find_toolbar), FALSE },
245 #define MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE((o), \
246 MODEST_TYPE_MSG_VIEW_WINDOW, \
247 ModestMsgViewWindowPrivate))
249 static GtkWindowClass *parent_class = NULL;
251 /* uncomment the following if you have defined any signals */
252 static guint signals[LAST_SIGNAL] = {0};
255 modest_msg_view_window_get_type (void)
257 static GType my_type = 0;
259 static const GTypeInfo my_info = {
260 sizeof(ModestMsgViewWindowClass),
261 NULL, /* base init */
262 NULL, /* base finalize */
263 (GClassInitFunc) modest_msg_view_window_class_init,
264 NULL, /* class finalize */
265 NULL, /* class data */
266 sizeof(ModestMsgViewWindow),
268 (GInstanceInitFunc) modest_msg_view_window_init,
271 my_type = g_type_register_static (MODEST_TYPE_HILDON2_WINDOW,
272 "ModestMsgViewWindow",
275 static const GInterfaceInfo modest_header_view_observer_info =
277 (GInterfaceInitFunc) modest_header_view_observer_init,
278 NULL, /* interface_finalize */
279 NULL /* interface_data */
282 g_type_add_interface_static (my_type,
283 MODEST_TYPE_HEADER_VIEW_OBSERVER,
284 &modest_header_view_observer_info);
290 save_state (ModestWindow *self)
292 modest_widget_memory_save (modest_runtime_get_conf (),
294 MODEST_CONF_MSG_VIEW_WINDOW_KEY);
298 modest_msg_view_window_scroll_child (ModestMsgViewWindow *self,
299 GtkScrollType scroll_type,
303 ModestMsgViewWindowPrivate *priv;
306 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(self);
308 switch (scroll_type) {
309 case GTK_SCROLL_STEP_UP:
312 case GTK_SCROLL_STEP_DOWN:
315 case GTK_SCROLL_PAGE_UP:
318 case GTK_SCROLL_PAGE_DOWN:
321 case GTK_SCROLL_START:
332 modest_maemo_utils_scroll_pannable((HildonPannableArea *) priv->main_scroll, 0, step);
334 return (gboolean) step;
338 add_scroll_binding (GtkBindingSet *binding_set,
340 GtkScrollType scroll)
342 guint keypad_keyval = keyval - GDK_Left + GDK_KP_Left;
344 gtk_binding_entry_add_signal (binding_set, keyval, 0,
346 GTK_TYPE_SCROLL_TYPE, scroll,
347 G_TYPE_BOOLEAN, FALSE);
348 gtk_binding_entry_add_signal (binding_set, keypad_keyval, 0,
350 GTK_TYPE_SCROLL_TYPE, scroll,
351 G_TYPE_BOOLEAN, FALSE);
355 modest_msg_view_window_class_init (ModestMsgViewWindowClass *klass)
357 GObjectClass *gobject_class;
358 HildonWindowClass *hildon_window_class;
359 ModestWindowClass *modest_window_class;
360 GtkBindingSet *binding_set;
362 gobject_class = (GObjectClass*) klass;
363 hildon_window_class = (HildonWindowClass *) klass;
364 modest_window_class = (ModestWindowClass *) klass;
366 parent_class = g_type_class_peek_parent (klass);
367 gobject_class->finalize = modest_msg_view_window_finalize;
369 modest_window_class->set_zoom_func = modest_msg_view_window_set_zoom;
370 modest_window_class->get_zoom_func = modest_msg_view_window_get_zoom;
371 modest_window_class->zoom_plus_func = modest_msg_view_window_zoom_plus;
372 modest_window_class->zoom_minus_func = modest_msg_view_window_zoom_minus;
373 modest_window_class->show_toolbar_func = modest_msg_view_window_show_toolbar;
374 modest_window_class->disconnect_signals_func = modest_msg_view_window_disconnect_signals;
376 modest_window_class->save_state_func = save_state;
378 klass->scroll_child = modest_msg_view_window_scroll_child;
380 signals[MSG_CHANGED_SIGNAL] =
381 g_signal_new ("msg-changed",
382 G_TYPE_FROM_CLASS (gobject_class),
384 G_STRUCT_OFFSET (ModestMsgViewWindowClass, msg_changed),
386 modest_marshal_VOID__POINTER_POINTER,
387 G_TYPE_NONE, 2, G_TYPE_POINTER, G_TYPE_POINTER);
389 signals[SCROLL_CHILD_SIGNAL] =
390 g_signal_new ("scroll-child",
391 G_TYPE_FROM_CLASS (gobject_class),
392 G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
393 G_STRUCT_OFFSET (ModestMsgViewWindowClass, scroll_child),
395 modest_marshal_BOOLEAN__ENUM_BOOLEAN,
396 G_TYPE_BOOLEAN, 2, GTK_TYPE_SCROLL_TYPE, G_TYPE_BOOLEAN);
398 binding_set = gtk_binding_set_by_class (klass);
399 add_scroll_binding (binding_set, GDK_Up, GTK_SCROLL_STEP_UP);
400 add_scroll_binding (binding_set, GDK_Down, GTK_SCROLL_STEP_DOWN);
401 add_scroll_binding (binding_set, GDK_Page_Up, GTK_SCROLL_PAGE_UP);
402 add_scroll_binding (binding_set, GDK_Page_Down, GTK_SCROLL_PAGE_DOWN);
403 add_scroll_binding (binding_set, GDK_Home, GTK_SCROLL_START);
404 add_scroll_binding (binding_set, GDK_End, GTK_SCROLL_END);
406 g_type_class_add_private (gobject_class, sizeof(ModestMsgViewWindowPrivate));
410 static void modest_header_view_observer_init(
411 ModestHeaderViewObserverIface *iface_class)
413 iface_class->update_func = modest_msg_view_window_update_model_replaced;
417 modest_msg_view_window_init (ModestMsgViewWindow *obj)
419 ModestMsgViewWindowPrivate *priv;
420 ModestWindowPrivate *parent_priv = NULL;
421 GtkActionGroup *action_group = NULL;
422 GError *error = NULL;
424 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(obj);
425 parent_priv = MODEST_WINDOW_GET_PRIVATE(obj);
426 parent_priv->ui_manager = gtk_ui_manager_new();
428 action_group = gtk_action_group_new ("ModestMsgViewWindowActions");
429 gtk_action_group_set_translation_domain (action_group, GETTEXT_PACKAGE);
431 /* Add common actions */
432 gtk_action_group_add_actions (action_group,
433 modest_action_entries,
434 G_N_ELEMENTS (modest_action_entries),
436 gtk_action_group_add_toggle_actions (action_group,
437 msg_view_toggle_action_entries,
438 G_N_ELEMENTS (msg_view_toggle_action_entries),
441 gtk_ui_manager_insert_action_group (parent_priv->ui_manager, action_group, 0);
442 g_object_unref (action_group);
444 /* Load the UI definition */
445 gtk_ui_manager_add_ui_from_file (parent_priv->ui_manager, MODEST_UIDIR "modest-msg-view-window-ui.xml",
448 g_printerr ("modest: could not merge modest-msg-view-window-ui.xml: %s\n", error->message);
449 g_error_free (error);
454 /* Add accelerators */
455 gtk_window_add_accel_group (GTK_WINDOW (obj),
456 gtk_ui_manager_get_accel_group (parent_priv->ui_manager));
458 priv->is_search_result = FALSE;
459 priv->is_outbox = FALSE;
461 priv->msg_view = NULL;
462 priv->header_model = NULL;
463 priv->header_folder_id = NULL;
464 priv->clipboard_change_handler = 0;
465 priv->queue_change_handler = 0;
466 priv->account_removed_handler = 0;
467 priv->row_changed_handler = 0;
468 priv->row_deleted_handler = 0;
469 priv->row_inserted_handler = 0;
470 priv->rows_reordered_handler = 0;
471 priv->progress_hint = FALSE;
472 priv->fetching_images = 0;
474 priv->optimized_view = FALSE;
475 priv->purge_timeout = 0;
476 priv->remove_attachment_banner = NULL;
477 priv->msg_uid = NULL;
478 priv->other_body = NULL;
480 priv->sighandlers = NULL;
483 init_window (MODEST_MSG_VIEW_WINDOW(obj));
485 hildon_program_add_window (hildon_program_get_instance(),
491 update_progress_hint (ModestMsgViewWindow *self)
493 ModestMsgViewWindowPrivate *priv;
494 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(self);
496 if (GTK_WIDGET_VISIBLE (self)) {
497 hildon_gtk_window_set_progress_indicator (GTK_WINDOW (self),
498 (priv->progress_hint || (priv->fetching_images > 0))?1:0);
503 set_progress_hint (ModestMsgViewWindow *self,
506 ModestWindowPrivate *parent_priv;
507 ModestMsgViewWindowPrivate *priv;
509 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self));
511 parent_priv = MODEST_WINDOW_GET_PRIVATE(self);
512 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(self);
514 /* Sets current progress hint */
515 priv->progress_hint = enabled;
517 update_progress_hint (self);
523 init_window (ModestMsgViewWindow *obj)
525 GtkWidget *main_vbox;
526 ModestMsgViewWindowPrivate *priv;
528 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(obj);
530 priv->msg_view = GTK_WIDGET (tny_platform_factory_new_msg_view (modest_tny_platform_factory_get_instance ()));
531 modest_msg_view_set_shadow_type (MODEST_MSG_VIEW (priv->msg_view), GTK_SHADOW_NONE);
532 main_vbox = gtk_vbox_new (FALSE, 6);
533 priv->main_scroll = hildon_pannable_area_new ();
534 g_object_set (G_OBJECT (priv->main_scroll),
535 "mov-mode", HILDON_MOVEMENT_MODE_BOTH,
538 gtk_container_add (GTK_CONTAINER (priv->main_scroll), priv->msg_view);
539 gtk_box_pack_start (GTK_BOX(main_vbox), priv->main_scroll, TRUE, TRUE, 0);
540 gtk_container_add (GTK_CONTAINER(obj), main_vbox);
542 /* NULL-ize fields if the window is destroyed */
543 g_signal_connect (priv->msg_view, "destroy", G_CALLBACK (gtk_widget_destroyed), &(priv->msg_view));
545 gtk_widget_show_all (GTK_WIDGET(main_vbox));
549 modest_msg_view_window_disconnect_signals (ModestWindow *self)
551 ModestMsgViewWindowPrivate *priv;
552 GtkWidget *header_view = NULL;
553 GtkWindow *parent_window = NULL;
555 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
557 if (gtk_clipboard_get (GDK_SELECTION_PRIMARY) &&
558 g_signal_handler_is_connected (gtk_clipboard_get (GDK_SELECTION_PRIMARY),
559 priv->clipboard_change_handler))
560 g_signal_handler_disconnect (gtk_clipboard_get (GDK_SELECTION_PRIMARY),
561 priv->clipboard_change_handler);
563 if (g_signal_handler_is_connected (G_OBJECT (modest_runtime_get_mail_operation_queue ()),
564 priv->queue_change_handler))
565 g_signal_handler_disconnect (G_OBJECT (modest_runtime_get_mail_operation_queue ()),
566 priv->queue_change_handler);
568 if (g_signal_handler_is_connected (G_OBJECT (modest_runtime_get_account_store ()),
569 priv->account_removed_handler))
570 g_signal_handler_disconnect (G_OBJECT (modest_runtime_get_account_store ()),
571 priv->account_removed_handler);
573 if (priv->header_model) {
574 if (g_signal_handler_is_connected(G_OBJECT (priv->header_model),
575 priv->row_changed_handler))
576 g_signal_handler_disconnect(G_OBJECT (priv->header_model),
577 priv->row_changed_handler);
579 if (g_signal_handler_is_connected(G_OBJECT (priv->header_model),
580 priv->row_deleted_handler))
581 g_signal_handler_disconnect(G_OBJECT (priv->header_model),
582 priv->row_deleted_handler);
584 if (g_signal_handler_is_connected(G_OBJECT (priv->header_model),
585 priv->row_inserted_handler))
586 g_signal_handler_disconnect(G_OBJECT (priv->header_model),
587 priv->row_inserted_handler);
589 if (g_signal_handler_is_connected(G_OBJECT (priv->header_model),
590 priv->rows_reordered_handler))
591 g_signal_handler_disconnect(G_OBJECT (priv->header_model),
592 priv->rows_reordered_handler);
595 modest_signal_mgr_disconnect_all_and_destroy (priv->sighandlers);
596 priv->sighandlers = NULL;
598 parent_window = gtk_window_get_transient_for (GTK_WINDOW (self));
599 if (parent_window && MODEST_IS_HEADER_WINDOW (parent_window)) {
600 header_view = GTK_WIDGET (modest_header_window_get_header_view (MODEST_HEADER_WINDOW (parent_window)));
602 modest_header_view_remove_observer(MODEST_HEADER_VIEW (header_view),
603 MODEST_HEADER_VIEW_OBSERVER(self));
609 modest_msg_view_window_finalize (GObject *obj)
611 ModestMsgViewWindowPrivate *priv;
613 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (obj);
615 /* Sanity check: shouldn't be needed, the window mgr should
616 call this function before */
617 modest_msg_view_window_disconnect_signals (MODEST_WINDOW (obj));
619 if (priv->other_body != NULL) {
620 g_object_unref (priv->other_body);
621 priv->other_body = NULL;
624 if (priv->header_model != NULL) {
625 g_object_unref (priv->header_model);
626 priv->header_model = NULL;
629 if (priv->remove_attachment_banner) {
630 gtk_widget_destroy (priv->remove_attachment_banner);
631 g_object_unref (priv->remove_attachment_banner);
632 priv->remove_attachment_banner = NULL;
635 if (priv->purge_timeout > 0) {
636 g_source_remove (priv->purge_timeout);
637 priv->purge_timeout = 0;
640 if (priv->row_reference) {
641 gtk_tree_row_reference_free (priv->row_reference);
642 priv->row_reference = NULL;
645 if (priv->next_row_reference) {
646 gtk_tree_row_reference_free (priv->next_row_reference);
647 priv->next_row_reference = NULL;
651 g_free (priv->msg_uid);
652 priv->msg_uid = NULL;
655 G_OBJECT_CLASS(parent_class)->finalize (obj);
659 select_next_valid_row (GtkTreeModel *model,
660 GtkTreeRowReference **row_reference,
664 GtkTreeIter tmp_iter;
666 GtkTreePath *next = NULL;
667 gboolean retval = FALSE, finished;
669 g_return_val_if_fail (gtk_tree_row_reference_valid (*row_reference), FALSE);
671 path = gtk_tree_row_reference_get_path (*row_reference);
672 gtk_tree_model_get_iter (model, &tmp_iter, path);
673 gtk_tree_row_reference_free (*row_reference);
674 *row_reference = NULL;
678 TnyHeader *header = NULL;
680 if (gtk_tree_model_iter_next (model, &tmp_iter)) {
681 gtk_tree_model_get (model, &tmp_iter,
682 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
686 if (msg_is_visible (header, is_outbox)) {
687 next = gtk_tree_model_get_path (model, &tmp_iter);
688 *row_reference = gtk_tree_row_reference_new (model, next);
689 gtk_tree_path_free (next);
693 g_object_unref (header);
696 } else if (cycle && gtk_tree_model_get_iter_first (model, &tmp_iter)) {
697 next = gtk_tree_model_get_path (model, &tmp_iter);
699 /* Ensure that we are not selecting the same */
700 if (gtk_tree_path_compare (path, next) != 0) {
701 gtk_tree_model_get (model, &tmp_iter,
702 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
705 if (msg_is_visible (header, is_outbox)) {
706 *row_reference = gtk_tree_row_reference_new (model, next);
710 g_object_unref (header);
714 /* If we ended up in the same message
715 then there is no valid next
719 gtk_tree_path_free (next);
721 /* If there are no more messages and we don't
722 want to start again in the first one then
723 there is no valid next message */
729 gtk_tree_path_free (path);
734 /* TODO: This should be in _init(), with the parameters as properties. */
736 modest_msg_view_window_construct (ModestMsgViewWindow *self,
737 const gchar *modest_account_name,
738 const gchar *mailbox,
739 const gchar *msg_uid)
742 ModestMsgViewWindowPrivate *priv = NULL;
743 ModestWindowPrivate *parent_priv = NULL;
744 ModestDimmingRulesGroup *toolbar_rules_group = NULL;
745 ModestDimmingRulesGroup *clipboard_rules_group = NULL;
747 obj = G_OBJECT (self);
748 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(obj);
749 parent_priv = MODEST_WINDOW_GET_PRIVATE(obj);
751 priv->msg_uid = g_strdup (msg_uid);
754 parent_priv->menubar = NULL;
756 toolbar_rules_group = modest_dimming_rules_group_new (MODEST_DIMMING_RULES_TOOLBAR, TRUE);
757 clipboard_rules_group = modest_dimming_rules_group_new (MODEST_DIMMING_RULES_CLIPBOARD, FALSE);
760 /* Add common dimming rules */
761 modest_dimming_rules_group_add_rules (toolbar_rules_group,
762 modest_msg_view_toolbar_dimming_entries,
763 G_N_ELEMENTS (modest_msg_view_toolbar_dimming_entries),
764 MODEST_WINDOW (self));
765 modest_dimming_rules_group_add_rules (clipboard_rules_group,
766 modest_msg_view_clipboard_dimming_entries,
767 G_N_ELEMENTS (modest_msg_view_clipboard_dimming_entries),
768 MODEST_WINDOW (self));
770 /* Insert dimming rules group for this window */
771 modest_ui_dimming_manager_insert_rules_group (parent_priv->ui_dimming_manager, toolbar_rules_group);
772 modest_ui_dimming_manager_insert_rules_group (parent_priv->ui_dimming_manager, clipboard_rules_group);
773 g_object_unref (toolbar_rules_group);
774 g_object_unref (clipboard_rules_group);
776 /* g_signal_connect (G_OBJECT(obj), "delete-event", G_CALLBACK(on_delete_event), obj); */
778 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);
779 g_signal_connect (G_OBJECT(priv->msg_view), "activate_link",
780 G_CALLBACK (modest_ui_actions_on_msg_link_clicked), obj);
781 g_signal_connect (G_OBJECT(priv->msg_view), "link_hover",
782 G_CALLBACK (modest_ui_actions_on_msg_link_hover), obj);
783 g_signal_connect (G_OBJECT(priv->msg_view), "attachment_clicked",
784 G_CALLBACK (modest_ui_actions_on_msg_attachment_clicked), obj);
785 g_signal_connect (G_OBJECT(priv->msg_view), "recpt_activated",
786 G_CALLBACK (modest_ui_actions_on_msg_recpt_activated), obj);
787 g_signal_connect (G_OBJECT(priv->msg_view), "show_details",
788 G_CALLBACK (modest_ui_actions_on_details), obj);
789 g_signal_connect (G_OBJECT(priv->msg_view), "link_contextual",
790 G_CALLBACK (modest_ui_actions_on_msg_link_contextual), obj);
791 g_signal_connect (G_OBJECT(priv->msg_view), "limit_error",
792 G_CALLBACK (modest_ui_actions_on_limit_error), obj);
793 g_signal_connect (G_OBJECT (priv->msg_view), "fetch_image",
794 G_CALLBACK (on_fetch_image), obj);
796 g_signal_connect (G_OBJECT (obj), "key-release-event",
797 G_CALLBACK (modest_msg_view_window_key_event),
800 g_signal_connect (G_OBJECT (obj), "key-press-event",
801 G_CALLBACK (modest_msg_view_window_key_event),
804 g_signal_connect (G_OBJECT (obj), "move-focus",
805 G_CALLBACK (on_move_focus), obj);
807 g_signal_connect (G_OBJECT (obj), "map-event",
808 G_CALLBACK (_modest_msg_view_window_map_event),
811 /* Mail Operation Queue */
812 priv->queue_change_handler = g_signal_connect (G_OBJECT (modest_runtime_get_mail_operation_queue ()),
814 G_CALLBACK (on_queue_changed),
817 /* Account manager */
818 priv->account_removed_handler = g_signal_connect (G_OBJECT (modest_runtime_get_account_store ()),
820 G_CALLBACK(on_account_removed),
823 modest_window_set_active_account (MODEST_WINDOW(obj), modest_account_name);
824 modest_window_set_active_mailbox (MODEST_WINDOW(obj), mailbox);
826 /* First add out toolbar ... */
827 modest_msg_view_window_show_toolbar (MODEST_WINDOW (obj), TRUE);
829 /* ... and later the find toolbar. This way find toolbar will
830 be shown over the other */
831 priv->find_toolbar = hildon_find_toolbar_new (NULL);
832 hildon_window_add_toolbar (HILDON_WINDOW (obj), GTK_TOOLBAR (priv->find_toolbar));
833 gtk_widget_set_no_show_all (priv->find_toolbar, TRUE);
834 g_signal_connect (G_OBJECT (priv->find_toolbar), "close",
835 G_CALLBACK (modest_msg_view_window_find_toolbar_close), obj);
836 g_signal_connect (G_OBJECT (priv->find_toolbar), "search",
837 G_CALLBACK (modest_msg_view_window_find_toolbar_search), obj);
838 priv->last_search = NULL;
840 /* Init the clipboard actions dim status */
841 modest_msg_view_grab_focus(MODEST_MSG_VIEW (priv->msg_view));
843 update_window_title (MODEST_MSG_VIEW_WINDOW (obj));
848 /* FIXME: parameter checks */
850 modest_msg_view_window_new_with_header_model (TnyMsg *msg,
851 const gchar *modest_account_name,
852 const gchar *mailbox,
853 const gchar *msg_uid,
855 GtkTreeRowReference *row_reference)
857 ModestMsgViewWindow *window = NULL;
858 ModestMsgViewWindowPrivate *priv = NULL;
859 TnyFolder *header_folder = NULL;
860 ModestHeaderView *header_view = NULL;
861 ModestWindowMgr *mgr = NULL;
864 modest_tny_mime_part_to_string (TNY_MIME_PART (msg), 0);
867 mgr = modest_runtime_get_window_mgr ();
868 window = MODEST_MSG_VIEW_WINDOW (modest_window_mgr_get_msg_view_window (mgr));
869 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), NULL);
871 modest_msg_view_window_construct (window, modest_account_name, mailbox, msg_uid);
873 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
875 /* Remember the message list's TreeModel so we can detect changes
876 * and change the list selection when necessary: */
877 header_folder = modest_header_view_get_folder (header_view);
879 priv->is_outbox = (modest_tny_folder_guess_folder_type (header_folder) ==
880 TNY_FOLDER_TYPE_OUTBOX);
881 priv->header_folder_id = tny_folder_get_id (header_folder);
882 g_object_unref(header_folder);
885 /* Setup row references and connect signals */
886 priv->header_model = g_object_ref (model);
888 if (row_reference && gtk_tree_row_reference_valid (row_reference)) {
889 priv->row_reference = gtk_tree_row_reference_copy (row_reference);
890 priv->next_row_reference = gtk_tree_row_reference_copy (row_reference);
891 select_next_valid_row (model, &(priv->next_row_reference), TRUE, priv->is_outbox);
893 priv->row_reference = NULL;
894 priv->next_row_reference = NULL;
897 /* Connect signals */
898 priv->row_changed_handler =
899 g_signal_connect (GTK_TREE_MODEL(model), "row-changed",
900 G_CALLBACK(modest_msg_view_window_on_row_changed),
902 priv->row_deleted_handler =
903 g_signal_connect (GTK_TREE_MODEL(model), "row-deleted",
904 G_CALLBACK(modest_msg_view_window_on_row_deleted),
906 priv->row_inserted_handler =
907 g_signal_connect (GTK_TREE_MODEL(model), "row-inserted",
908 G_CALLBACK(modest_msg_view_window_on_row_inserted),
910 priv->rows_reordered_handler =
911 g_signal_connect(GTK_TREE_MODEL(model), "rows-reordered",
912 G_CALLBACK(modest_msg_view_window_on_row_reordered),
915 if (header_view != NULL){
916 modest_header_view_add_observer(header_view,
917 MODEST_HEADER_VIEW_OBSERVER(window));
920 tny_msg_view_set_msg (TNY_MSG_VIEW (priv->msg_view), msg);
921 update_window_title (MODEST_MSG_VIEW_WINDOW (window));
922 update_branding (MODEST_MSG_VIEW_WINDOW (window));
924 /* gtk_widget_show_all (GTK_WIDGET (window)); */
925 modest_msg_view_window_update_priority (window);
926 /* Check dimming rules */
927 modest_ui_actions_check_toolbar_dimming_rules (MODEST_WINDOW (window));
928 modest_ui_actions_check_menu_dimming_rules (MODEST_WINDOW (window));
929 modest_window_check_dimming_rules_group (MODEST_WINDOW (window), MODEST_DIMMING_RULES_CLIPBOARD);
931 return MODEST_WINDOW(window);
935 modest_msg_view_window_new_from_uid (const gchar *modest_account_name,
936 const gchar *mailbox,
937 const gchar *msg_uid)
939 ModestMsgViewWindow *window = NULL;
940 ModestMsgViewWindowPrivate *priv = NULL;
941 ModestWindowMgr *mgr = NULL;
943 TnyAccount *account = NULL;
945 mgr = modest_runtime_get_window_mgr ();
946 window = MODEST_MSG_VIEW_WINDOW (modest_window_mgr_get_msg_view_window (mgr));
947 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), NULL);
949 modest_msg_view_window_construct (window, modest_account_name, mailbox, msg_uid);
951 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
955 is_merge = g_str_has_prefix (msg_uid, "merge:");
957 /* Get the account */
959 account = tny_account_store_find_account (TNY_ACCOUNT_STORE (modest_runtime_get_account_store ()),
963 if (is_merge || account) {
964 TnyFolder *folder = NULL;
966 /* Try to get the message, if it's already downloaded
967 we don't need to connect */
969 folder = modest_tny_folder_store_find_folder_from_uri (TNY_FOLDER_STORE (account), msg_uid);
971 ModestTnyAccountStore *account_store;
972 ModestTnyLocalFoldersAccount *local_folders_account;
974 account_store = modest_runtime_get_account_store ();
975 local_folders_account = MODEST_TNY_LOCAL_FOLDERS_ACCOUNT (
976 modest_tny_account_store_get_local_folders_account (account_store));
977 folder = modest_tny_local_folders_account_get_merged_outbox (local_folders_account);
978 g_object_unref (local_folders_account);
982 gboolean device_online;
984 device = modest_runtime_get_device();
985 device_online = tny_device_is_online (device);
987 message_reader (window, priv, NULL, msg_uid, folder, NULL);
989 TnyMsg *msg = tny_folder_find_msg (folder, msg_uid, NULL);
991 tny_msg_view_set_msg (TNY_MSG_VIEW (priv->msg_view), msg);
992 update_window_title (MODEST_MSG_VIEW_WINDOW (window));
993 update_branding (MODEST_MSG_VIEW_WINDOW (window));
994 g_object_unref (msg);
996 message_reader (window, priv, NULL, msg_uid, folder, NULL);
999 g_object_unref (folder);
1004 /* Check dimming rules */
1005 modest_ui_actions_check_toolbar_dimming_rules (MODEST_WINDOW (window));
1006 modest_ui_actions_check_menu_dimming_rules (MODEST_WINDOW (window));
1007 modest_window_check_dimming_rules_group (MODEST_WINDOW (window), MODEST_DIMMING_RULES_CLIPBOARD);
1009 return MODEST_WINDOW(window);
1013 modest_msg_view_window_new_from_header_view (ModestHeaderView *header_view,
1014 const gchar *modest_account_name,
1015 const gchar *mailbox,
1016 const gchar *msg_uid,
1017 GtkTreeRowReference *row_reference)
1019 ModestMsgViewWindow *window = NULL;
1020 ModestMsgViewWindowPrivate *priv = NULL;
1021 TnyFolder *header_folder = NULL;
1022 ModestWindowMgr *mgr = NULL;
1026 mgr = modest_runtime_get_window_mgr ();
1027 window = MODEST_MSG_VIEW_WINDOW (modest_window_mgr_get_msg_view_window (mgr));
1028 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), NULL);
1030 modest_msg_view_window_construct (window, modest_account_name, mailbox, msg_uid);
1032 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1034 /* Remember the message list's TreeModel so we can detect changes
1035 * and change the list selection when necessary: */
1037 if (header_view != NULL){
1038 header_folder = modest_header_view_get_folder(header_view);
1039 /* This could happen if the header folder was
1040 unseleted before opening this msg window (for
1041 example if the user selects an account in the
1042 folder view of the main window */
1043 if (header_folder) {
1044 priv->is_outbox = (modest_tny_folder_guess_folder_type (header_folder) ==
1045 TNY_FOLDER_TYPE_OUTBOX);
1046 priv->header_folder_id = tny_folder_get_id(header_folder);
1047 g_object_unref(header_folder);
1051 /* Setup row references and connect signals */
1052 priv->header_model = gtk_tree_view_get_model (GTK_TREE_VIEW (header_view));
1053 g_object_ref (priv->header_model);
1055 if (row_reference && gtk_tree_row_reference_valid (row_reference)) {
1056 priv->row_reference = gtk_tree_row_reference_copy (row_reference);
1057 priv->next_row_reference = gtk_tree_row_reference_copy (row_reference);
1058 select_next_valid_row (priv->header_model, &(priv->next_row_reference), TRUE, priv->is_outbox);
1060 priv->row_reference = NULL;
1061 priv->next_row_reference = NULL;
1064 /* Connect signals */
1065 priv->row_changed_handler =
1066 g_signal_connect (GTK_TREE_MODEL(priv->header_model), "row-changed",
1067 G_CALLBACK(modest_msg_view_window_on_row_changed),
1069 priv->row_deleted_handler =
1070 g_signal_connect (GTK_TREE_MODEL(priv->header_model), "row-deleted",
1071 G_CALLBACK(modest_msg_view_window_on_row_deleted),
1073 priv->row_inserted_handler =
1074 g_signal_connect (GTK_TREE_MODEL(priv->header_model), "row-inserted",
1075 G_CALLBACK(modest_msg_view_window_on_row_inserted),
1077 priv->rows_reordered_handler =
1078 g_signal_connect(GTK_TREE_MODEL(priv->header_model), "rows-reordered",
1079 G_CALLBACK(modest_msg_view_window_on_row_reordered),
1082 if (header_view != NULL){
1083 modest_header_view_add_observer(header_view,
1084 MODEST_HEADER_VIEW_OBSERVER(window));
1087 tny_msg_view_set_msg (TNY_MSG_VIEW (priv->msg_view), NULL);
1088 update_branding (MODEST_MSG_VIEW_WINDOW (window));
1090 if (priv->row_reference) {
1091 path = gtk_tree_row_reference_get_path (priv->row_reference);
1092 if (gtk_tree_model_get_iter (priv->header_model, &iter, path)) {
1094 gtk_tree_model_get (priv->header_model, &iter,
1095 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
1097 message_reader (window, priv, header, NULL, NULL, priv->row_reference);
1098 g_object_unref (header);
1100 gtk_tree_path_free (path);
1102 /* Check dimming rules */
1103 modest_ui_actions_check_toolbar_dimming_rules (MODEST_WINDOW (window));
1104 modest_ui_actions_check_menu_dimming_rules (MODEST_WINDOW (window));
1105 modest_window_check_dimming_rules_group (MODEST_WINDOW (window), MODEST_DIMMING_RULES_CLIPBOARD);
1107 return MODEST_WINDOW(window);
1111 modest_msg_view_window_new_for_search_result (TnyMsg *msg,
1112 const gchar *modest_account_name,
1113 const gchar *mailbox,
1114 const gchar *msg_uid)
1116 ModestMsgViewWindow *window = NULL;
1117 ModestMsgViewWindowPrivate *priv = NULL;
1118 ModestWindowMgr *mgr = NULL;
1120 mgr = modest_runtime_get_window_mgr ();
1121 window = MODEST_MSG_VIEW_WINDOW (modest_window_mgr_get_msg_view_window (mgr));
1122 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), NULL);
1123 modest_msg_view_window_construct (window, modest_account_name, mailbox, msg_uid);
1125 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1127 /* Remember that this is a search result,
1128 * so we can disable some UI appropriately: */
1129 priv->is_search_result = TRUE;
1131 tny_msg_view_set_msg (TNY_MSG_VIEW (priv->msg_view), msg);
1132 update_branding (MODEST_MSG_VIEW_WINDOW (window));
1134 update_window_title (window);
1135 /* gtk_widget_show_all (GTK_WIDGET (window));*/
1136 modest_msg_view_window_update_priority (window);
1138 /* Check dimming rules */
1139 modest_ui_actions_check_toolbar_dimming_rules (MODEST_WINDOW (window));
1140 modest_ui_actions_check_menu_dimming_rules (MODEST_WINDOW (window));
1141 modest_window_check_dimming_rules_group (MODEST_WINDOW (window), MODEST_DIMMING_RULES_CLIPBOARD);
1143 return MODEST_WINDOW(window);
1147 modest_msg_view_window_is_other_body (ModestMsgViewWindow *self)
1149 ModestMsgViewWindowPrivate *priv = NULL;
1151 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self), FALSE);
1152 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
1154 return (priv->other_body != NULL);
1158 modest_msg_view_window_new_with_other_body (TnyMsg *msg,
1159 TnyMimePart *other_body,
1160 const gchar *modest_account_name,
1161 const gchar *mailbox,
1162 const gchar *msg_uid)
1164 GObject *obj = NULL;
1165 ModestMsgViewWindowPrivate *priv;
1166 ModestWindowMgr *mgr = NULL;
1168 g_return_val_if_fail (msg, NULL);
1169 mgr = modest_runtime_get_window_mgr ();
1170 obj = G_OBJECT (modest_window_mgr_get_msg_view_window (mgr));
1171 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (obj);
1172 modest_msg_view_window_construct (MODEST_MSG_VIEW_WINDOW (obj),
1173 modest_account_name, mailbox, msg_uid);
1176 priv->other_body = g_object_ref (other_body);
1177 modest_msg_view_set_msg_with_other_body (MODEST_MSG_VIEW (priv->msg_view), msg, other_body);
1179 tny_msg_view_set_msg (TNY_MSG_VIEW (priv->msg_view), msg);
1181 update_window_title (MODEST_MSG_VIEW_WINDOW (obj));
1182 update_branding (MODEST_MSG_VIEW_WINDOW (obj));
1184 /* gtk_widget_show_all (GTK_WIDGET (obj)); */
1186 /* Check dimming rules */
1187 modest_ui_actions_check_toolbar_dimming_rules (MODEST_WINDOW (obj));
1188 modest_ui_actions_check_menu_dimming_rules (MODEST_WINDOW (obj));
1189 modest_window_check_dimming_rules_group (MODEST_WINDOW (obj), MODEST_DIMMING_RULES_CLIPBOARD);
1191 return MODEST_WINDOW(obj);
1195 modest_msg_view_window_new_for_attachment (TnyMsg *msg,
1196 const gchar *modest_account_name,
1197 const gchar *mailbox,
1198 const gchar *msg_uid)
1200 return modest_msg_view_window_new_with_other_body (msg, NULL, modest_account_name, mailbox, msg_uid);
1204 modest_msg_view_window_on_row_changed (GtkTreeModel *header_model,
1207 ModestMsgViewWindow *window)
1209 check_dimming_rules_after_change (window);
1213 modest_msg_view_window_on_row_deleted(GtkTreeModel *header_model,
1215 ModestMsgViewWindow *window)
1217 check_dimming_rules_after_change (window);
1219 /* The window could have dissapeared */
1222 check_dimming_rules_after_change (ModestMsgViewWindow *window)
1224 modest_ui_actions_check_menu_dimming_rules (MODEST_WINDOW (window));
1225 modest_ui_actions_check_toolbar_dimming_rules (MODEST_WINDOW (window));
1229 /* On insertions we check if the folder still has the message we are
1230 * showing or do not. If do not, we do nothing. Which means we are still
1231 * not attached to any header folder and thus next/prev buttons are
1232 * still dimmed. Once the message that is shown by msg-view is found, the
1233 * new model of header-view will be attached and the references will be set.
1234 * On each further insertions dimming rules will be checked. However
1235 * this requires extra CPU time at least works.
1236 * (An message might be deleted from TnyFolder and thus will not be
1237 * inserted into the model again for example if it is removed by the
1238 * imap server and the header view is refreshed.)
1241 modest_msg_view_window_on_row_inserted (GtkTreeModel *model,
1242 GtkTreePath *tree_path,
1243 GtkTreeIter *tree_iter,
1244 ModestMsgViewWindow *window)
1246 ModestMsgViewWindowPrivate *priv = NULL;
1247 TnyHeader *header = NULL;
1249 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window));
1250 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1252 g_assert (model == priv->header_model);
1254 /* Check if the newly inserted message is the same we are actually
1255 * showing. IF not, we should remain detached from the header model
1256 * and thus prev and next toolbar buttons should remain dimmed. */
1257 gtk_tree_model_get (model, tree_iter,
1258 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
1261 if (TNY_IS_HEADER (header)) {
1264 uid = modest_tny_folder_get_header_unique_id (header);
1265 if (!g_str_equal(priv->msg_uid, uid)) {
1266 check_dimming_rules_after_change (window);
1268 g_object_unref (G_OBJECT(header));
1272 g_object_unref(G_OBJECT(header));
1275 if (priv->row_reference) {
1276 gtk_tree_row_reference_free (priv->row_reference);
1279 /* Setup row_reference for the actual msg. */
1280 priv->row_reference = gtk_tree_row_reference_new (priv->header_model, tree_path);
1281 if (priv->row_reference == NULL) {
1282 g_warning("%s: No reference for msg header item.", __FUNCTION__);
1286 /* Now set up next_row_reference. */
1287 if (priv->next_row_reference) {
1288 gtk_tree_row_reference_free (priv->next_row_reference);
1291 priv->next_row_reference = gtk_tree_row_reference_copy (priv->row_reference);
1292 select_next_valid_row (priv->header_model,
1293 &(priv->next_row_reference), FALSE, priv->is_outbox);
1295 /* Connect the remaining callbacks to become able to detect
1296 * changes in header-view. */
1297 priv->row_changed_handler =
1298 g_signal_connect (priv->header_model, "row-changed",
1299 G_CALLBACK (modest_msg_view_window_on_row_changed),
1301 priv->row_deleted_handler =
1302 g_signal_connect (priv->header_model, "row-deleted",
1303 G_CALLBACK (modest_msg_view_window_on_row_deleted),
1305 priv->rows_reordered_handler =
1306 g_signal_connect (priv->header_model, "rows-reordered",
1307 G_CALLBACK (modest_msg_view_window_on_row_reordered),
1310 check_dimming_rules_after_change (window);
1314 modest_msg_view_window_on_row_reordered (GtkTreeModel *header_model,
1318 ModestMsgViewWindow *window)
1320 ModestMsgViewWindowPrivate *priv = NULL;
1321 gboolean already_changed = FALSE;
1323 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(window);
1325 /* If the current row was reordered select the proper next
1326 valid row. The same if the next row reference changes */
1327 if (!priv->row_reference ||
1328 !gtk_tree_row_reference_valid (priv->row_reference))
1331 if (priv->next_row_reference &&
1332 gtk_tree_row_reference_valid (priv->next_row_reference)) {
1333 GtkTreePath *cur, *next;
1334 /* Check that the order is still the correct one */
1335 cur = gtk_tree_row_reference_get_path (priv->row_reference);
1336 next = gtk_tree_row_reference_get_path (priv->next_row_reference);
1337 gtk_tree_path_next (cur);
1338 if (gtk_tree_path_compare (cur, next) != 0) {
1339 gtk_tree_row_reference_free (priv->next_row_reference);
1340 priv->next_row_reference = gtk_tree_row_reference_copy (priv->row_reference);
1341 select_next_valid_row (header_model, &(priv->next_row_reference), FALSE, priv->is_outbox);
1342 already_changed = TRUE;
1344 gtk_tree_path_free (cur);
1345 gtk_tree_path_free (next);
1347 if (priv->next_row_reference)
1348 gtk_tree_row_reference_free (priv->next_row_reference);
1349 /* Update next row reference */
1350 priv->next_row_reference = gtk_tree_row_reference_copy (priv->row_reference);
1351 select_next_valid_row (header_model, &(priv->next_row_reference), FALSE, priv->is_outbox);
1352 already_changed = TRUE;
1355 check_dimming_rules_after_change (window);
1358 /* The modest_msg_view_window_update_model_replaced implements update
1359 * function for ModestHeaderViewObserver. Checks whether the TnyFolder
1360 * actually belongs to the header-view is the same as the TnyFolder of
1361 * the message of msg-view or not. If they are different, there is
1362 * nothing to do. If they are the same, then the model has replaced and
1363 * the reference in msg-view shall be replaced from the old model to
1364 * the new model. In this case the view will be detached from it's
1365 * header folder. From this point the next/prev buttons are dimmed.
1368 modest_msg_view_window_update_model_replaced (ModestHeaderViewObserver *observer,
1369 GtkTreeModel *model,
1370 const gchar *tny_folder_id)
1372 ModestMsgViewWindowPrivate *priv = NULL;
1373 ModestMsgViewWindow *window = NULL;
1375 g_assert(MODEST_IS_HEADER_VIEW_OBSERVER(observer));
1376 g_assert(MODEST_IS_MSG_VIEW_WINDOW(observer));
1378 window = MODEST_MSG_VIEW_WINDOW(observer);
1379 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(window);
1381 /* If there is an other folder in the header-view then we do
1382 * not care about it's model (msg list). Else if the
1383 * header-view shows the folder the msg shown by us is in, we
1384 * shall replace our model reference and make some check. */
1385 if(model == NULL || tny_folder_id == NULL ||
1386 (priv->header_folder_id && !g_str_equal(tny_folder_id, priv->header_folder_id)))
1389 /* Model is changed(replaced), so we should forget the old
1390 * one. Because there might be other references and there
1391 * might be some change on the model even if we unreferenced
1392 * it, we need to disconnect our signals here. */
1393 if (priv->header_model) {
1394 if (g_signal_handler_is_connected(G_OBJECT (priv->header_model),
1395 priv->row_changed_handler))
1396 g_signal_handler_disconnect(G_OBJECT (priv->header_model),
1397 priv->row_changed_handler);
1398 if (g_signal_handler_is_connected(G_OBJECT (priv->header_model),
1399 priv->row_deleted_handler))
1400 g_signal_handler_disconnect(G_OBJECT (priv->header_model),
1401 priv->row_deleted_handler);
1402 if (g_signal_handler_is_connected(G_OBJECT (priv->header_model),
1403 priv->row_inserted_handler))
1404 g_signal_handler_disconnect(G_OBJECT (priv->header_model),
1405 priv->row_inserted_handler);
1406 if (g_signal_handler_is_connected(G_OBJECT (priv->header_model),
1407 priv->rows_reordered_handler))
1408 g_signal_handler_disconnect(G_OBJECT (priv->header_model),
1409 priv->rows_reordered_handler);
1412 if (priv->row_reference)
1413 gtk_tree_row_reference_free (priv->row_reference);
1414 if (priv->next_row_reference)
1415 gtk_tree_row_reference_free (priv->next_row_reference);
1416 g_object_unref(priv->header_model);
1419 priv->row_changed_handler = 0;
1420 priv->row_deleted_handler = 0;
1421 priv->row_inserted_handler = 0;
1422 priv->rows_reordered_handler = 0;
1423 priv->next_row_reference = NULL;
1424 priv->row_reference = NULL;
1425 priv->header_model = NULL;
1428 priv->header_model = g_object_ref (model);
1430 /* Also we must connect to the new model for row insertions.
1431 * Only for insertions now. We will need other ones only after
1432 * the msg is show by msg-view is added to the new model. */
1433 priv->row_inserted_handler =
1434 g_signal_connect (priv->header_model, "row-inserted",
1435 G_CALLBACK(modest_msg_view_window_on_row_inserted),
1438 modest_ui_actions_check_menu_dimming_rules(MODEST_WINDOW(window));
1439 modest_ui_actions_check_toolbar_dimming_rules(MODEST_WINDOW(window));
1443 modest_msg_view_window_toolbar_on_transfer_mode (ModestMsgViewWindow *self)
1445 ModestMsgViewWindowPrivate *priv= NULL;
1447 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self), FALSE);
1448 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
1450 return priv->progress_hint;
1454 modest_msg_view_window_get_header (ModestMsgViewWindow *self)
1456 ModestMsgViewWindowPrivate *priv= NULL;
1458 TnyHeader *header = NULL;
1459 GtkTreePath *path = NULL;
1462 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self), NULL);
1463 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
1465 /* If the message was not obtained from a treemodel,
1466 * for instance if it was opened directly by the search UI:
1468 if (priv->header_model == NULL ||
1469 priv->row_reference == NULL ||
1470 !gtk_tree_row_reference_valid (priv->row_reference)) {
1471 msg = modest_msg_view_window_get_message (self);
1473 header = tny_msg_get_header (msg);
1474 g_object_unref (msg);
1479 /* Get iter of the currently selected message in the header view: */
1480 path = gtk_tree_row_reference_get_path (priv->row_reference);
1481 g_return_val_if_fail (path != NULL, NULL);
1482 gtk_tree_model_get_iter (priv->header_model,
1486 /* Get current message header */
1487 gtk_tree_model_get (priv->header_model, &iter,
1488 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
1491 gtk_tree_path_free (path);
1496 modest_msg_view_window_get_message (ModestMsgViewWindow *self)
1498 ModestMsgViewWindowPrivate *priv;
1500 g_return_val_if_fail (self, NULL);
1502 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(self);
1504 return tny_msg_view_get_msg (TNY_MSG_VIEW (priv->msg_view));
1508 modest_msg_view_window_get_message_uid (ModestMsgViewWindow *self)
1510 ModestMsgViewWindowPrivate *priv;
1512 g_return_val_if_fail (self, NULL);
1514 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
1516 return (const gchar*) priv->msg_uid;
1519 /* Used for the Ctrl+F accelerator */
1521 modest_msg_view_window_toggle_find_toolbar (GtkWidget *obj,
1524 ModestMsgViewWindow *window = MODEST_MSG_VIEW_WINDOW (data);
1525 ModestMsgViewWindowPrivate *priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1527 if (GTK_WIDGET_VISIBLE (priv->find_toolbar)) {
1528 modest_msg_view_window_find_toolbar_close (obj, data);
1530 modest_msg_view_window_show_find_toolbar (obj, data);
1534 /* Handler for menu option */
1536 modest_msg_view_window_show_find_toolbar (GtkWidget *obj,
1539 ModestMsgViewWindow *window = MODEST_MSG_VIEW_WINDOW (data);
1540 ModestMsgViewWindowPrivate *priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1542 gtk_widget_show (priv->find_toolbar);
1543 hildon_find_toolbar_highlight_entry (HILDON_FIND_TOOLBAR (priv->find_toolbar), TRUE);
1546 /* Handler for click on the "X" close button in find toolbar */
1548 modest_msg_view_window_find_toolbar_close (GtkWidget *widget,
1549 ModestMsgViewWindow *obj)
1551 ModestMsgViewWindowPrivate *priv;
1553 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (obj);
1556 gtk_widget_hide (priv->find_toolbar);
1557 modest_msg_view_grab_focus (MODEST_MSG_VIEW (priv->msg_view));
1561 modest_msg_view_window_find_toolbar_search (GtkWidget *widget,
1562 ModestMsgViewWindow *obj)
1564 gchar *current_search;
1565 ModestMsgViewWindowPrivate *priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (obj);
1567 if (modest_mime_part_view_is_empty (MODEST_MIME_PART_VIEW (priv->msg_view))) {
1568 hildon_banner_show_information (NULL, NULL, _("mail_ib_nothing_to_find"));
1572 g_object_get (G_OBJECT (widget), "prefix", ¤t_search, NULL);
1574 if ((current_search == NULL) || (strcmp (current_search, "") == 0)) {
1575 g_free (current_search);
1576 hildon_banner_show_information (NULL, NULL, _CS("ecdg_ib_find_rep_enter_text"));
1580 if ((priv->last_search == NULL) || (strcmp (priv->last_search, current_search) != 0)) {
1582 g_free (priv->last_search);
1583 priv->last_search = g_strdup (current_search);
1584 result = modest_isearch_view_search (MODEST_ISEARCH_VIEW (priv->msg_view),
1587 hildon_banner_show_information (NULL, NULL,
1588 _HL("ckct_ib_find_no_matches"));
1589 g_free (priv->last_search);
1590 priv->last_search = NULL;
1592 hildon_find_toolbar_highlight_entry (HILDON_FIND_TOOLBAR (priv->find_toolbar), TRUE);
1595 if (!modest_isearch_view_search_next (MODEST_ISEARCH_VIEW (priv->msg_view))) {
1596 hildon_banner_show_information (NULL, NULL,
1597 _HL("ckct_ib_find_search_complete"));
1598 g_free (priv->last_search);
1599 priv->last_search = NULL;
1601 hildon_find_toolbar_highlight_entry (HILDON_FIND_TOOLBAR (priv->find_toolbar), TRUE);
1605 g_free (current_search);
1610 modest_msg_view_window_set_zoom (ModestWindow *window,
1613 ModestMsgViewWindowPrivate *priv;
1614 ModestWindowPrivate *parent_priv;
1616 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window));
1618 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1619 parent_priv = MODEST_WINDOW_GET_PRIVATE (window);
1620 modest_zoomable_set_zoom (MODEST_ZOOMABLE (priv->msg_view), zoom);
1625 modest_msg_view_window_get_zoom (ModestWindow *window)
1627 ModestMsgViewWindowPrivate *priv;
1629 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), 1.0);
1631 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1632 return modest_zoomable_get_zoom (MODEST_ZOOMABLE (priv->msg_view));
1636 modest_msg_view_window_zoom_plus (ModestWindow *window)
1639 ModestMsgViewWindowPrivate *priv;
1643 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), 1.0);
1644 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1646 zoom_level = modest_zoomable_get_zoom (MODEST_ZOOMABLE (priv->msg_view));
1648 if (zoom_level >= 2.0) {
1649 hildon_banner_show_information (NULL, NULL,
1650 _CS("ckct_ib_max_zoom_level_reached"));
1652 } else if (zoom_level >= 1.5) {
1654 } else if (zoom_level >= 1.2) {
1656 } else if (zoom_level >= 1.0) {
1658 } else if (zoom_level >= 0.8) {
1660 } else if (zoom_level >= 0.5) {
1666 /* set zoom level */
1667 int_zoom = (gint) rint (zoom_level*100.0+0.1);
1668 banner_text = g_strdup_printf (_HL("wdgt_ib_zoom"), int_zoom);
1669 modest_platform_information_banner (GTK_WIDGET (window), NULL, banner_text);
1670 g_free (banner_text);
1671 modest_zoomable_set_zoom (MODEST_ZOOMABLE (priv->msg_view), zoom_level);
1677 modest_msg_view_window_zoom_minus (ModestWindow *window)
1680 ModestMsgViewWindowPrivate *priv;
1684 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), 1.0);
1685 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1687 zoom_level = modest_zoomable_get_zoom (MODEST_ZOOMABLE (priv->msg_view));
1689 if (zoom_level <= 0.5) {
1690 hildon_banner_show_information (NULL, NULL,
1691 _CS("ckct_ib_min_zoom_level_reached"));
1693 } else if (zoom_level <= 0.8) {
1695 } else if (zoom_level <= 1.0) {
1697 } else if (zoom_level <= 1.2) {
1699 } else if (zoom_level <= 1.5) {
1701 } else if (zoom_level <= 2.0) {
1707 /* set zoom level */
1708 int_zoom = (gint) rint (zoom_level*100.0+0.1);
1709 banner_text = g_strdup_printf (_HL("wdgt_ib_zoom"), int_zoom);
1710 modest_platform_information_banner (GTK_WIDGET (window), NULL, banner_text);
1711 g_free (banner_text);
1712 modest_zoomable_set_zoom (MODEST_ZOOMABLE (priv->msg_view), zoom_level);
1718 modest_msg_view_window_key_event (GtkWidget *window,
1724 focus = gtk_window_get_focus (GTK_WINDOW (window));
1726 /* for the find toolbar case */
1727 if (focus && GTK_IS_ENTRY (focus)) {
1728 if (event->keyval == GDK_BackSpace) {
1730 copy = gdk_event_copy ((GdkEvent *) event);
1731 gtk_widget_event (focus, copy);
1732 gdk_event_free (copy);
1742 modest_msg_view_window_last_message_selected (ModestMsgViewWindow *window)
1745 ModestMsgViewWindowPrivate *priv;
1746 GtkTreeIter tmp_iter;
1747 gboolean is_last_selected;
1749 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), TRUE);
1750 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1752 /*if no model (so no rows at all), then virtually we are the last*/
1753 if (!priv->header_model || !priv->row_reference)
1756 if (!gtk_tree_row_reference_valid (priv->row_reference))
1759 path = gtk_tree_row_reference_get_path (priv->row_reference);
1763 is_last_selected = TRUE;
1764 while (is_last_selected) {
1766 gtk_tree_path_next (path);
1767 if (!gtk_tree_model_get_iter (priv->header_model, &tmp_iter, path))
1769 gtk_tree_model_get (priv->header_model, &tmp_iter,
1770 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
1773 if (msg_is_visible (header, priv->is_outbox))
1774 is_last_selected = FALSE;
1775 g_object_unref(G_OBJECT(header));
1778 gtk_tree_path_free (path);
1779 return is_last_selected;
1783 modest_msg_view_window_has_headers_model (ModestMsgViewWindow *window)
1785 ModestMsgViewWindowPrivate *priv;
1787 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), TRUE);
1788 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1790 return priv->header_model != NULL;
1794 modest_msg_view_window_is_search_result (ModestMsgViewWindow *window)
1796 ModestMsgViewWindowPrivate *priv;
1798 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), TRUE);
1799 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1801 return priv->is_search_result;
1805 msg_is_visible (TnyHeader *header, gboolean check_outbox)
1807 if ((tny_header_get_flags(header) & TNY_HEADER_FLAG_DELETED))
1809 if (!check_outbox) {
1812 ModestTnySendQueueStatus status;
1813 status = modest_tny_all_send_queues_get_msg_status (header);
1814 return ((status != MODEST_TNY_SEND_QUEUE_FAILED) &&
1815 (status != MODEST_TNY_SEND_QUEUE_SENDING));
1820 modest_msg_view_window_first_message_selected (ModestMsgViewWindow *window)
1823 ModestMsgViewWindowPrivate *priv;
1824 gboolean is_first_selected;
1825 GtkTreeIter tmp_iter;
1827 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), TRUE);
1828 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1830 /*if no model (so no rows at all), then virtually we are the first*/
1831 if (!priv->header_model || !priv->row_reference)
1834 if (!gtk_tree_row_reference_valid (priv->row_reference))
1837 path = gtk_tree_row_reference_get_path (priv->row_reference);
1841 is_first_selected = TRUE;
1842 while (is_first_selected) {
1844 if(!gtk_tree_path_prev (path))
1846 /* Here the 'if' is needless for logic, but let make sure
1847 * iter is valid for gtk_tree_model_get. */
1848 if (!gtk_tree_model_get_iter (priv->header_model, &tmp_iter, path))
1850 gtk_tree_model_get (priv->header_model, &tmp_iter,
1851 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
1854 if (msg_is_visible (header, priv->is_outbox))
1855 is_first_selected = FALSE;
1856 g_object_unref(G_OBJECT(header));
1859 gtk_tree_path_free (path);
1860 return is_first_selected;
1867 GtkTreeRowReference *row_reference;
1871 message_reader_performer (gboolean canceled,
1873 GtkWindow *parent_window,
1874 TnyAccount *account,
1877 ModestMailOperation *mail_op = NULL;
1878 MsgReaderInfo *info;
1880 info = (MsgReaderInfo *) user_data;
1881 if (canceled || err) {
1882 update_window_title (MODEST_MSG_VIEW_WINDOW (parent_window));
1886 /* Register the header - it'll be unregistered in the callback */
1888 modest_window_mgr_register_header (modest_runtime_get_window_mgr (), info->header, NULL);
1890 /* New mail operation */
1891 mail_op = modest_mail_operation_new_with_error_handling (G_OBJECT(parent_window),
1892 modest_ui_actions_disk_operations_error_handler,
1895 modest_mail_operation_queue_add (modest_runtime_get_mail_operation_queue (), mail_op);
1897 modest_mail_operation_get_msg (mail_op, info->header, TRUE, view_msg_cb, info->row_reference);
1899 modest_mail_operation_find_msg (mail_op, info->folder, info->msg_uid, TRUE, view_msg_cb, NULL);
1900 g_object_unref (mail_op);
1902 /* Update dimming rules */
1903 modest_ui_actions_check_toolbar_dimming_rules (MODEST_WINDOW (parent_window));
1904 modest_ui_actions_check_menu_dimming_rules (MODEST_WINDOW (parent_window));
1907 /* Frees. The row_reference will be freed by the view_msg_cb callback */
1908 g_free (info->msg_uid);
1910 g_object_unref (info->folder);
1912 g_object_unref (info->header);
1913 g_slice_free (MsgReaderInfo, info);
1918 * Reads the message whose summary item is @header. It takes care of
1919 * several things, among others:
1921 * If the message was not previously downloaded then ask the user
1922 * before downloading. If there is no connection launch the connection
1923 * dialog. Update toolbar dimming rules.
1925 * Returns: TRUE if the mail operation was started, otherwise if the
1926 * user do not want to download the message, or if the user do not
1927 * want to connect, then the operation is not issued
1930 message_reader (ModestMsgViewWindow *window,
1931 ModestMsgViewWindowPrivate *priv,
1933 const gchar *msg_uid,
1935 GtkTreeRowReference *row_reference)
1937 ModestWindowMgr *mgr;
1938 TnyAccount *account = NULL;
1939 MsgReaderInfo *info;
1941 /* We set the header from model while we're loading */
1942 tny_header_view_set_header (TNY_HEADER_VIEW (priv->msg_view), header);
1943 gtk_window_set_title (GTK_WINDOW (window), _CS("ckdg_pb_updating"));
1949 g_object_ref (folder);
1951 mgr = modest_runtime_get_window_mgr ();
1952 /* Msg download completed */
1953 if (!header || !(tny_header_get_flags (header) & TNY_HEADER_FLAG_CACHED)) {
1955 /* Ask the user if he wants to download the message if
1957 if (!tny_device_is_online (modest_runtime_get_device())) {
1958 GtkResponseType response;
1960 response = modest_platform_run_confirmation_dialog (GTK_WINDOW (window),
1961 _("mcen_nc_get_msg"));
1962 if (response == GTK_RESPONSE_CANCEL) {
1963 update_window_title (window);
1968 folder = tny_header_get_folder (header);
1970 info = g_slice_new (MsgReaderInfo);
1971 info->msg_uid = g_strdup (msg_uid);
1973 info->header = g_object_ref (header);
1975 info->header = NULL;
1977 info->folder = g_object_ref (folder);
1979 info->folder = NULL;
1980 if (row_reference) {
1981 info->row_reference = gtk_tree_row_reference_copy (row_reference);
1983 info->row_reference = NULL;
1986 /* Offer the connection dialog if necessary */
1987 modest_platform_connect_if_remote_and_perform ((GtkWindow *) window,
1989 TNY_FOLDER_STORE (folder),
1990 message_reader_performer,
1993 g_object_unref (folder);
1999 folder = tny_header_get_folder (header);
2002 account = tny_folder_get_account (folder);
2004 info = g_slice_new (MsgReaderInfo);
2005 info->msg_uid = g_strdup (msg_uid);
2007 info->folder = g_object_ref (folder);
2009 info->folder = NULL;
2011 info->header = g_object_ref (header);
2013 info->header = NULL;
2015 info->row_reference = gtk_tree_row_reference_copy (row_reference);
2017 info->row_reference = NULL;
2019 message_reader_performer (FALSE, NULL, (GtkWindow *) window, account, info);
2021 g_object_unref (account);
2023 g_object_unref (folder);
2029 modest_msg_view_window_select_next_message (ModestMsgViewWindow *window)
2031 ModestMsgViewWindowPrivate *priv;
2032 GtkTreePath *path= NULL;
2033 GtkTreeIter tmp_iter;
2035 gboolean retval = TRUE;
2036 GtkTreeRowReference *row_reference = NULL;
2038 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), FALSE);
2039 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
2041 if (!priv->row_reference)
2044 /* Update the next row reference if it's not valid. This could
2045 happen if for example the header which it was pointing to,
2046 was deleted. The best place to do it is in the row-deleted
2047 handler but the tinymail model do not work like the glib
2048 tree models and reports the deletion when the row is still
2050 if (!gtk_tree_row_reference_valid (priv->next_row_reference)) {
2051 if (priv->next_row_reference) {
2052 gtk_tree_row_reference_free (priv->next_row_reference);
2054 if (gtk_tree_row_reference_valid (priv->row_reference)) {
2055 priv->next_row_reference = gtk_tree_row_reference_copy (priv->row_reference);
2056 select_next_valid_row (priv->header_model, &(priv->next_row_reference), FALSE, priv->is_outbox);
2058 priv->next_row_reference = NULL;
2061 if (priv->next_row_reference)
2062 path = gtk_tree_row_reference_get_path (priv->next_row_reference);
2066 row_reference = gtk_tree_row_reference_copy (priv->next_row_reference);
2068 gtk_tree_model_get_iter (priv->header_model,
2071 gtk_tree_path_free (path);
2073 gtk_tree_model_get (priv->header_model, &tmp_iter,
2074 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
2077 /* Read the message & show it */
2078 if (!message_reader (window, priv, header, NULL, NULL, row_reference)) {
2081 gtk_tree_row_reference_free (row_reference);
2084 g_object_unref (header);
2090 modest_msg_view_window_select_previous_message (ModestMsgViewWindow *window)
2092 ModestMsgViewWindowPrivate *priv = NULL;
2094 gboolean finished = FALSE;
2095 gboolean retval = FALSE;
2097 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), FALSE);
2098 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
2100 if (priv->row_reference && !gtk_tree_row_reference_valid (priv->row_reference)) {
2101 gtk_tree_row_reference_free (priv->row_reference);
2102 priv->row_reference = NULL;
2105 /* Return inmediatly if there is no header model */
2106 if (!priv->header_model || !priv->row_reference)
2109 path = gtk_tree_row_reference_get_path (priv->row_reference);
2110 while (!finished && gtk_tree_path_prev (path)) {
2114 gtk_tree_model_get_iter (priv->header_model, &iter, path);
2115 gtk_tree_model_get (priv->header_model, &iter,
2116 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
2120 if (msg_is_visible (header, priv->is_outbox)) {
2121 GtkTreeRowReference *row_reference;
2122 row_reference = gtk_tree_row_reference_new (priv->header_model, path);
2123 /* Read the message & show it */
2124 retval = message_reader (window, priv, header, NULL, NULL, row_reference);
2125 gtk_tree_row_reference_free (row_reference);
2129 g_object_unref (header);
2133 gtk_tree_path_free (path);
2138 view_msg_cb (ModestMailOperation *mail_op,
2145 ModestMsgViewWindow *self = NULL;
2146 ModestMsgViewWindowPrivate *priv = NULL;
2147 GtkTreeRowReference *row_reference = NULL;
2149 /* Unregister the header (it was registered before creating the mail operation) */
2150 modest_window_mgr_unregister_header (modest_runtime_get_window_mgr (), header);
2152 row_reference = (GtkTreeRowReference *) user_data;
2155 gtk_tree_row_reference_free (row_reference);
2156 self = (ModestMsgViewWindow *) modest_mail_operation_get_source (mail_op);
2158 /* Restore window title */
2159 update_window_title (self);
2160 modest_ui_actions_on_close_window (NULL, MODEST_WINDOW (self));
2161 g_object_unref (self);
2166 /* If there was any error */
2167 if (!modest_ui_actions_msg_retrieval_check (mail_op, header, msg)) {
2169 gtk_tree_row_reference_free (row_reference);
2170 self = (ModestMsgViewWindow *) modest_mail_operation_get_source (mail_op);
2172 /* Restore window title */
2173 update_window_title (self);
2174 modest_ui_actions_on_close_window (NULL, MODEST_WINDOW (self));
2175 g_object_unref (self);
2180 /* Get the window */
2181 self = (ModestMsgViewWindow *) modest_mail_operation_get_source (mail_op);
2182 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self));
2183 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
2185 /* Update the row reference */
2186 if (priv->row_reference != NULL) {
2187 gtk_tree_row_reference_free (priv->row_reference);
2188 priv->row_reference = (row_reference && gtk_tree_row_reference_valid (row_reference))?gtk_tree_row_reference_copy (row_reference):NULL;
2189 if (priv->next_row_reference != NULL) {
2190 gtk_tree_row_reference_free (priv->next_row_reference);
2192 if (priv->row_reference) {
2193 priv->next_row_reference = gtk_tree_row_reference_copy (priv->row_reference);
2194 select_next_valid_row (priv->header_model, &(priv->next_row_reference), TRUE, priv->is_outbox);
2196 priv->next_row_reference = NULL;
2200 /* Mark header as read */
2201 if (!(tny_header_get_flags (header) & TNY_HEADER_FLAG_SEEN))
2202 tny_header_set_flag (header, TNY_HEADER_FLAG_SEEN);
2204 /* Set new message */
2205 if (priv->msg_view != NULL && TNY_IS_MSG_VIEW (priv->msg_view)) {
2206 tny_msg_view_set_msg (TNY_MSG_VIEW (priv->msg_view), msg);
2207 modest_msg_view_window_update_priority (self);
2208 update_window_title (MODEST_MSG_VIEW_WINDOW (self));
2209 update_branding (MODEST_MSG_VIEW_WINDOW (self));
2210 modest_msg_view_grab_focus (MODEST_MSG_VIEW (priv->msg_view));
2213 /* Set the new message uid of the window */
2214 if (priv->msg_uid) {
2215 g_free (priv->msg_uid);
2216 priv->msg_uid = modest_tny_folder_get_header_unique_id (header);
2219 /* Notify the observers */
2220 g_signal_emit (G_OBJECT (self), signals[MSG_CHANGED_SIGNAL],
2221 0, priv->header_model, priv->row_reference);
2224 g_object_unref (self);
2226 gtk_tree_row_reference_free (row_reference);
2230 modest_msg_view_window_get_folder_type (ModestMsgViewWindow *window)
2232 ModestMsgViewWindowPrivate *priv;
2234 TnyFolderType folder_type;
2236 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
2238 folder_type = TNY_FOLDER_TYPE_UNKNOWN;
2240 msg = tny_msg_view_get_msg (TNY_MSG_VIEW (priv->msg_view));
2244 folder = tny_msg_get_folder (msg);
2246 folder_type = modest_tny_folder_guess_folder_type (folder);
2247 g_object_unref (folder);
2249 g_object_unref (msg);
2257 modest_msg_view_window_update_priority (ModestMsgViewWindow *window)
2259 ModestMsgViewWindowPrivate *priv;
2260 TnyHeader *header = NULL;
2261 TnyHeaderFlags flags = 0;
2263 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
2265 if (priv->header_model && priv->row_reference && gtk_tree_row_reference_valid (priv->row_reference)) {
2267 GtkTreePath *path = NULL;
2269 path = gtk_tree_row_reference_get_path (priv->row_reference);
2270 g_return_if_fail (path != NULL);
2271 gtk_tree_model_get_iter (priv->header_model,
2273 gtk_tree_row_reference_get_path (priv->row_reference));
2275 gtk_tree_model_get (priv->header_model, &iter, TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
2277 gtk_tree_path_free (path);
2280 msg = tny_msg_view_get_msg (TNY_MSG_VIEW (priv->msg_view));
2282 header = tny_msg_get_header (msg);
2283 g_object_unref (msg);
2288 flags = tny_header_get_flags (header);
2289 g_object_unref(G_OBJECT(header));
2292 modest_msg_view_set_priority (MODEST_MSG_VIEW(priv->msg_view), flags);
2297 toolbar_resize (ModestMsgViewWindow *self)
2299 ModestMsgViewWindowPrivate *priv = NULL;
2300 ModestWindowPrivate *parent_priv = NULL;
2302 gint static_button_size;
2303 ModestWindowMgr *mgr;
2305 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self));
2306 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
2307 parent_priv = MODEST_WINDOW_GET_PRIVATE(self);
2309 mgr = modest_runtime_get_window_mgr ();
2310 static_button_size = modest_window_mgr_get_fullscreen_mode (mgr)?120:120;
2312 if (parent_priv->toolbar) {
2313 /* Set expandable and homogeneous tool buttons */
2314 widget = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/ToolbarMessageReply");
2315 gtk_tool_item_set_expand (GTK_TOOL_ITEM (widget), TRUE);
2316 gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (widget), TRUE);
2317 widget = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/ToolbarMessageReplyAll");
2318 gtk_tool_item_set_expand (GTK_TOOL_ITEM (widget), TRUE);
2319 gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (widget), TRUE);
2320 widget = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/ToolbarMessageForward");
2321 gtk_tool_item_set_expand (GTK_TOOL_ITEM (widget), TRUE);
2322 gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (widget), TRUE);
2323 widget = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/ToolbarDeleteMessage");
2324 gtk_tool_item_set_expand (GTK_TOOL_ITEM (widget), TRUE);
2325 gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (widget), TRUE);
2326 widget = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/ToolbarDownloadExternalImages");
2327 gtk_tool_item_set_expand (GTK_TOOL_ITEM (widget), TRUE);
2328 gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (widget), TRUE);
2329 gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (priv->next_toolitem), TRUE);
2330 gtk_tool_item_set_expand (GTK_TOOL_ITEM (priv->next_toolitem), TRUE);
2331 gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (priv->prev_toolitem), TRUE);
2332 gtk_tool_item_set_expand (GTK_TOOL_ITEM (priv->prev_toolitem), TRUE);
2337 modest_msg_view_window_show_toolbar (ModestWindow *self,
2338 gboolean show_toolbar)
2340 ModestMsgViewWindowPrivate *priv = NULL;
2341 ModestWindowPrivate *parent_priv;
2343 parent_priv = MODEST_WINDOW_GET_PRIVATE(self);
2344 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(self);
2346 /* Set optimized view status */
2347 priv->optimized_view = !show_toolbar;
2349 if (!parent_priv->toolbar) {
2350 parent_priv->toolbar = gtk_ui_manager_get_widget (parent_priv->ui_manager,
2352 gtk_toolbar_set_icon_size (GTK_TOOLBAR (parent_priv->toolbar), HILDON_ICON_SIZE_FINGER);
2353 gtk_widget_set_no_show_all (parent_priv->toolbar, TRUE);
2355 priv->next_toolitem = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/ToolbarMessageNext");
2356 priv->prev_toolitem = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/ToolbarMessageBack");
2357 toolbar_resize (MODEST_MSG_VIEW_WINDOW (self));
2360 hildon_window_add_toolbar (HILDON_WINDOW (self),
2361 GTK_TOOLBAR (parent_priv->toolbar));
2366 /* Quick hack: this prevents toolbar icons "dance" when progress bar show status is changed */
2367 /* TODO: resize mode migth be GTK_RESIZE_QUEUE, in order to avoid unneccesary shows */
2368 gtk_container_set_resize_mode (GTK_CONTAINER(parent_priv->toolbar), GTK_RESIZE_IMMEDIATE);
2370 gtk_widget_show (GTK_WIDGET (parent_priv->toolbar));
2371 if (modest_msg_view_window_transfer_mode_enabled (MODEST_MSG_VIEW_WINDOW (self)))
2372 set_progress_hint (MODEST_MSG_VIEW_WINDOW (self), TRUE);
2374 set_progress_hint (MODEST_MSG_VIEW_WINDOW (self), FALSE);
2377 gtk_widget_set_no_show_all (parent_priv->toolbar, TRUE);
2378 gtk_widget_hide (GTK_WIDGET (parent_priv->toolbar));
2383 modest_msg_view_window_clipboard_owner_change (GtkClipboard *clipboard,
2385 ModestMsgViewWindow *window)
2387 if (!GTK_WIDGET_VISIBLE (window))
2390 modest_window_check_dimming_rules_group (MODEST_WINDOW (window), MODEST_DIMMING_RULES_CLIPBOARD);
2394 modest_msg_view_window_transfer_mode_enabled (ModestMsgViewWindow *self)
2396 ModestMsgViewWindowPrivate *priv;
2398 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self), FALSE);
2399 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(self);
2401 return priv->progress_hint;
2405 observers_empty (ModestMsgViewWindow *self)
2408 ModestMsgViewWindowPrivate *priv;
2409 gboolean is_empty = TRUE;
2410 guint pending_ops = 0;
2412 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(self);
2413 tmp = priv->progress_widgets;
2415 /* Check all observers */
2416 while (tmp && is_empty) {
2417 pending_ops = modest_progress_object_num_pending_operations (MODEST_PROGRESS_OBJECT(tmp->data));
2418 is_empty = pending_ops == 0;
2420 tmp = g_slist_next(tmp);
2427 on_account_removed (TnyAccountStore *account_store,
2428 TnyAccount *account,
2431 /* Do nothing if it's a transport account, because we only
2432 show the messages of a store account */
2433 if (tny_account_get_account_type(account) == TNY_ACCOUNT_TYPE_STORE) {
2434 const gchar *parent_acc = NULL;
2435 const gchar *our_acc = NULL;
2437 our_acc = modest_window_get_active_account (MODEST_WINDOW (user_data));
2438 parent_acc = modest_tny_account_get_parent_modest_account_name_for_server_account (account);
2440 /* Close this window if I'm showing a message of the removed account */
2441 if (our_acc && parent_acc && strcmp (parent_acc, our_acc) == 0)
2442 modest_ui_actions_on_close_window (NULL, MODEST_WINDOW (user_data));
2447 on_mail_operation_started (ModestMailOperation *mail_op,
2450 ModestMsgViewWindow *self;
2451 ModestMailOperationTypeOperation op_type;
2453 ModestMsgViewWindowPrivate *priv;
2454 GObject *source = NULL;
2456 self = MODEST_MSG_VIEW_WINDOW (user_data);
2457 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
2458 op_type = modest_mail_operation_get_type_operation (mail_op);
2459 tmp = priv->progress_widgets;
2460 source = modest_mail_operation_get_source(mail_op);
2461 if (G_OBJECT (self) == source) {
2462 if (op_type == MODEST_MAIL_OPERATION_TYPE_RECEIVE ||
2463 op_type == MODEST_MAIL_OPERATION_TYPE_OPEN ||
2464 op_type == MODEST_MAIL_OPERATION_TYPE_DELETE) {
2465 set_progress_hint (self, TRUE);
2467 modest_progress_object_add_operation (
2468 MODEST_PROGRESS_OBJECT (tmp->data),
2470 tmp = g_slist_next (tmp);
2474 g_object_unref (source);
2476 /* Update dimming rules */
2477 check_dimming_rules_after_change (self);
2481 on_mail_operation_finished (ModestMailOperation *mail_op,
2484 ModestMsgViewWindow *self;
2485 ModestMailOperationTypeOperation op_type;
2487 ModestMsgViewWindowPrivate *priv;
2489 self = MODEST_MSG_VIEW_WINDOW (user_data);
2490 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
2491 op_type = modest_mail_operation_get_type_operation (mail_op);
2492 tmp = priv->progress_widgets;
2494 if (op_type == MODEST_MAIL_OPERATION_TYPE_RECEIVE ||
2495 op_type == MODEST_MAIL_OPERATION_TYPE_OPEN ||
2496 op_type == MODEST_MAIL_OPERATION_TYPE_DELETE) {
2498 modest_progress_object_remove_operation (MODEST_PROGRESS_OBJECT (tmp->data),
2500 tmp = g_slist_next (tmp);
2503 /* If no more operations are being observed, NORMAL mode is enabled again */
2504 if (observers_empty (self)) {
2505 set_progress_hint (self, FALSE);
2509 /* Update dimming rules. We have to do this right here
2510 and not in view_msg_cb because at that point the
2511 transfer mode is still enabled so the dimming rule
2512 won't let the user delete the message that has been
2513 readed for example */
2514 check_dimming_rules_after_change (self);
2518 on_queue_changed (ModestMailOperationQueue *queue,
2519 ModestMailOperation *mail_op,
2520 ModestMailOperationQueueNotification type,
2521 ModestMsgViewWindow *self)
2523 ModestMsgViewWindowPrivate *priv;
2525 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
2527 /* If this operations was created by another window, do nothing */
2528 if (!modest_mail_operation_is_mine (mail_op, G_OBJECT(self)))
2531 if (type == MODEST_MAIL_OPERATION_QUEUE_OPERATION_ADDED) {
2532 priv->sighandlers = modest_signal_mgr_connect (priv->sighandlers,
2534 "operation-started",
2535 G_CALLBACK (on_mail_operation_started),
2537 priv->sighandlers = modest_signal_mgr_connect (priv->sighandlers,
2539 "operation-finished",
2540 G_CALLBACK (on_mail_operation_finished),
2542 } else if (type == MODEST_MAIL_OPERATION_QUEUE_OPERATION_REMOVED) {
2543 priv->sighandlers = modest_signal_mgr_disconnect (priv->sighandlers,
2545 "operation-started");
2546 priv->sighandlers = modest_signal_mgr_disconnect (priv->sighandlers,
2548 "operation-finished");
2553 modest_msg_view_window_get_attachments (ModestMsgViewWindow *win)
2555 ModestMsgViewWindowPrivate *priv;
2556 TnyList *selected_attachments = NULL;
2558 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (win), NULL);
2559 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (win);
2561 /* In Hildon 2.2 as there's no selection we assume we have all attachments selected */
2562 selected_attachments = modest_msg_view_get_attachments (MODEST_MSG_VIEW (priv->msg_view));
2564 return selected_attachments;
2568 ModestMsgViewWindow *self;
2570 gchar *attachment_uid;
2571 } DecodeAsyncHelper;
2574 on_decode_to_stream_async_handler (TnyMimePart *mime_part,
2580 DecodeAsyncHelper *helper = (DecodeAsyncHelper *) user_data;
2581 const gchar *content_type;
2583 /* It could happen that the window was closed */
2584 if (GTK_WIDGET_VISIBLE (helper->self))
2585 set_progress_hint (helper->self, FALSE);
2587 if (cancelled || err) {
2590 if ((err->domain == TNY_ERROR_DOMAIN) &&
2591 (err->code == TNY_IO_ERROR_WRITE) &&
2592 (errno == ENOSPC)) {
2593 msg = g_strdup_printf (_KR("cerm_device_memory_full"), "");
2595 msg = g_strdup (_("mail_ib_file_operation_failed"));
2597 modest_platform_information_banner (NULL, NULL, msg);
2603 content_type = tny_mime_part_get_content_type (mime_part);
2604 if (g_str_has_prefix (content_type, "message/rfc822")) {
2605 ModestWindowMgr *mgr;
2606 ModestWindow *msg_win = NULL;
2609 const gchar *mailbox;
2610 TnyStream *file_stream;
2613 fd = g_open (helper->file_path, O_RDONLY, 0644);
2615 file_stream = tny_fs_stream_new (fd);
2617 mgr = modest_runtime_get_window_mgr ();
2619 account = g_strdup (modest_window_get_active_account (MODEST_WINDOW (helper->self)));
2620 mailbox = modest_window_get_active_mailbox (MODEST_WINDOW (helper->self));
2623 account = modest_account_mgr_get_default_account (modest_runtime_get_account_mgr ());
2625 msg = tny_camel_msg_new ();
2626 tny_camel_msg_parse (msg, file_stream);
2627 msg_win = modest_msg_view_window_new_for_attachment (TNY_MSG (msg), account, mailbox, helper->attachment_uid);
2628 modest_window_set_zoom (MODEST_WINDOW (msg_win),
2629 modest_window_get_zoom (MODEST_WINDOW (helper->self)));
2630 if (modest_window_mgr_register_window (mgr, msg_win, MODEST_WINDOW (helper->self)))
2631 gtk_widget_show_all (GTK_WIDGET (msg_win));
2633 gtk_widget_destroy (GTK_WIDGET (msg_win));
2634 g_object_unref (msg);
2635 g_object_unref (file_stream);
2637 modest_platform_information_banner (NULL, NULL, _("mail_ib_file_operation_failed"));
2642 /* make the file read-only */
2643 g_chmod(helper->file_path, 0444);
2645 /* Activate the file */
2646 modest_platform_activate_file (helper->file_path, tny_mime_part_get_content_type (mime_part));
2651 g_object_unref (helper->self);
2652 g_free (helper->file_path);
2653 g_free (helper->attachment_uid);
2654 g_slice_free (DecodeAsyncHelper, helper);
2658 modest_msg_view_window_view_attachment (ModestMsgViewWindow *window,
2659 TnyMimePart *mime_part)
2661 ModestMsgViewWindowPrivate *priv;
2662 const gchar *msg_uid;
2663 gchar *attachment_uid = NULL;
2664 gint attachment_index = 0;
2665 TnyList *attachments;
2667 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window));
2668 g_return_if_fail (TNY_IS_MIME_PART (mime_part) || (mime_part == NULL));
2669 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
2671 msg_uid = modest_msg_view_window_get_message_uid (MODEST_MSG_VIEW_WINDOW (window));
2672 attachments = modest_msg_view_get_attachments (MODEST_MSG_VIEW (priv->msg_view));
2673 attachment_index = modest_list_index (attachments, (GObject *) mime_part);
2674 g_object_unref (attachments);
2676 if (msg_uid && attachment_index >= 0) {
2677 attachment_uid = g_strdup_printf ("%s/%d", msg_uid, attachment_index);
2680 if (mime_part == NULL) {
2681 gboolean error = FALSE;
2682 TnyList *selected_attachments = modest_msg_view_get_selected_attachments (MODEST_MSG_VIEW (priv->msg_view));
2683 if (selected_attachments == NULL || tny_list_get_length (selected_attachments) == 0) {
2685 } else if (tny_list_get_length (selected_attachments) > 1) {
2686 hildon_banner_show_information (NULL, NULL, _("mcen_ib_unable_to_display_more"));
2690 iter = tny_list_create_iterator (selected_attachments);
2691 mime_part = (TnyMimePart *) tny_iterator_get_current (iter);
2692 g_object_unref (iter);
2694 if (selected_attachments)
2695 g_object_unref (selected_attachments);
2700 g_object_ref (mime_part);
2703 if (tny_mime_part_is_purged (mime_part))
2706 if (!modest_tny_mime_part_is_msg (mime_part) && tny_mime_part_get_filename (mime_part)) {
2707 gchar *filepath = NULL;
2708 const gchar *att_filename = tny_mime_part_get_filename (mime_part);
2709 gboolean show_error_banner = FALSE;
2710 TnyFsStream *temp_stream = NULL;
2711 temp_stream = modest_utils_create_temp_stream (att_filename, attachment_uid,
2714 if (temp_stream != NULL) {
2715 ModestAccountMgr *mgr;
2716 DecodeAsyncHelper *helper;
2717 gboolean decode_in_provider;
2718 ModestProtocol *protocol;
2719 const gchar *account;
2721 /* Activate progress hint */
2722 set_progress_hint (window, TRUE);
2724 helper = g_slice_new0 (DecodeAsyncHelper);
2725 helper->self = g_object_ref (window);
2726 helper->file_path = g_strdup (filepath);
2727 helper->attachment_uid = g_strdup (attachment_uid);
2729 decode_in_provider = FALSE;
2730 mgr = modest_runtime_get_account_mgr ();
2731 account = modest_window_get_active_account (MODEST_WINDOW (window));
2732 if (modest_account_mgr_account_is_multimailbox (mgr, account, &protocol)) {
2733 if (MODEST_IS_ACCOUNT_PROTOCOL (protocol)) {
2735 uri = g_strconcat ("file://", filepath, NULL);
2736 decode_in_provider =
2737 modest_account_protocol_decode_part_to_stream_async (
2738 MODEST_ACCOUNT_PROTOCOL (protocol),
2741 TNY_STREAM (temp_stream),
2742 on_decode_to_stream_async_handler,
2749 if (!decode_in_provider)
2750 tny_mime_part_decode_to_stream_async (mime_part, TNY_STREAM (temp_stream),
2751 on_decode_to_stream_async_handler,
2754 g_object_unref (temp_stream);
2755 /* NOTE: files in the temporary area will be automatically
2756 * cleaned after some time if they are no longer in use */
2759 const gchar *content_type;
2760 /* the file may already exist but it isn't writable,
2761 * let's try to open it anyway */
2762 content_type = tny_mime_part_get_content_type (mime_part);
2763 modest_platform_activate_file (filepath, content_type);
2765 g_warning ("%s: modest_utils_create_temp_stream failed", __FUNCTION__);
2766 show_error_banner = TRUE;
2771 if (show_error_banner)
2772 modest_platform_information_banner (NULL, NULL, _("mail_ib_file_operation_failed"));
2773 } else if (!modest_tny_mime_part_is_msg (mime_part)) {
2774 ModestWindowMgr *mgr;
2775 ModestWindow *msg_win = NULL;
2776 TnyMsg *current_msg;
2780 current_msg = modest_msg_view_window_get_message (MODEST_MSG_VIEW_WINDOW (window));
2781 mgr = modest_runtime_get_window_mgr ();
2782 header = tny_msg_get_header (TNY_MSG (current_msg));
2783 found = modest_window_mgr_find_registered_message_uid (mgr,
2788 g_debug ("window for this body is already being created");
2791 /* it's not found, so create a new window for it */
2792 modest_window_mgr_register_header (mgr, header, attachment_uid); /* register the uid before building the window */
2793 gchar *account = g_strdup (modest_window_get_active_account (MODEST_WINDOW (window)));
2794 const gchar *mailbox = modest_window_get_active_mailbox (MODEST_WINDOW (window));
2796 account = modest_account_mgr_get_default_account (modest_runtime_get_account_mgr ());
2798 msg_win = modest_msg_view_window_new_with_other_body (TNY_MSG (current_msg), TNY_MIME_PART (mime_part),
2799 account, mailbox, attachment_uid);
2801 modest_window_set_zoom (MODEST_WINDOW (msg_win),
2802 modest_window_get_zoom (MODEST_WINDOW (window)));
2803 if (modest_window_mgr_register_window (mgr, msg_win, MODEST_WINDOW (window)))
2804 gtk_widget_show_all (GTK_WIDGET (msg_win));
2806 gtk_widget_destroy (GTK_WIDGET (msg_win));
2808 g_object_unref (current_msg);
2810 /* message attachment */
2811 TnyHeader *header = NULL;
2812 ModestWindowMgr *mgr;
2813 ModestWindow *msg_win = NULL;
2816 header = tny_msg_get_header (TNY_MSG (mime_part));
2817 mgr = modest_runtime_get_window_mgr ();
2818 found = modest_window_mgr_find_registered_header (mgr, header, &msg_win);
2821 /* if it's found, but there is no msg_win, it's probably in the process of being created;
2822 * thus, we don't do anything */
2823 g_debug ("window for is already being created");
2825 /* it's not found, so create a new window for it */
2826 modest_window_mgr_register_header (mgr, header, attachment_uid); /* register the uid before building the window */
2827 gchar *account = g_strdup (modest_window_get_active_account (MODEST_WINDOW (window)));
2828 const gchar *mailbox = modest_window_get_active_mailbox (MODEST_WINDOW (window));
2830 account = modest_account_mgr_get_default_account (modest_runtime_get_account_mgr ());
2831 msg_win = modest_msg_view_window_new_for_attachment (TNY_MSG (mime_part), account,
2832 mailbox, attachment_uid);
2833 modest_window_set_zoom (MODEST_WINDOW (msg_win),
2834 modest_window_get_zoom (MODEST_WINDOW (window)));
2835 if (modest_window_mgr_register_window (mgr, msg_win, MODEST_WINDOW (window)))
2836 gtk_widget_show_all (GTK_WIDGET (msg_win));
2838 gtk_widget_destroy (GTK_WIDGET (msg_win));
2844 g_free (attachment_uid);
2846 g_object_unref (mime_part);
2858 GnomeVFSResult result;
2860 ModestMsgViewWindow *window;
2863 static void save_mime_part_info_free (SaveMimePartInfo *info, gboolean with_struct);
2864 static gboolean idle_save_mime_part_show_result (SaveMimePartInfo *info);
2865 static gpointer save_mime_part_to_file (SaveMimePartInfo *info);
2866 static void save_mime_parts_to_file_with_checks (GtkWindow *parent, SaveMimePartInfo *info);
2869 save_mime_part_info_free (SaveMimePartInfo *info, gboolean with_struct)
2873 for (node = info->pairs; node != NULL; node = g_list_next (node)) {
2874 SaveMimePartPair *pair = (SaveMimePartPair *) node->data;
2875 g_free (pair->filename);
2876 g_object_unref (pair->part);
2877 g_slice_free (SaveMimePartPair, pair);
2879 g_list_free (info->pairs);
2882 g_object_unref (info->window);
2883 info->window = NULL;
2885 g_slice_free (SaveMimePartInfo, info);
2890 idle_save_mime_part_show_result (SaveMimePartInfo *info)
2892 /* This is a GDK lock because we are an idle callback and
2893 * hildon_banner_show_information is or does Gtk+ code */
2895 gdk_threads_enter (); /* CHECKED */
2896 if (info->result == GNOME_VFS_OK) {
2897 hildon_banner_show_information (NULL, NULL, _CS("sfil_ib_saved"));
2898 } else if (info->result == GNOME_VFS_ERROR_NO_SPACE) {
2901 /* Check if the uri belongs to the external mmc */
2902 if (g_str_has_prefix (info->uri, g_getenv (MODEST_MMC1_VOLUMEPATH_ENV)))
2903 msg = g_strdup_printf (_KR("cerm_device_memory_full"), "");
2905 msg = g_strdup (_KR("cerm_memory_card_full"));
2906 modest_platform_information_banner (NULL, NULL, msg);
2909 hildon_banner_show_information (NULL, NULL, _("mail_ib_file_operation_failed"));
2911 save_mime_part_info_free (info, FALSE);
2912 gdk_threads_leave (); /* CHECKED */
2918 save_mime_part_to_file (SaveMimePartInfo *info)
2920 GnomeVFSHandle *handle;
2922 SaveMimePartPair *pair = (SaveMimePartPair *) info->pairs->data;
2924 info->result = gnome_vfs_create (&handle, pair->filename, GNOME_VFS_OPEN_WRITE, FALSE, 0644);
2925 if (info->result == GNOME_VFS_OK) {
2926 GError *error = NULL;
2927 gboolean decode_in_provider;
2929 ModestAccountMgr *mgr;
2930 const gchar *account;
2931 ModestProtocol *protocol = NULL;
2933 stream = tny_vfs_stream_new (handle);
2935 decode_in_provider = FALSE;
2936 mgr = modest_runtime_get_account_mgr ();
2937 account = modest_window_get_active_account (MODEST_WINDOW (info->window));
2938 if (modest_account_mgr_account_is_multimailbox (mgr, account, &protocol)) {
2939 if (MODEST_IS_ACCOUNT_PROTOCOL (protocol)) {
2940 decode_in_provider =
2941 modest_account_protocol_decode_part_to_stream (
2942 MODEST_ACCOUNT_PROTOCOL (protocol),
2950 if (!decode_in_provider)
2951 written = tny_mime_part_decode_to_stream (pair->part, stream, &error);
2954 g_warning ("modest: could not save attachment %s: %d (%s)\n", pair->filename, error?error->code:-1, error?error->message:"Unknown error");
2956 if ((error->domain == TNY_ERROR_DOMAIN) &&
2957 (error->code == TNY_IO_ERROR_WRITE) &&
2958 (errno == ENOSPC)) {
2959 info->result = GNOME_VFS_ERROR_NO_SPACE;
2961 info->result = GNOME_VFS_ERROR_IO;
2964 g_object_unref (G_OBJECT (stream));
2966 g_warning ("Could not create save attachment %s: %s\n",
2967 pair->filename, gnome_vfs_result_to_string (info->result));
2970 /* Go on saving remaining files */
2971 info->pairs = g_list_remove_link (info->pairs, info->pairs);
2972 if (info->pairs != NULL) {
2973 save_mime_part_to_file (info);
2975 g_idle_add ((GSourceFunc) idle_save_mime_part_show_result, info);
2982 save_mime_parts_to_file_with_checks (GtkWindow *parent,
2983 SaveMimePartInfo *info)
2985 gboolean is_ok = TRUE;
2986 gint replaced_files = 0;
2987 const GList *files = info->pairs;
2988 const GList *iter, *to_replace = NULL;
2990 for (iter = files; (iter != NULL) && (replaced_files < 2); iter = g_list_next(iter)) {
2991 SaveMimePartPair *pair = iter->data;
2992 gchar *unescaped = g_uri_unescape_string (pair->filename, NULL);
2994 if (modest_utils_file_exists (unescaped)) {
2996 if (replaced_files == 1)
3001 if (replaced_files) {
3004 if (replaced_files == 1) {
3005 SaveMimePartPair *pair = to_replace->data;
3006 const gchar *basename = strrchr (pair->filename, G_DIR_SEPARATOR) + 1;
3007 gchar *escaped_basename, *message;
3009 escaped_basename = g_uri_unescape_string (basename, NULL);
3010 message = g_strdup_printf ("%s\n%s",
3011 _FM("docm_nc_replace_file"),
3012 (escaped_basename) ? escaped_basename : "");
3013 response = modest_platform_run_confirmation_dialog (parent, message);
3015 g_free (escaped_basename);
3017 response = modest_platform_run_confirmation_dialog (parent,
3018 _FM("docm_nc_replace_multiple"));
3020 if (response != GTK_RESPONSE_OK)
3025 save_mime_part_info_free (info, TRUE);
3027 g_thread_create ((GThreadFunc)save_mime_part_to_file, info, FALSE, NULL);
3032 typedef struct _SaveAttachmentsInfo {
3033 TnyList *attachments_list;
3034 ModestMsgViewWindow *window;
3035 } SaveAttachmentsInfo;
3038 save_attachments_response (GtkDialog *dialog,
3042 TnyList *mime_parts;
3044 GList *files_to_save = NULL;
3045 gchar *current_folder;
3046 SaveAttachmentsInfo *sa_info = (SaveAttachmentsInfo *) user_data;
3048 mime_parts = TNY_LIST (sa_info->attachments_list);
3050 if (arg1 != GTK_RESPONSE_OK)
3053 chooser_uri = gtk_file_chooser_get_uri (GTK_FILE_CHOOSER (dialog));
3054 current_folder = gtk_file_chooser_get_current_folder_uri (GTK_FILE_CHOOSER (dialog));
3055 if (current_folder && *current_folder != '\0') {
3057 modest_conf_set_string (modest_runtime_get_conf (), MODEST_CONF_LATEST_SAVE_ATTACHMENT_PATH,
3058 current_folder,&err);
3060 g_debug ("Error storing latest used folder: %s", err->message);
3064 g_free (current_folder);
3066 if (!modest_utils_folder_writable (chooser_uri)) {
3067 const gchar *err_msg;
3069 #ifdef MODEST_PLATFORM_MAEMO
3070 if (modest_maemo_utils_in_usb_mode ()) {
3071 err_msg = dgettext ("hildon-status-bar-usb", "usbh_ib_mmc_usb_connected");
3073 err_msg = _FM("sfil_ib_readonly_location");
3076 err_msg = _FM("sfil_ib_readonly_location");
3078 hildon_banner_show_information (NULL, NULL, err_msg);
3082 iter = tny_list_create_iterator (mime_parts);
3083 while (!tny_iterator_is_done (iter)) {
3084 TnyMimePart *mime_part = (TnyMimePart *) tny_iterator_get_current (iter);
3086 if ((modest_tny_mime_part_is_attachment_for_modest (mime_part)) &&
3087 !tny_mime_part_is_purged (mime_part) &&
3088 (tny_mime_part_get_filename (mime_part) != NULL)) {
3089 SaveMimePartPair *pair;
3091 pair = g_slice_new0 (SaveMimePartPair);
3093 if (tny_list_get_length (mime_parts) > 1) {
3095 gnome_vfs_escape_slashes (tny_mime_part_get_filename (mime_part));
3096 pair->filename = g_build_filename (chooser_uri, escaped, NULL);
3099 pair->filename = g_strdup (chooser_uri);
3101 pair->part = mime_part;
3102 files_to_save = g_list_prepend (files_to_save, pair);
3104 tny_iterator_next (iter);
3106 g_object_unref (iter);
3109 if (files_to_save != NULL) {
3110 SaveMimePartInfo *info = g_slice_new0 (SaveMimePartInfo);
3111 info->pairs = files_to_save;
3112 info->result = TRUE;
3113 info->uri = g_strdup (chooser_uri);
3114 info->window = g_object_ref (sa_info->window);
3115 save_mime_parts_to_file_with_checks ((GtkWindow *) dialog, info);
3117 g_free (chooser_uri);
3120 /* Free and close the dialog */
3121 g_object_unref (mime_parts);
3122 g_object_unref (sa_info->window);
3123 g_slice_free (SaveAttachmentsInfo, sa_info);
3124 gtk_widget_destroy (GTK_WIDGET (dialog));
3128 modest_msg_view_window_save_attachments (ModestMsgViewWindow *window,
3129 TnyList *mime_parts)
3131 ModestMsgViewWindowPrivate *priv;
3132 GtkWidget *save_dialog = NULL;
3133 gchar *conf_folder = NULL;
3134 gchar *filename = NULL;
3135 gchar *save_multiple_str = NULL;
3136 const gchar *root_folder = "file:///";
3138 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window));
3139 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
3141 if (mime_parts == NULL) {
3142 /* In Hildon 2.2 save and delete operate over all the attachments as there's no
3143 * selection available */
3144 mime_parts = modest_msg_view_get_attachments (MODEST_MSG_VIEW (priv->msg_view));
3145 if (mime_parts && !modest_maemo_utils_select_attachments (GTK_WINDOW (window), mime_parts, FALSE)) {
3146 g_object_unref (mime_parts);
3149 if (mime_parts == NULL || tny_list_get_length (mime_parts) == 0) {
3151 g_object_unref (mime_parts);
3157 g_object_ref (mime_parts);
3160 /* prepare dialog */
3161 if (tny_list_get_length (mime_parts) == 1) {
3163 /* only one attachment selected */
3164 iter = tny_list_create_iterator (mime_parts);
3165 TnyMimePart *mime_part = (TnyMimePart *) tny_iterator_get_current (iter);
3166 g_object_unref (iter);
3167 if (!modest_tny_mime_part_is_msg (mime_part) &&
3168 modest_tny_mime_part_is_attachment_for_modest (mime_part) &&
3169 !tny_mime_part_is_purged (mime_part)) {
3170 filename = g_strdup (tny_mime_part_get_filename (mime_part));
3172 /* TODO: show any error? */
3173 g_warning ("%s: Tried to save a non-file attachment", __FUNCTION__);
3174 g_object_unref (mime_parts);
3177 g_object_unref (mime_part);
3179 gint num = tny_list_get_length (mime_parts);
3180 save_multiple_str = g_strdup_printf (dngettext("hildon-fm",
3181 "sfil_va_number_of_objects_attachment",
3182 "sfil_va_number_of_objects_attachments",
3186 save_dialog = hildon_file_chooser_dialog_new (GTK_WINDOW (window),
3187 GTK_FILE_CHOOSER_ACTION_SAVE);
3189 /* Get last used folder */
3190 conf_folder = modest_conf_get_string (modest_runtime_get_conf (),
3191 MODEST_CONF_LATEST_SAVE_ATTACHMENT_PATH, NULL);
3193 /* File chooser stops working if we select "file:///" as current folder */
3194 if (conf_folder && g_ascii_strcasecmp (root_folder, conf_folder) != 0) {
3195 g_free (conf_folder);
3199 if (conf_folder && conf_folder[0] != '\0') {
3200 gtk_file_chooser_set_current_folder_uri (GTK_FILE_CHOOSER (save_dialog), conf_folder);
3203 /* Set the default folder to documents folder */
3204 docs_folder = (gchar *) g_strdup(g_get_user_special_dir (G_USER_DIRECTORY_DOCUMENTS));
3207 docs_folder = g_build_filename (g_getenv (MYDOCS_ENV), DOCS_FOLDER, NULL);
3209 gtk_file_chooser_set_current_folder (GTK_FILE_CHOOSER (save_dialog), docs_folder);
3210 g_free (docs_folder);
3212 g_free (conf_folder);
3216 gtk_file_chooser_set_current_name (GTK_FILE_CHOOSER (save_dialog),
3221 /* if multiple, set multiple string */
3222 if (save_multiple_str) {
3223 g_object_set (G_OBJECT (save_dialog), "save-multiple", save_multiple_str, NULL);
3224 gtk_window_set_title (GTK_WINDOW (save_dialog), _FM("sfil_ti_save_objects_files"));
3225 g_free (save_multiple_str);
3228 /* We must run this asynchronously, because the hildon dialog
3229 performs a gtk_dialog_run by itself which leads to gdk
3231 SaveAttachmentsInfo *sa_info;
3232 sa_info = g_slice_new (SaveAttachmentsInfo);
3233 sa_info->attachments_list = mime_parts;
3234 sa_info->window = g_object_ref (window);
3235 g_signal_connect (save_dialog, "response",
3236 G_CALLBACK (save_attachments_response), sa_info);
3238 gtk_widget_show_all (save_dialog);
3242 show_remove_attachment_information (gpointer userdata)
3244 ModestMsgViewWindow *window = (ModestMsgViewWindow *) userdata;
3245 ModestMsgViewWindowPrivate *priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
3247 /* We're outside the main lock */
3248 gdk_threads_enter ();
3250 if (priv->remove_attachment_banner != NULL) {
3251 gtk_widget_destroy (priv->remove_attachment_banner);
3252 g_object_unref (priv->remove_attachment_banner);
3255 priv->remove_attachment_banner = g_object_ref (
3256 hildon_banner_show_animation (NULL, NULL, _("mcen_me_inbox_remove_attachments")));
3258 gdk_threads_leave ();
3264 modest_msg_view_window_remove_attachments (ModestMsgViewWindow *window, gboolean get_all)
3266 ModestMsgViewWindowPrivate *priv;
3267 TnyList *mime_parts = NULL, *tmp;
3268 gchar *confirmation_message;
3274 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window));
3275 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
3277 /* In hildon 2.2 we ignore the get_all flag as we always get all attachments. This is
3278 * because we don't have selection
3280 mime_parts = modest_msg_view_get_attachments (MODEST_MSG_VIEW (priv->msg_view));
3282 /* Remove already purged messages from mime parts list. We use
3283 a copy of the list to remove items in the original one */
3284 tmp = tny_list_copy (mime_parts);
3285 iter = tny_list_create_iterator (tmp);
3286 while (!tny_iterator_is_done (iter)) {
3287 TnyMimePart *part = TNY_MIME_PART (tny_iterator_get_current (iter));
3288 if (tny_mime_part_is_purged (part))
3289 tny_list_remove (mime_parts, (GObject *) part);
3291 g_object_unref (part);
3292 tny_iterator_next (iter);
3294 g_object_unref (tmp);
3295 g_object_unref (iter);
3297 if (!modest_maemo_utils_select_attachments (GTK_WINDOW (window), mime_parts, TRUE) ||
3298 tny_list_get_length (mime_parts) == 0) {
3299 g_object_unref (mime_parts);
3303 n_attachments = tny_list_get_length (mime_parts);
3304 if (n_attachments == 1) {
3308 iter = tny_list_create_iterator (mime_parts);
3309 part = (TnyMimePart *) tny_iterator_get_current (iter);
3310 g_object_unref (iter);
3311 if (modest_tny_mime_part_is_msg (part)) {
3313 header = tny_msg_get_header (TNY_MSG (part));
3314 filename = tny_header_dup_subject (header);
3315 g_object_unref (header);
3316 if (filename == NULL)
3317 filename = g_strdup (_("mail_va_no_subject"));
3319 filename = g_strdup (tny_mime_part_get_filename (TNY_MIME_PART (part)));
3321 confirmation_message = g_strdup_printf (_("mcen_nc_purge_file_text"), filename);
3323 g_object_unref (part);
3325 confirmation_message = g_strdup_printf (ngettext("mcen_nc_purge_file_text",
3326 "mcen_nc_purge_files_text",
3327 n_attachments), n_attachments);
3329 response = modest_platform_run_confirmation_dialog (GTK_WINDOW (window),
3330 confirmation_message);
3331 g_free (confirmation_message);
3333 if (response != GTK_RESPONSE_OK) {
3334 g_object_unref (mime_parts);
3338 priv->purge_timeout = g_timeout_add (2000, show_remove_attachment_information, window);
3340 iter = tny_list_create_iterator (mime_parts);
3341 while (!tny_iterator_is_done (iter)) {
3344 part = (TnyMimePart *) tny_iterator_get_current (iter);
3345 tny_mime_part_set_purged (TNY_MIME_PART (part));
3346 g_object_unref (part);
3347 tny_iterator_next (iter);
3349 g_object_unref (iter);
3351 msg = tny_msg_view_get_msg (TNY_MSG_VIEW (priv->msg_view));
3352 tny_msg_view_clear (TNY_MSG_VIEW (priv->msg_view));
3353 tny_msg_rewrite_cache (msg);
3354 tny_msg_view_set_msg (TNY_MSG_VIEW (priv->msg_view), msg);
3355 g_object_unref (msg);
3356 update_branding (MODEST_MSG_VIEW_WINDOW (window));
3358 g_object_unref (mime_parts);
3360 if (priv->purge_timeout > 0) {
3361 g_source_remove (priv->purge_timeout);
3362 priv->purge_timeout = 0;
3365 if (priv->remove_attachment_banner) {
3366 gtk_widget_destroy (priv->remove_attachment_banner);
3367 g_object_unref (priv->remove_attachment_banner);
3368 priv->remove_attachment_banner = NULL;
3374 update_window_title (ModestMsgViewWindow *window)
3376 ModestMsgViewWindowPrivate *priv;
3378 TnyHeader *header = NULL;
3379 gchar *subject = NULL;
3381 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
3383 /* Note that if the window is closed while we're retrieving
3384 the message, this widget could de deleted */
3385 if (!priv->msg_view)
3388 msg = tny_msg_view_get_msg (TNY_MSG_VIEW (priv->msg_view));
3390 if (priv->other_body) {
3393 description = modest_tny_mime_part_get_header_value (priv->other_body, "Content-Description");
3395 g_strstrip (description);
3396 subject = description;
3398 } else if (msg != NULL) {
3399 header = tny_msg_get_header (msg);
3400 subject = tny_header_dup_subject (header);
3401 g_object_unref (header);
3402 g_object_unref (msg);
3405 if ((subject == NULL)||(subject[0] == '\0')) {
3407 subject = g_strdup (_("mail_va_no_subject"));
3410 gtk_window_set_title (GTK_WINDOW (window), subject);
3415 on_move_focus (GtkWidget *widget,
3416 GtkDirectionType direction,
3419 g_signal_stop_emission_by_name (G_OBJECT (widget), "move-focus");
3423 fetch_image_open_stream (TnyStreamCache *self, gint64 *expected_size, gchar *uri)
3425 GnomeVFSResult result;
3426 GnomeVFSHandle *handle = NULL;
3427 GnomeVFSFileInfo *info = NULL;
3430 result = gnome_vfs_open (&handle, uri, GNOME_VFS_OPEN_READ);
3431 if (result != GNOME_VFS_OK) {
3436 info = gnome_vfs_file_info_new ();
3437 result = gnome_vfs_get_file_info_from_handle (handle, info, GNOME_VFS_FILE_INFO_DEFAULT);
3438 if (result != GNOME_VFS_OK || ! (info->valid_fields & GNOME_VFS_FILE_INFO_FIELDS_SIZE)) {
3439 /* We put a "safe" default size for going to cache */
3440 *expected_size = (300*1024);
3442 *expected_size = info->size;
3444 gnome_vfs_file_info_unref (info);
3446 stream = tny_vfs_stream_new (handle);
3455 TnyStream *output_stream;
3456 GtkWidget *msg_view;
3461 on_fetch_image_idle_refresh_view (gpointer userdata)
3464 FetchImageData *fidata = (FetchImageData *) userdata;
3466 gdk_threads_enter ();
3467 if (GTK_WIDGET_DRAWABLE (fidata->msg_view)) {
3468 ModestMsgViewWindowPrivate *priv;
3470 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (fidata->window);
3471 priv->fetching_images--;
3472 gtk_widget_queue_draw (fidata->msg_view);
3473 update_progress_hint (MODEST_MSG_VIEW_WINDOW (fidata->window));
3475 gdk_threads_leave ();
3477 g_object_unref (fidata->msg_view);
3478 g_object_unref (fidata->window);
3479 g_slice_free (FetchImageData, fidata);
3484 on_fetch_image_thread (gpointer userdata)
3486 FetchImageData *fidata = (FetchImageData *) userdata;
3487 TnyStreamCache *cache;
3488 TnyStream *cache_stream;
3490 cache = modest_runtime_get_images_cache ();
3492 tny_stream_cache_get_stream (cache,
3494 (TnyStreamCacheOpenStreamFetcher) fetch_image_open_stream,
3495 (gpointer) fidata->uri);
3496 g_free (fidata->cache_id);
3497 g_free (fidata->uri);
3499 if (cache_stream != NULL) {
3502 while (G_LIKELY (!tny_stream_is_eos (cache_stream))) {
3505 nb_read = tny_stream_read (cache_stream, buffer, sizeof (buffer));
3506 if (G_UNLIKELY (nb_read < 0)) {
3508 } else if (G_LIKELY (nb_read > 0)) {
3509 gssize nb_written = 0;
3511 while (G_UNLIKELY (nb_written < nb_read)) {
3514 len = tny_stream_write (fidata->output_stream, buffer + nb_written,
3515 nb_read - nb_written);
3516 if (G_UNLIKELY (len < 0))
3522 tny_stream_close (cache_stream);
3523 g_object_unref (cache_stream);
3526 tny_stream_close (fidata->output_stream);
3527 g_object_unref (fidata->output_stream);
3529 g_idle_add (on_fetch_image_idle_refresh_view, fidata);
3535 on_fetch_image (ModestMsgView *msgview,
3538 ModestMsgViewWindow *window)
3540 const gchar *current_account;
3541 ModestMsgViewWindowPrivate *priv;
3542 FetchImageData *fidata;
3544 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
3546 current_account = modest_window_get_active_account (MODEST_WINDOW (window));
3548 fidata = g_slice_new0 (FetchImageData);
3549 fidata->msg_view = g_object_ref (msgview);
3550 fidata->window = g_object_ref (window);
3551 fidata->uri = g_strdup (uri);
3552 fidata->cache_id = modest_images_cache_get_id (current_account, uri);
3553 fidata->output_stream = g_object_ref (stream);
3555 priv->fetching_images++;
3556 if (g_thread_create (on_fetch_image_thread, fidata, FALSE, NULL) == NULL) {
3557 g_object_unref (fidata->output_stream);
3558 g_free (fidata->cache_id);
3559 g_free (fidata->uri);
3560 g_object_unref (fidata->msg_view);
3561 g_slice_free (FetchImageData, fidata);
3562 tny_stream_close (stream);
3563 priv->fetching_images--;
3564 update_progress_hint (window);
3567 update_progress_hint (window);
3573 setup_menu (ModestMsgViewWindow *self)
3575 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW(self));
3577 /* Settings menu buttons */
3578 modest_hildon2_window_add_to_menu (MODEST_HILDON2_WINDOW (self), _("mcen_me_viewer_find"), NULL,
3579 APP_MENU_CALLBACK (modest_msg_view_window_show_find_toolbar),
3580 MODEST_DIMMING_CALLBACK (modest_ui_dimming_rules_on_find_in_msg));
3582 modest_hildon2_window_add_to_menu (MODEST_HILDON2_WINDOW (self),
3583 dngettext(GETTEXT_PACKAGE,
3584 "mcen_me_move_message",
3585 "mcen_me_move_messages",
3588 APP_MENU_CALLBACK (modest_ui_actions_on_move_to),
3589 MODEST_DIMMING_CALLBACK (modest_ui_dimming_rules_on_move_to));
3591 modest_hildon2_window_add_to_menu (MODEST_HILDON2_WINDOW (self), _("mcen_me_inbox_mark_as_read"), NULL,
3592 APP_MENU_CALLBACK (modest_ui_actions_on_mark_as_read),
3593 MODEST_DIMMING_CALLBACK (modest_ui_dimming_rules_on_mark_as_read_msg_in_view));
3595 modest_hildon2_window_add_to_menu (MODEST_HILDON2_WINDOW (self), _("mcen_me_inbox_mark_as_unread"), NULL,
3596 APP_MENU_CALLBACK (modest_ui_actions_on_mark_as_unread),
3597 MODEST_DIMMING_CALLBACK (modest_ui_dimming_rules_on_mark_as_unread_msg_in_view));
3599 modest_hildon2_window_add_to_menu (MODEST_HILDON2_WINDOW (self), _("mcen_me_viewer_save_attachments"), NULL,
3600 APP_MENU_CALLBACK (modest_ui_actions_save_attachments),
3601 MODEST_DIMMING_CALLBACK (modest_ui_dimming_rules_on_save_attachments));
3602 modest_hildon2_window_add_to_menu (MODEST_HILDON2_WINDOW (self), _("mcen_me_inbox_remove_attachments"), NULL,
3603 APP_MENU_CALLBACK (modest_ui_actions_remove_attachments),
3604 MODEST_DIMMING_CALLBACK (modest_ui_dimming_rules_on_remove_attachments));
3606 modest_hildon2_window_add_to_menu (MODEST_HILDON2_WINDOW (self), _("mcen_me_new_message"), "<Control>n",
3607 APP_MENU_CALLBACK (modest_ui_actions_on_new_msg),
3608 MODEST_DIMMING_CALLBACK (modest_ui_dimming_rules_on_new_msg));
3609 modest_hildon2_window_add_to_menu (MODEST_HILDON2_WINDOW (self), _("mcen_me_viewer_addtocontacts"), NULL,
3610 APP_MENU_CALLBACK (modest_ui_actions_add_to_contacts),
3611 MODEST_DIMMING_CALLBACK (modest_ui_dimming_rules_on_add_to_contacts));
3613 modest_hildon2_window_add_to_menu (MODEST_HILDON2_WINDOW (self), _("mcen_ti_message_properties"), NULL,
3614 APP_MENU_CALLBACK (modest_ui_actions_on_details),
3615 MODEST_DIMMING_CALLBACK (modest_ui_dimming_rules_on_details));
3619 modest_msg_view_window_add_to_contacts (ModestMsgViewWindow *self)
3621 ModestMsgViewWindowPrivate *priv;
3622 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
3623 GSList *recipients = NULL;
3625 gboolean contacts_to_add = FALSE;
3627 msg = tny_msg_view_get_msg (TNY_MSG_VIEW (priv->msg_view));
3631 header = modest_msg_view_window_get_header (self);
3634 recipients = modest_tny_msg_header_get_all_recipients_list (header);
3635 g_object_unref (header);
3637 recipients = modest_tny_msg_get_all_recipients_list (msg);
3638 g_object_unref (msg);
3641 if (recipients != NULL) {
3642 GtkWidget *picker_dialog;
3643 GtkWidget *selector;
3645 gchar *selected = NULL;
3647 selector = hildon_touch_selector_new_text ();
3648 g_object_ref (selector);
3650 for (node = recipients; node != NULL; node = g_slist_next (node)) {
3651 if (!modest_address_book_has_address ((const gchar *) node->data)) {
3652 hildon_touch_selector_append_text (HILDON_TOUCH_SELECTOR (selector),
3653 (const gchar *) node->data);
3654 contacts_to_add = TRUE;
3658 if (contacts_to_add) {
3661 picker_dialog = hildon_picker_dialog_new (GTK_WINDOW (self));
3662 gtk_window_set_title (GTK_WINDOW (picker_dialog), _("mcen_me_viewer_addtocontacts"));
3664 hildon_picker_dialog_set_selector (HILDON_PICKER_DIALOG (picker_dialog),
3665 HILDON_TOUCH_SELECTOR (selector));
3667 picker_result = gtk_dialog_run (GTK_DIALOG (picker_dialog));
3669 if (picker_result == GTK_RESPONSE_OK) {
3670 selected = hildon_touch_selector_get_current_text (HILDON_TOUCH_SELECTOR (selector));
3672 gtk_widget_destroy (picker_dialog);
3675 modest_address_book_add_address (selected, (GtkWindow *) self);
3680 g_object_unref (selector);
3685 if (recipients) {g_slist_foreach (recipients, (GFunc) g_free, NULL); g_slist_free (recipients);}
3689 _modest_msg_view_window_map_event (GtkWidget *widget,
3693 ModestMsgViewWindow *self = (ModestMsgViewWindow *) userdata;
3695 update_progress_hint (self);
3701 modest_msg_view_window_fetch_images (ModestMsgViewWindow *self)
3703 ModestMsgViewWindowPrivate *priv;
3704 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
3706 modest_msg_view_request_fetch_images (MODEST_MSG_VIEW (priv->msg_view));
3710 modest_msg_view_window_has_blocked_external_images (ModestMsgViewWindow *self)
3712 ModestMsgViewWindowPrivate *priv;
3713 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
3715 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self), FALSE);
3717 return modest_msg_view_has_blocked_external_images (MODEST_MSG_VIEW (priv->msg_view));
3721 modest_msg_view_window_reload (ModestMsgViewWindow *self)
3723 ModestMsgViewWindowPrivate *priv;
3724 const gchar *msg_uid;
3725 TnyHeader *header = NULL;
3726 TnyFolder *folder = NULL;
3728 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self));
3730 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
3732 header = modest_msg_view_window_get_header (MODEST_MSG_VIEW_WINDOW (self));
3736 folder = tny_header_get_folder (header);
3737 g_object_unref (header);
3742 msg_uid = modest_msg_view_window_get_message_uid (self);
3744 GtkTreeRowReference *row_reference;
3746 if (priv->row_reference && gtk_tree_row_reference_valid (priv->row_reference)) {
3747 row_reference = priv->row_reference;
3749 row_reference = NULL;
3751 if (!message_reader (self, priv, NULL, msg_uid, folder, row_reference))
3752 g_warning ("Shouldn't happen, trying to reload a message failed");
3755 g_object_unref (folder);
3759 update_branding (ModestMsgViewWindow *self)
3761 const gchar *account;
3762 const gchar *mailbox;
3763 ModestAccountMgr *mgr;
3764 ModestProtocol *protocol = NULL;
3765 gchar *service_name = NULL;
3766 const GdkPixbuf *service_icon = NULL;
3767 ModestMsgViewWindowPrivate *priv;
3769 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
3771 account = modest_window_get_active_account (MODEST_WINDOW (self));
3772 mailbox = modest_window_get_active_mailbox (MODEST_WINDOW (self));
3774 mgr = modest_runtime_get_account_mgr ();
3776 if (modest_account_mgr_account_is_multimailbox (mgr, account, &protocol)) {
3777 if (MODEST_IS_ACCOUNT_PROTOCOL (protocol)) {
3778 service_name = modest_account_protocol_get_service_name (MODEST_ACCOUNT_PROTOCOL (protocol),
3780 service_icon = modest_account_protocol_get_service_icon (MODEST_ACCOUNT_PROTOCOL (protocol),
3781 account, mailbox, MODEST_ICON_SIZE_SMALL);
3785 modest_msg_view_set_branding (MODEST_MSG_VIEW (priv->msg_view), service_name, service_icon);
3786 g_free (service_name);