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 if (cancelled || err) {
2586 if ((err->domain == TNY_ERROR_DOMAIN) &&
2587 (err->code == TNY_IO_ERROR_WRITE) &&
2588 (errno == ENOSPC)) {
2589 msg = g_strdup_printf (_KR("cerm_device_memory_full"), "");
2591 msg = g_strdup (_("mail_ib_file_operation_failed"));
2593 modest_platform_information_banner (NULL, NULL, msg);
2599 /* It could happen that the window was closed. So we
2600 assume it is a cancelation */
2601 if (!GTK_WIDGET_VISIBLE (helper->self))
2604 /* Remove the progress hint */
2605 set_progress_hint (helper->self, FALSE);
2607 content_type = tny_mime_part_get_content_type (mime_part);
2608 if (g_str_has_prefix (content_type, "message/rfc822")) {
2609 ModestWindowMgr *mgr;
2610 ModestWindow *msg_win = NULL;
2613 const gchar *mailbox;
2614 TnyStream *file_stream;
2617 fd = g_open (helper->file_path, O_RDONLY, 0644);
2619 file_stream = tny_fs_stream_new (fd);
2621 mgr = modest_runtime_get_window_mgr ();
2623 account = g_strdup (modest_window_get_active_account (MODEST_WINDOW (helper->self)));
2624 mailbox = modest_window_get_active_mailbox (MODEST_WINDOW (helper->self));
2627 account = modest_account_mgr_get_default_account (modest_runtime_get_account_mgr ());
2629 msg = tny_camel_msg_new ();
2630 tny_camel_msg_parse (msg, file_stream);
2631 msg_win = modest_msg_view_window_new_for_attachment (TNY_MSG (msg), account, mailbox, helper->attachment_uid);
2632 modest_window_set_zoom (MODEST_WINDOW (msg_win),
2633 modest_window_get_zoom (MODEST_WINDOW (helper->self)));
2634 if (modest_window_mgr_register_window (mgr, msg_win, MODEST_WINDOW (helper->self)))
2635 gtk_widget_show_all (GTK_WIDGET (msg_win));
2637 gtk_widget_destroy (GTK_WIDGET (msg_win));
2638 g_object_unref (msg);
2639 g_object_unref (file_stream);
2641 modest_platform_information_banner (NULL, NULL, _("mail_ib_file_operation_failed"));
2646 /* make the file read-only */
2647 g_chmod(helper->file_path, 0444);
2649 /* Activate the file */
2650 modest_platform_activate_file (helper->file_path, tny_mime_part_get_content_type (mime_part));
2655 g_object_unref (helper->self);
2656 g_free (helper->file_path);
2657 g_free (helper->attachment_uid);
2658 g_slice_free (DecodeAsyncHelper, helper);
2662 modest_msg_view_window_view_attachment (ModestMsgViewWindow *window,
2663 TnyMimePart *mime_part)
2665 ModestMsgViewWindowPrivate *priv;
2666 const gchar *msg_uid;
2667 gchar *attachment_uid = NULL;
2668 gint attachment_index = 0;
2669 TnyList *attachments;
2671 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window));
2672 g_return_if_fail (TNY_IS_MIME_PART (mime_part) || (mime_part == NULL));
2673 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
2675 msg_uid = modest_msg_view_window_get_message_uid (MODEST_MSG_VIEW_WINDOW (window));
2676 attachments = modest_msg_view_get_attachments (MODEST_MSG_VIEW (priv->msg_view));
2677 attachment_index = modest_list_index (attachments, (GObject *) mime_part);
2678 g_object_unref (attachments);
2680 if (msg_uid && attachment_index >= 0) {
2681 attachment_uid = g_strdup_printf ("%s/%d", msg_uid, attachment_index);
2684 if (mime_part == NULL) {
2685 gboolean error = FALSE;
2686 TnyList *selected_attachments = modest_msg_view_get_selected_attachments (MODEST_MSG_VIEW (priv->msg_view));
2687 if (selected_attachments == NULL || tny_list_get_length (selected_attachments) == 0) {
2689 } else if (tny_list_get_length (selected_attachments) > 1) {
2690 hildon_banner_show_information (NULL, NULL, _("mcen_ib_unable_to_display_more"));
2694 iter = tny_list_create_iterator (selected_attachments);
2695 mime_part = (TnyMimePart *) tny_iterator_get_current (iter);
2696 g_object_unref (iter);
2698 if (selected_attachments)
2699 g_object_unref (selected_attachments);
2704 g_object_ref (mime_part);
2707 if (tny_mime_part_is_purged (mime_part))
2710 if (!modest_tny_mime_part_is_msg (mime_part) && tny_mime_part_get_filename (mime_part)) {
2711 gchar *filepath = NULL;
2712 const gchar *att_filename = tny_mime_part_get_filename (mime_part);
2713 gboolean show_error_banner = FALSE;
2714 TnyFsStream *temp_stream = NULL;
2715 temp_stream = modest_utils_create_temp_stream (att_filename, attachment_uid,
2718 if (temp_stream != NULL) {
2719 ModestAccountMgr *mgr;
2720 DecodeAsyncHelper *helper;
2721 gboolean decode_in_provider;
2722 ModestProtocol *protocol;
2723 const gchar *account;
2725 /* Activate progress hint */
2726 set_progress_hint (window, TRUE);
2728 helper = g_slice_new0 (DecodeAsyncHelper);
2729 helper->self = g_object_ref (window);
2730 helper->file_path = g_strdup (filepath);
2731 helper->attachment_uid = g_strdup (attachment_uid);
2733 decode_in_provider = FALSE;
2734 mgr = modest_runtime_get_account_mgr ();
2735 account = modest_window_get_active_account (MODEST_WINDOW (window));
2736 if (modest_account_mgr_account_is_multimailbox (mgr, account, &protocol)) {
2737 if (MODEST_IS_ACCOUNT_PROTOCOL (protocol)) {
2739 uri = g_strconcat ("file://", filepath, NULL);
2740 decode_in_provider =
2741 modest_account_protocol_decode_part_to_stream_async (
2742 MODEST_ACCOUNT_PROTOCOL (protocol),
2745 TNY_STREAM (temp_stream),
2746 on_decode_to_stream_async_handler,
2753 if (!decode_in_provider)
2754 tny_mime_part_decode_to_stream_async (mime_part, TNY_STREAM (temp_stream),
2755 on_decode_to_stream_async_handler,
2758 g_object_unref (temp_stream);
2759 /* NOTE: files in the temporary area will be automatically
2760 * cleaned after some time if they are no longer in use */
2763 const gchar *content_type;
2764 /* the file may already exist but it isn't writable,
2765 * let's try to open it anyway */
2766 content_type = tny_mime_part_get_content_type (mime_part);
2767 modest_platform_activate_file (filepath, content_type);
2769 g_warning ("%s: modest_utils_create_temp_stream failed", __FUNCTION__);
2770 show_error_banner = TRUE;
2775 if (show_error_banner)
2776 modest_platform_information_banner (NULL, NULL, _("mail_ib_file_operation_failed"));
2777 } else if (!modest_tny_mime_part_is_msg (mime_part)) {
2778 ModestWindowMgr *mgr;
2779 ModestWindow *msg_win = NULL;
2780 TnyMsg *current_msg;
2784 current_msg = modest_msg_view_window_get_message (MODEST_MSG_VIEW_WINDOW (window));
2785 mgr = modest_runtime_get_window_mgr ();
2786 header = tny_msg_get_header (TNY_MSG (current_msg));
2787 found = modest_window_mgr_find_registered_message_uid (mgr,
2792 g_debug ("window for this body is already being created");
2795 /* it's not found, so create a new window for it */
2796 modest_window_mgr_register_header (mgr, header, attachment_uid); /* register the uid before building the window */
2797 gchar *account = g_strdup (modest_window_get_active_account (MODEST_WINDOW (window)));
2798 const gchar *mailbox = modest_window_get_active_mailbox (MODEST_WINDOW (window));
2800 account = modest_account_mgr_get_default_account (modest_runtime_get_account_mgr ());
2802 msg_win = modest_msg_view_window_new_with_other_body (TNY_MSG (current_msg), TNY_MIME_PART (mime_part),
2803 account, mailbox, attachment_uid);
2805 modest_window_set_zoom (MODEST_WINDOW (msg_win),
2806 modest_window_get_zoom (MODEST_WINDOW (window)));
2807 if (modest_window_mgr_register_window (mgr, msg_win, MODEST_WINDOW (window)))
2808 gtk_widget_show_all (GTK_WIDGET (msg_win));
2810 gtk_widget_destroy (GTK_WIDGET (msg_win));
2812 g_object_unref (current_msg);
2814 /* message attachment */
2815 TnyHeader *header = NULL;
2816 ModestWindowMgr *mgr;
2817 ModestWindow *msg_win = NULL;
2820 header = tny_msg_get_header (TNY_MSG (mime_part));
2821 mgr = modest_runtime_get_window_mgr ();
2822 found = modest_window_mgr_find_registered_header (mgr, header, &msg_win);
2825 /* if it's found, but there is no msg_win, it's probably in the process of being created;
2826 * thus, we don't do anything */
2827 g_debug ("window for is already being created");
2829 /* it's not found, so create a new window for it */
2830 modest_window_mgr_register_header (mgr, header, attachment_uid); /* register the uid before building the window */
2831 gchar *account = g_strdup (modest_window_get_active_account (MODEST_WINDOW (window)));
2832 const gchar *mailbox = modest_window_get_active_mailbox (MODEST_WINDOW (window));
2834 account = modest_account_mgr_get_default_account (modest_runtime_get_account_mgr ());
2835 msg_win = modest_msg_view_window_new_for_attachment (TNY_MSG (mime_part), account,
2836 mailbox, attachment_uid);
2837 modest_window_set_zoom (MODEST_WINDOW (msg_win),
2838 modest_window_get_zoom (MODEST_WINDOW (window)));
2839 if (modest_window_mgr_register_window (mgr, msg_win, MODEST_WINDOW (window)))
2840 gtk_widget_show_all (GTK_WIDGET (msg_win));
2842 gtk_widget_destroy (GTK_WIDGET (msg_win));
2848 g_free (attachment_uid);
2850 g_object_unref (mime_part);
2862 GnomeVFSResult result;
2864 ModestMsgViewWindow *window;
2867 static void save_mime_part_info_free (SaveMimePartInfo *info, gboolean with_struct);
2868 static gboolean idle_save_mime_part_show_result (SaveMimePartInfo *info);
2869 static gpointer save_mime_part_to_file (SaveMimePartInfo *info);
2870 static void save_mime_parts_to_file_with_checks (GtkWindow *parent, SaveMimePartInfo *info);
2873 save_mime_part_info_free (SaveMimePartInfo *info, gboolean with_struct)
2877 for (node = info->pairs; node != NULL; node = g_list_next (node)) {
2878 SaveMimePartPair *pair = (SaveMimePartPair *) node->data;
2879 g_free (pair->filename);
2880 g_object_unref (pair->part);
2881 g_slice_free (SaveMimePartPair, pair);
2883 g_list_free (info->pairs);
2886 g_object_unref (info->window);
2887 info->window = NULL;
2889 g_slice_free (SaveMimePartInfo, info);
2894 idle_save_mime_part_show_result (SaveMimePartInfo *info)
2896 /* This is a GDK lock because we are an idle callback and
2897 * hildon_banner_show_information is or does Gtk+ code */
2899 gdk_threads_enter (); /* CHECKED */
2900 if (info->result == GNOME_VFS_OK) {
2901 hildon_banner_show_information (NULL, NULL, _CS("sfil_ib_saved"));
2902 } else if (info->result == GNOME_VFS_ERROR_NO_SPACE) {
2905 /* Check if the uri belongs to the external mmc */
2906 if (g_str_has_prefix (info->uri, g_getenv (MODEST_MMC1_VOLUMEPATH_ENV)))
2907 msg = g_strdup_printf (_KR("cerm_device_memory_full"), "");
2909 msg = g_strdup (_KR("cerm_memory_card_full"));
2910 modest_platform_information_banner (NULL, NULL, msg);
2913 hildon_banner_show_information (NULL, NULL, _("mail_ib_file_operation_failed"));
2915 save_mime_part_info_free (info, FALSE);
2916 gdk_threads_leave (); /* CHECKED */
2922 save_mime_part_to_file (SaveMimePartInfo *info)
2924 GnomeVFSHandle *handle;
2926 SaveMimePartPair *pair = (SaveMimePartPair *) info->pairs->data;
2928 info->result = gnome_vfs_create (&handle, pair->filename, GNOME_VFS_OPEN_WRITE, FALSE, 0644);
2929 if (info->result == GNOME_VFS_OK) {
2930 GError *error = NULL;
2931 gboolean decode_in_provider;
2933 ModestAccountMgr *mgr;
2934 const gchar *account;
2935 ModestProtocol *protocol = NULL;
2937 stream = tny_vfs_stream_new (handle);
2939 decode_in_provider = FALSE;
2940 mgr = modest_runtime_get_account_mgr ();
2941 account = modest_window_get_active_account (MODEST_WINDOW (info->window));
2942 if (modest_account_mgr_account_is_multimailbox (mgr, account, &protocol)) {
2943 if (MODEST_IS_ACCOUNT_PROTOCOL (protocol)) {
2944 decode_in_provider =
2945 modest_account_protocol_decode_part_to_stream (
2946 MODEST_ACCOUNT_PROTOCOL (protocol),
2954 if (!decode_in_provider)
2955 written = tny_mime_part_decode_to_stream (pair->part, stream, &error);
2958 g_warning ("modest: could not save attachment %s: %d (%s)\n", pair->filename, error?error->code:-1, error?error->message:"Unknown error");
2960 if ((error->domain == TNY_ERROR_DOMAIN) &&
2961 (error->code == TNY_IO_ERROR_WRITE) &&
2962 (errno == ENOSPC)) {
2963 info->result = GNOME_VFS_ERROR_NO_SPACE;
2965 info->result = GNOME_VFS_ERROR_IO;
2968 g_object_unref (G_OBJECT (stream));
2970 g_warning ("Could not create save attachment %s: %s\n",
2971 pair->filename, gnome_vfs_result_to_string (info->result));
2974 /* Go on saving remaining files */
2975 info->pairs = g_list_remove_link (info->pairs, info->pairs);
2976 if (info->pairs != NULL) {
2977 save_mime_part_to_file (info);
2979 g_idle_add ((GSourceFunc) idle_save_mime_part_show_result, info);
2986 save_mime_parts_to_file_with_checks (GtkWindow *parent,
2987 SaveMimePartInfo *info)
2989 gboolean is_ok = TRUE;
2990 gint replaced_files = 0;
2991 const GList *files = info->pairs;
2992 const GList *iter, *to_replace = NULL;
2994 for (iter = files; (iter != NULL) && (replaced_files < 2); iter = g_list_next(iter)) {
2995 SaveMimePartPair *pair = iter->data;
2996 gchar *unescaped = g_uri_unescape_string (pair->filename, NULL);
2998 if (modest_utils_file_exists (unescaped)) {
3000 if (replaced_files == 1)
3005 if (replaced_files) {
3008 if (replaced_files == 1) {
3009 SaveMimePartPair *pair = to_replace->data;
3010 const gchar *basename = strrchr (pair->filename, G_DIR_SEPARATOR) + 1;
3011 gchar *escaped_basename, *message;
3013 escaped_basename = g_uri_unescape_string (basename, NULL);
3014 message = g_strdup_printf ("%s\n%s",
3015 _FM("docm_nc_replace_file"),
3016 (escaped_basename) ? escaped_basename : "");
3017 response = modest_platform_run_confirmation_dialog (parent, message);
3019 g_free (escaped_basename);
3021 response = modest_platform_run_confirmation_dialog (parent,
3022 _FM("docm_nc_replace_multiple"));
3024 if (response != GTK_RESPONSE_OK)
3029 save_mime_part_info_free (info, TRUE);
3031 g_thread_create ((GThreadFunc)save_mime_part_to_file, info, FALSE, NULL);
3036 typedef struct _SaveAttachmentsInfo {
3037 TnyList *attachments_list;
3038 ModestMsgViewWindow *window;
3039 } SaveAttachmentsInfo;
3042 save_attachments_response (GtkDialog *dialog,
3046 TnyList *mime_parts;
3048 GList *files_to_save = NULL;
3049 gchar *current_folder;
3050 SaveAttachmentsInfo *sa_info = (SaveAttachmentsInfo *) user_data;
3052 mime_parts = TNY_LIST (sa_info->attachments_list);
3054 if (arg1 != GTK_RESPONSE_OK)
3057 chooser_uri = gtk_file_chooser_get_uri (GTK_FILE_CHOOSER (dialog));
3058 current_folder = gtk_file_chooser_get_current_folder_uri (GTK_FILE_CHOOSER (dialog));
3059 if (current_folder && *current_folder != '\0') {
3061 modest_conf_set_string (modest_runtime_get_conf (), MODEST_CONF_LATEST_SAVE_ATTACHMENT_PATH,
3062 current_folder,&err);
3064 g_debug ("Error storing latest used folder: %s", err->message);
3068 g_free (current_folder);
3070 if (!modest_utils_folder_writable (chooser_uri)) {
3071 const gchar *err_msg;
3073 #ifdef MODEST_PLATFORM_MAEMO
3074 if (modest_maemo_utils_in_usb_mode ()) {
3075 err_msg = dgettext ("hildon-status-bar-usb", "usbh_ib_mmc_usb_connected");
3077 err_msg = _FM("sfil_ib_readonly_location");
3080 err_msg = _FM("sfil_ib_readonly_location");
3082 hildon_banner_show_information (NULL, NULL, err_msg);
3086 iter = tny_list_create_iterator (mime_parts);
3087 while (!tny_iterator_is_done (iter)) {
3088 TnyMimePart *mime_part = (TnyMimePart *) tny_iterator_get_current (iter);
3090 if ((modest_tny_mime_part_is_attachment_for_modest (mime_part)) &&
3091 !tny_mime_part_is_purged (mime_part) &&
3092 (tny_mime_part_get_filename (mime_part) != NULL)) {
3093 SaveMimePartPair *pair;
3095 pair = g_slice_new0 (SaveMimePartPair);
3097 if (tny_list_get_length (mime_parts) > 1) {
3099 gnome_vfs_escape_slashes (tny_mime_part_get_filename (mime_part));
3100 pair->filename = g_build_filename (chooser_uri, escaped, NULL);
3103 pair->filename = g_strdup (chooser_uri);
3105 pair->part = mime_part;
3106 files_to_save = g_list_prepend (files_to_save, pair);
3108 tny_iterator_next (iter);
3110 g_object_unref (iter);
3113 if (files_to_save != NULL) {
3114 SaveMimePartInfo *info = g_slice_new0 (SaveMimePartInfo);
3115 info->pairs = files_to_save;
3116 info->result = TRUE;
3117 info->uri = g_strdup (chooser_uri);
3118 info->window = g_object_ref (sa_info->window);
3119 save_mime_parts_to_file_with_checks ((GtkWindow *) dialog, info);
3121 g_free (chooser_uri);
3124 /* Free and close the dialog */
3125 g_object_unref (mime_parts);
3126 g_object_unref (sa_info->window);
3127 g_slice_free (SaveAttachmentsInfo, sa_info);
3128 gtk_widget_destroy (GTK_WIDGET (dialog));
3132 modest_msg_view_window_save_attachments (ModestMsgViewWindow *window,
3133 TnyList *mime_parts)
3135 ModestMsgViewWindowPrivate *priv;
3136 GtkWidget *save_dialog = NULL;
3137 gchar *conf_folder = NULL;
3138 gchar *filename = NULL;
3139 gchar *save_multiple_str = NULL;
3140 const gchar *root_folder = "file:///";
3142 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window));
3143 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
3145 if (mime_parts == NULL) {
3146 /* In Hildon 2.2 save and delete operate over all the attachments as there's no
3147 * selection available */
3148 mime_parts = modest_msg_view_get_attachments (MODEST_MSG_VIEW (priv->msg_view));
3149 if (mime_parts && !modest_maemo_utils_select_attachments (GTK_WINDOW (window), mime_parts, FALSE)) {
3150 g_object_unref (mime_parts);
3153 if (mime_parts == NULL || tny_list_get_length (mime_parts) == 0) {
3155 g_object_unref (mime_parts);
3161 g_object_ref (mime_parts);
3164 /* prepare dialog */
3165 if (tny_list_get_length (mime_parts) == 1) {
3167 /* only one attachment selected */
3168 iter = tny_list_create_iterator (mime_parts);
3169 TnyMimePart *mime_part = (TnyMimePart *) tny_iterator_get_current (iter);
3170 g_object_unref (iter);
3171 if (!modest_tny_mime_part_is_msg (mime_part) &&
3172 modest_tny_mime_part_is_attachment_for_modest (mime_part) &&
3173 !tny_mime_part_is_purged (mime_part)) {
3174 filename = g_strdup (tny_mime_part_get_filename (mime_part));
3176 /* TODO: show any error? */
3177 g_warning ("%s: Tried to save a non-file attachment", __FUNCTION__);
3178 g_object_unref (mime_parts);
3181 g_object_unref (mime_part);
3183 gint num = tny_list_get_length (mime_parts);
3184 save_multiple_str = g_strdup_printf (dngettext("hildon-fm",
3185 "sfil_va_number_of_objects_attachment",
3186 "sfil_va_number_of_objects_attachments",
3190 save_dialog = hildon_file_chooser_dialog_new (GTK_WINDOW (window),
3191 GTK_FILE_CHOOSER_ACTION_SAVE);
3193 /* Get last used folder */
3194 conf_folder = modest_conf_get_string (modest_runtime_get_conf (),
3195 MODEST_CONF_LATEST_SAVE_ATTACHMENT_PATH, NULL);
3197 /* File chooser stops working if we select "file:///" as current folder */
3198 if (conf_folder && g_ascii_strcasecmp (root_folder, conf_folder) != 0) {
3199 g_free (conf_folder);
3203 if (conf_folder && conf_folder[0] != '\0') {
3204 gtk_file_chooser_set_current_folder_uri (GTK_FILE_CHOOSER (save_dialog), conf_folder);
3207 /* Set the default folder to documents folder */
3208 docs_folder = (gchar *) g_strdup(g_get_user_special_dir (G_USER_DIRECTORY_DOCUMENTS));
3211 docs_folder = g_build_filename (g_getenv (MYDOCS_ENV), DOCS_FOLDER, NULL);
3213 gtk_file_chooser_set_current_folder (GTK_FILE_CHOOSER (save_dialog), docs_folder);
3214 g_free (docs_folder);
3216 g_free (conf_folder);
3220 gtk_file_chooser_set_current_name (GTK_FILE_CHOOSER (save_dialog),
3225 /* if multiple, set multiple string */
3226 if (save_multiple_str) {
3227 g_object_set (G_OBJECT (save_dialog), "save-multiple", save_multiple_str, NULL);
3228 gtk_window_set_title (GTK_WINDOW (save_dialog), _FM("sfil_ti_save_objects_files"));
3229 g_free (save_multiple_str);
3232 /* We must run this asynchronously, because the hildon dialog
3233 performs a gtk_dialog_run by itself which leads to gdk
3235 SaveAttachmentsInfo *sa_info;
3236 sa_info = g_slice_new (SaveAttachmentsInfo);
3237 sa_info->attachments_list = mime_parts;
3238 sa_info->window = g_object_ref (window);
3239 g_signal_connect (save_dialog, "response",
3240 G_CALLBACK (save_attachments_response), sa_info);
3242 gtk_widget_show_all (save_dialog);
3246 show_remove_attachment_information (gpointer userdata)
3248 ModestMsgViewWindow *window = (ModestMsgViewWindow *) userdata;
3249 ModestMsgViewWindowPrivate *priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
3251 /* We're outside the main lock */
3252 gdk_threads_enter ();
3254 if (priv->remove_attachment_banner != NULL) {
3255 gtk_widget_destroy (priv->remove_attachment_banner);
3256 g_object_unref (priv->remove_attachment_banner);
3259 priv->remove_attachment_banner = g_object_ref (
3260 hildon_banner_show_animation (NULL, NULL, _("mcen_me_inbox_remove_attachments")));
3262 gdk_threads_leave ();
3268 modest_msg_view_window_remove_attachments (ModestMsgViewWindow *window, gboolean get_all)
3270 ModestMsgViewWindowPrivate *priv;
3271 TnyList *mime_parts = NULL, *tmp;
3272 gchar *confirmation_message;
3278 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window));
3279 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
3281 /* In hildon 2.2 we ignore the get_all flag as we always get all attachments. This is
3282 * because we don't have selection
3284 mime_parts = modest_msg_view_get_attachments (MODEST_MSG_VIEW (priv->msg_view));
3286 /* Remove already purged messages from mime parts list. We use
3287 a copy of the list to remove items in the original one */
3288 tmp = tny_list_copy (mime_parts);
3289 iter = tny_list_create_iterator (tmp);
3290 while (!tny_iterator_is_done (iter)) {
3291 TnyMimePart *part = TNY_MIME_PART (tny_iterator_get_current (iter));
3292 if (tny_mime_part_is_purged (part))
3293 tny_list_remove (mime_parts, (GObject *) part);
3295 g_object_unref (part);
3296 tny_iterator_next (iter);
3298 g_object_unref (tmp);
3299 g_object_unref (iter);
3301 if (!modest_maemo_utils_select_attachments (GTK_WINDOW (window), mime_parts, TRUE) ||
3302 tny_list_get_length (mime_parts) == 0) {
3303 g_object_unref (mime_parts);
3307 n_attachments = tny_list_get_length (mime_parts);
3308 if (n_attachments == 1) {
3312 iter = tny_list_create_iterator (mime_parts);
3313 part = (TnyMimePart *) tny_iterator_get_current (iter);
3314 g_object_unref (iter);
3315 if (modest_tny_mime_part_is_msg (part)) {
3317 header = tny_msg_get_header (TNY_MSG (part));
3318 filename = tny_header_dup_subject (header);
3319 g_object_unref (header);
3320 if (filename == NULL)
3321 filename = g_strdup (_("mail_va_no_subject"));
3323 filename = g_strdup (tny_mime_part_get_filename (TNY_MIME_PART (part)));
3325 confirmation_message = g_strdup_printf (_("mcen_nc_purge_file_text"), filename);
3327 g_object_unref (part);
3329 confirmation_message = g_strdup_printf (ngettext("mcen_nc_purge_file_text",
3330 "mcen_nc_purge_files_text",
3331 n_attachments), n_attachments);
3333 response = modest_platform_run_confirmation_dialog (GTK_WINDOW (window),
3334 confirmation_message);
3335 g_free (confirmation_message);
3337 if (response != GTK_RESPONSE_OK) {
3338 g_object_unref (mime_parts);
3342 priv->purge_timeout = g_timeout_add (2000, show_remove_attachment_information, window);
3344 iter = tny_list_create_iterator (mime_parts);
3345 while (!tny_iterator_is_done (iter)) {
3348 part = (TnyMimePart *) tny_iterator_get_current (iter);
3349 tny_mime_part_set_purged (TNY_MIME_PART (part));
3350 g_object_unref (part);
3351 tny_iterator_next (iter);
3353 g_object_unref (iter);
3355 msg = tny_msg_view_get_msg (TNY_MSG_VIEW (priv->msg_view));
3356 tny_msg_view_clear (TNY_MSG_VIEW (priv->msg_view));
3357 tny_msg_rewrite_cache (msg);
3358 tny_msg_view_set_msg (TNY_MSG_VIEW (priv->msg_view), msg);
3359 g_object_unref (msg);
3360 update_branding (MODEST_MSG_VIEW_WINDOW (window));
3362 g_object_unref (mime_parts);
3364 if (priv->purge_timeout > 0) {
3365 g_source_remove (priv->purge_timeout);
3366 priv->purge_timeout = 0;
3369 if (priv->remove_attachment_banner) {
3370 gtk_widget_destroy (priv->remove_attachment_banner);
3371 g_object_unref (priv->remove_attachment_banner);
3372 priv->remove_attachment_banner = NULL;
3378 update_window_title (ModestMsgViewWindow *window)
3380 ModestMsgViewWindowPrivate *priv;
3382 TnyHeader *header = NULL;
3383 gchar *subject = NULL;
3385 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
3387 /* Note that if the window is closed while we're retrieving
3388 the message, this widget could de deleted */
3389 if (!priv->msg_view)
3392 msg = tny_msg_view_get_msg (TNY_MSG_VIEW (priv->msg_view));
3394 if (priv->other_body) {
3397 description = modest_tny_mime_part_get_header_value (priv->other_body, "Content-Description");
3399 g_strstrip (description);
3400 subject = description;
3402 } else if (msg != NULL) {
3403 header = tny_msg_get_header (msg);
3404 subject = tny_header_dup_subject (header);
3405 g_object_unref (header);
3406 g_object_unref (msg);
3409 if ((subject == NULL)||(subject[0] == '\0')) {
3411 subject = g_strdup (_("mail_va_no_subject"));
3414 gtk_window_set_title (GTK_WINDOW (window), subject);
3419 on_move_focus (GtkWidget *widget,
3420 GtkDirectionType direction,
3423 g_signal_stop_emission_by_name (G_OBJECT (widget), "move-focus");
3427 fetch_image_open_stream (TnyStreamCache *self, gint64 *expected_size, gchar *uri)
3429 GnomeVFSResult result;
3430 GnomeVFSHandle *handle = NULL;
3431 GnomeVFSFileInfo *info = NULL;
3434 result = gnome_vfs_open (&handle, uri, GNOME_VFS_OPEN_READ);
3435 if (result != GNOME_VFS_OK) {
3440 info = gnome_vfs_file_info_new ();
3441 result = gnome_vfs_get_file_info_from_handle (handle, info, GNOME_VFS_FILE_INFO_DEFAULT);
3442 if (result != GNOME_VFS_OK || ! (info->valid_fields & GNOME_VFS_FILE_INFO_FIELDS_SIZE)) {
3443 /* We put a "safe" default size for going to cache */
3444 *expected_size = (300*1024);
3446 *expected_size = info->size;
3448 gnome_vfs_file_info_unref (info);
3450 stream = tny_vfs_stream_new (handle);
3459 TnyStream *output_stream;
3460 GtkWidget *msg_view;
3465 on_fetch_image_idle_refresh_view (gpointer userdata)
3468 FetchImageData *fidata = (FetchImageData *) userdata;
3470 gdk_threads_enter ();
3471 if (GTK_WIDGET_DRAWABLE (fidata->msg_view)) {
3472 ModestMsgViewWindowPrivate *priv;
3474 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (fidata->window);
3475 priv->fetching_images--;
3476 gtk_widget_queue_draw (fidata->msg_view);
3477 update_progress_hint (MODEST_MSG_VIEW_WINDOW (fidata->window));
3479 gdk_threads_leave ();
3481 g_object_unref (fidata->msg_view);
3482 g_object_unref (fidata->window);
3483 g_slice_free (FetchImageData, fidata);
3488 on_fetch_image_thread (gpointer userdata)
3490 FetchImageData *fidata = (FetchImageData *) userdata;
3491 TnyStreamCache *cache;
3492 TnyStream *cache_stream;
3494 cache = modest_runtime_get_images_cache ();
3496 tny_stream_cache_get_stream (cache,
3498 (TnyStreamCacheOpenStreamFetcher) fetch_image_open_stream,
3499 (gpointer) fidata->uri);
3500 g_free (fidata->cache_id);
3501 g_free (fidata->uri);
3503 if (cache_stream != NULL) {
3506 while (G_LIKELY (!tny_stream_is_eos (cache_stream))) {
3509 nb_read = tny_stream_read (cache_stream, buffer, sizeof (buffer));
3510 if (G_UNLIKELY (nb_read < 0)) {
3512 } else if (G_LIKELY (nb_read > 0)) {
3513 gssize nb_written = 0;
3515 while (G_UNLIKELY (nb_written < nb_read)) {
3518 len = tny_stream_write (fidata->output_stream, buffer + nb_written,
3519 nb_read - nb_written);
3520 if (G_UNLIKELY (len < 0))
3526 tny_stream_close (cache_stream);
3527 g_object_unref (cache_stream);
3530 tny_stream_close (fidata->output_stream);
3531 g_object_unref (fidata->output_stream);
3533 g_idle_add (on_fetch_image_idle_refresh_view, fidata);
3539 on_fetch_image (ModestMsgView *msgview,
3542 ModestMsgViewWindow *window)
3544 const gchar *current_account;
3545 ModestMsgViewWindowPrivate *priv;
3546 FetchImageData *fidata;
3548 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
3550 current_account = modest_window_get_active_account (MODEST_WINDOW (window));
3552 fidata = g_slice_new0 (FetchImageData);
3553 fidata->msg_view = g_object_ref (msgview);
3554 fidata->window = g_object_ref (window);
3555 fidata->uri = g_strdup (uri);
3556 fidata->cache_id = modest_images_cache_get_id (current_account, uri);
3557 fidata->output_stream = g_object_ref (stream);
3559 priv->fetching_images++;
3560 if (g_thread_create (on_fetch_image_thread, fidata, FALSE, NULL) == NULL) {
3561 g_object_unref (fidata->output_stream);
3562 g_free (fidata->cache_id);
3563 g_free (fidata->uri);
3564 g_object_unref (fidata->msg_view);
3565 g_slice_free (FetchImageData, fidata);
3566 tny_stream_close (stream);
3567 priv->fetching_images--;
3568 update_progress_hint (window);
3571 update_progress_hint (window);
3577 setup_menu (ModestMsgViewWindow *self)
3579 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW(self));
3581 /* Settings menu buttons */
3582 modest_hildon2_window_add_to_menu (MODEST_HILDON2_WINDOW (self), _("mcen_me_viewer_find"), NULL,
3583 APP_MENU_CALLBACK (modest_msg_view_window_show_find_toolbar),
3584 MODEST_DIMMING_CALLBACK (modest_ui_dimming_rules_on_find_in_msg));
3586 modest_hildon2_window_add_to_menu (MODEST_HILDON2_WINDOW (self),
3587 dngettext(GETTEXT_PACKAGE,
3588 "mcen_me_move_message",
3589 "mcen_me_move_messages",
3592 APP_MENU_CALLBACK (modest_ui_actions_on_move_to),
3593 MODEST_DIMMING_CALLBACK (modest_ui_dimming_rules_on_move_to));
3595 modest_hildon2_window_add_to_menu (MODEST_HILDON2_WINDOW (self), _("mcen_me_inbox_mark_as_read"), NULL,
3596 APP_MENU_CALLBACK (modest_ui_actions_on_mark_as_read),
3597 MODEST_DIMMING_CALLBACK (modest_ui_dimming_rules_on_mark_as_read_msg_in_view));
3599 modest_hildon2_window_add_to_menu (MODEST_HILDON2_WINDOW (self), _("mcen_me_inbox_mark_as_unread"), NULL,
3600 APP_MENU_CALLBACK (modest_ui_actions_on_mark_as_unread),
3601 MODEST_DIMMING_CALLBACK (modest_ui_dimming_rules_on_mark_as_unread_msg_in_view));
3603 modest_hildon2_window_add_to_menu (MODEST_HILDON2_WINDOW (self), _("mcen_me_viewer_save_attachments"), NULL,
3604 APP_MENU_CALLBACK (modest_ui_actions_save_attachments),
3605 MODEST_DIMMING_CALLBACK (modest_ui_dimming_rules_on_save_attachments));
3606 modest_hildon2_window_add_to_menu (MODEST_HILDON2_WINDOW (self), _("mcen_me_inbox_remove_attachments"), NULL,
3607 APP_MENU_CALLBACK (modest_ui_actions_remove_attachments),
3608 MODEST_DIMMING_CALLBACK (modest_ui_dimming_rules_on_remove_attachments));
3610 modest_hildon2_window_add_to_menu (MODEST_HILDON2_WINDOW (self), _("mcen_me_new_message"), "<Control>n",
3611 APP_MENU_CALLBACK (modest_ui_actions_on_new_msg),
3612 MODEST_DIMMING_CALLBACK (modest_ui_dimming_rules_on_new_msg));
3613 modest_hildon2_window_add_to_menu (MODEST_HILDON2_WINDOW (self), _("mcen_me_viewer_addtocontacts"), NULL,
3614 APP_MENU_CALLBACK (modest_ui_actions_add_to_contacts),
3615 MODEST_DIMMING_CALLBACK (modest_ui_dimming_rules_on_add_to_contacts));
3617 modest_hildon2_window_add_to_menu (MODEST_HILDON2_WINDOW (self), _("mcen_ti_message_properties"), NULL,
3618 APP_MENU_CALLBACK (modest_ui_actions_on_details),
3619 MODEST_DIMMING_CALLBACK (modest_ui_dimming_rules_on_details));
3623 modest_msg_view_window_add_to_contacts (ModestMsgViewWindow *self)
3625 ModestMsgViewWindowPrivate *priv;
3626 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
3627 GSList *recipients = NULL;
3629 gboolean contacts_to_add = FALSE;
3631 msg = tny_msg_view_get_msg (TNY_MSG_VIEW (priv->msg_view));
3635 header = modest_msg_view_window_get_header (self);
3638 recipients = modest_tny_msg_header_get_all_recipients_list (header);
3639 g_object_unref (header);
3641 recipients = modest_tny_msg_get_all_recipients_list (msg);
3642 g_object_unref (msg);
3645 if (recipients != NULL) {
3646 GtkWidget *picker_dialog;
3647 GtkWidget *selector;
3649 gchar *selected = NULL;
3651 selector = hildon_touch_selector_new_text ();
3652 g_object_ref (selector);
3654 for (node = recipients; node != NULL; node = g_slist_next (node)) {
3655 if (!modest_address_book_has_address ((const gchar *) node->data)) {
3656 hildon_touch_selector_append_text (HILDON_TOUCH_SELECTOR (selector),
3657 (const gchar *) node->data);
3658 contacts_to_add = TRUE;
3662 if (contacts_to_add) {
3665 picker_dialog = hildon_picker_dialog_new (GTK_WINDOW (self));
3666 gtk_window_set_title (GTK_WINDOW (picker_dialog), _("mcen_me_viewer_addtocontacts"));
3668 hildon_picker_dialog_set_selector (HILDON_PICKER_DIALOG (picker_dialog),
3669 HILDON_TOUCH_SELECTOR (selector));
3671 picker_result = gtk_dialog_run (GTK_DIALOG (picker_dialog));
3673 if (picker_result == GTK_RESPONSE_OK) {
3674 selected = hildon_touch_selector_get_current_text (HILDON_TOUCH_SELECTOR (selector));
3676 gtk_widget_destroy (picker_dialog);
3679 modest_address_book_add_address (selected, (GtkWindow *) self);
3684 g_object_unref (selector);
3689 if (recipients) {g_slist_foreach (recipients, (GFunc) g_free, NULL); g_slist_free (recipients);}
3693 _modest_msg_view_window_map_event (GtkWidget *widget,
3697 ModestMsgViewWindow *self = (ModestMsgViewWindow *) userdata;
3699 update_progress_hint (self);
3705 modest_msg_view_window_fetch_images (ModestMsgViewWindow *self)
3707 ModestMsgViewWindowPrivate *priv;
3708 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
3710 modest_msg_view_request_fetch_images (MODEST_MSG_VIEW (priv->msg_view));
3714 modest_msg_view_window_has_blocked_external_images (ModestMsgViewWindow *self)
3716 ModestMsgViewWindowPrivate *priv;
3717 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
3719 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self), FALSE);
3721 return modest_msg_view_has_blocked_external_images (MODEST_MSG_VIEW (priv->msg_view));
3725 modest_msg_view_window_reload (ModestMsgViewWindow *self)
3727 ModestMsgViewWindowPrivate *priv;
3728 const gchar *msg_uid;
3729 TnyHeader *header = NULL;
3730 TnyFolder *folder = NULL;
3732 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self));
3734 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
3736 header = modest_msg_view_window_get_header (MODEST_MSG_VIEW_WINDOW (self));
3740 folder = tny_header_get_folder (header);
3741 g_object_unref (header);
3746 msg_uid = modest_msg_view_window_get_message_uid (self);
3748 GtkTreeRowReference *row_reference;
3750 if (priv->row_reference && gtk_tree_row_reference_valid (priv->row_reference)) {
3751 row_reference = priv->row_reference;
3753 row_reference = NULL;
3755 if (!message_reader (self, priv, NULL, msg_uid, folder, row_reference))
3756 g_warning ("Shouldn't happen, trying to reload a message failed");
3759 g_object_unref (folder);
3763 update_branding (ModestMsgViewWindow *self)
3765 const gchar *account;
3766 const gchar *mailbox;
3767 ModestAccountMgr *mgr;
3768 ModestProtocol *protocol = NULL;
3769 gchar *service_name = NULL;
3770 const GdkPixbuf *service_icon = NULL;
3771 ModestMsgViewWindowPrivate *priv;
3773 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
3775 account = modest_window_get_active_account (MODEST_WINDOW (self));
3776 mailbox = modest_window_get_active_mailbox (MODEST_WINDOW (self));
3778 mgr = modest_runtime_get_account_mgr ();
3780 if (modest_account_mgr_account_is_multimailbox (mgr, account, &protocol)) {
3781 if (MODEST_IS_ACCOUNT_PROTOCOL (protocol)) {
3782 service_name = modest_account_protocol_get_service_name (MODEST_ACCOUNT_PROTOCOL (protocol),
3784 service_icon = modest_account_protocol_get_service_icon (MODEST_ACCOUNT_PROTOCOL (protocol),
3785 account, mailbox, MODEST_ICON_SIZE_SMALL);
3789 modest_msg_view_set_branding (MODEST_MSG_VIEW (priv->msg_view), service_name, service_icon);
3790 g_free (service_name);