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 g_object_unref (self);
2165 /* If there was any error */
2166 if (!modest_ui_actions_msg_retrieval_check (mail_op, header, msg)) {
2168 gtk_tree_row_reference_free (row_reference);
2169 self = (ModestMsgViewWindow *) modest_mail_operation_get_source (mail_op);
2171 /* Restore window title */
2172 update_window_title (self);
2173 g_object_unref (self);
2178 /* Get the window */
2179 self = (ModestMsgViewWindow *) modest_mail_operation_get_source (mail_op);
2180 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self));
2181 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
2183 /* Update the row reference */
2184 if (priv->row_reference != NULL) {
2185 gtk_tree_row_reference_free (priv->row_reference);
2186 priv->row_reference = (row_reference && gtk_tree_row_reference_valid (row_reference))?gtk_tree_row_reference_copy (row_reference):NULL;
2187 if (priv->next_row_reference != NULL) {
2188 gtk_tree_row_reference_free (priv->next_row_reference);
2190 if (priv->row_reference) {
2191 priv->next_row_reference = gtk_tree_row_reference_copy (priv->row_reference);
2192 select_next_valid_row (priv->header_model, &(priv->next_row_reference), TRUE, priv->is_outbox);
2194 priv->next_row_reference = NULL;
2198 /* Mark header as read */
2199 if (!(tny_header_get_flags (header) & TNY_HEADER_FLAG_SEEN))
2200 tny_header_set_flag (header, TNY_HEADER_FLAG_SEEN);
2202 /* Set new message */
2203 if (priv->msg_view != NULL && TNY_IS_MSG_VIEW (priv->msg_view)) {
2204 tny_msg_view_set_msg (TNY_MSG_VIEW (priv->msg_view), msg);
2205 modest_msg_view_window_update_priority (self);
2206 update_window_title (MODEST_MSG_VIEW_WINDOW (self));
2207 update_branding (MODEST_MSG_VIEW_WINDOW (self));
2208 modest_msg_view_grab_focus (MODEST_MSG_VIEW (priv->msg_view));
2211 /* Set the new message uid of the window */
2212 if (priv->msg_uid) {
2213 g_free (priv->msg_uid);
2214 priv->msg_uid = modest_tny_folder_get_header_unique_id (header);
2217 /* Notify the observers */
2218 g_signal_emit (G_OBJECT (self), signals[MSG_CHANGED_SIGNAL],
2219 0, priv->header_model, priv->row_reference);
2222 g_object_unref (self);
2224 gtk_tree_row_reference_free (row_reference);
2228 modest_msg_view_window_get_folder_type (ModestMsgViewWindow *window)
2230 ModestMsgViewWindowPrivate *priv;
2232 TnyFolderType folder_type;
2234 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
2236 folder_type = TNY_FOLDER_TYPE_UNKNOWN;
2238 msg = tny_msg_view_get_msg (TNY_MSG_VIEW (priv->msg_view));
2242 folder = tny_msg_get_folder (msg);
2244 folder_type = modest_tny_folder_guess_folder_type (folder);
2245 g_object_unref (folder);
2247 g_object_unref (msg);
2255 modest_msg_view_window_update_priority (ModestMsgViewWindow *window)
2257 ModestMsgViewWindowPrivate *priv;
2258 TnyHeader *header = NULL;
2259 TnyHeaderFlags flags = 0;
2261 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
2263 if (priv->header_model && priv->row_reference && gtk_tree_row_reference_valid (priv->row_reference)) {
2265 GtkTreePath *path = NULL;
2267 path = gtk_tree_row_reference_get_path (priv->row_reference);
2268 g_return_if_fail (path != NULL);
2269 gtk_tree_model_get_iter (priv->header_model,
2271 gtk_tree_row_reference_get_path (priv->row_reference));
2273 gtk_tree_model_get (priv->header_model, &iter, TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
2275 gtk_tree_path_free (path);
2278 msg = tny_msg_view_get_msg (TNY_MSG_VIEW (priv->msg_view));
2280 header = tny_msg_get_header (msg);
2281 g_object_unref (msg);
2286 flags = tny_header_get_flags (header);
2287 g_object_unref(G_OBJECT(header));
2290 modest_msg_view_set_priority (MODEST_MSG_VIEW(priv->msg_view), flags);
2295 toolbar_resize (ModestMsgViewWindow *self)
2297 ModestMsgViewWindowPrivate *priv = NULL;
2298 ModestWindowPrivate *parent_priv = NULL;
2300 gint static_button_size;
2301 ModestWindowMgr *mgr;
2303 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self));
2304 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
2305 parent_priv = MODEST_WINDOW_GET_PRIVATE(self);
2307 mgr = modest_runtime_get_window_mgr ();
2308 static_button_size = modest_window_mgr_get_fullscreen_mode (mgr)?120:120;
2310 if (parent_priv->toolbar) {
2311 /* Set expandable and homogeneous tool buttons */
2312 widget = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/ToolbarMessageReply");
2313 gtk_tool_item_set_expand (GTK_TOOL_ITEM (widget), TRUE);
2314 gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (widget), TRUE);
2315 widget = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/ToolbarMessageReplyAll");
2316 gtk_tool_item_set_expand (GTK_TOOL_ITEM (widget), TRUE);
2317 gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (widget), TRUE);
2318 widget = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/ToolbarMessageForward");
2319 gtk_tool_item_set_expand (GTK_TOOL_ITEM (widget), TRUE);
2320 gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (widget), TRUE);
2321 widget = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/ToolbarDeleteMessage");
2322 gtk_tool_item_set_expand (GTK_TOOL_ITEM (widget), TRUE);
2323 gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (widget), TRUE);
2324 widget = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/ToolbarDownloadExternalImages");
2325 gtk_tool_item_set_expand (GTK_TOOL_ITEM (widget), TRUE);
2326 gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (widget), TRUE);
2327 gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (priv->next_toolitem), TRUE);
2328 gtk_tool_item_set_expand (GTK_TOOL_ITEM (priv->next_toolitem), TRUE);
2329 gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (priv->prev_toolitem), TRUE);
2330 gtk_tool_item_set_expand (GTK_TOOL_ITEM (priv->prev_toolitem), TRUE);
2335 modest_msg_view_window_show_toolbar (ModestWindow *self,
2336 gboolean show_toolbar)
2338 ModestMsgViewWindowPrivate *priv = NULL;
2339 ModestWindowPrivate *parent_priv;
2341 parent_priv = MODEST_WINDOW_GET_PRIVATE(self);
2342 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(self);
2344 /* Set optimized view status */
2345 priv->optimized_view = !show_toolbar;
2347 if (!parent_priv->toolbar) {
2348 parent_priv->toolbar = gtk_ui_manager_get_widget (parent_priv->ui_manager,
2350 gtk_toolbar_set_icon_size (GTK_TOOLBAR (parent_priv->toolbar), HILDON_ICON_SIZE_FINGER);
2351 gtk_widget_set_no_show_all (parent_priv->toolbar, TRUE);
2353 priv->next_toolitem = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/ToolbarMessageNext");
2354 priv->prev_toolitem = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/ToolbarMessageBack");
2355 toolbar_resize (MODEST_MSG_VIEW_WINDOW (self));
2358 hildon_window_add_toolbar (HILDON_WINDOW (self),
2359 GTK_TOOLBAR (parent_priv->toolbar));
2364 /* Quick hack: this prevents toolbar icons "dance" when progress bar show status is changed */
2365 /* TODO: resize mode migth be GTK_RESIZE_QUEUE, in order to avoid unneccesary shows */
2366 gtk_container_set_resize_mode (GTK_CONTAINER(parent_priv->toolbar), GTK_RESIZE_IMMEDIATE);
2368 gtk_widget_show (GTK_WIDGET (parent_priv->toolbar));
2369 if (modest_msg_view_window_transfer_mode_enabled (MODEST_MSG_VIEW_WINDOW (self)))
2370 set_progress_hint (MODEST_MSG_VIEW_WINDOW (self), TRUE);
2372 set_progress_hint (MODEST_MSG_VIEW_WINDOW (self), FALSE);
2375 gtk_widget_set_no_show_all (parent_priv->toolbar, TRUE);
2376 gtk_widget_hide (GTK_WIDGET (parent_priv->toolbar));
2381 modest_msg_view_window_clipboard_owner_change (GtkClipboard *clipboard,
2383 ModestMsgViewWindow *window)
2385 if (!GTK_WIDGET_VISIBLE (window))
2388 modest_window_check_dimming_rules_group (MODEST_WINDOW (window), MODEST_DIMMING_RULES_CLIPBOARD);
2392 modest_msg_view_window_transfer_mode_enabled (ModestMsgViewWindow *self)
2394 ModestMsgViewWindowPrivate *priv;
2396 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self), FALSE);
2397 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(self);
2399 return priv->progress_hint;
2403 observers_empty (ModestMsgViewWindow *self)
2406 ModestMsgViewWindowPrivate *priv;
2407 gboolean is_empty = TRUE;
2408 guint pending_ops = 0;
2410 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(self);
2411 tmp = priv->progress_widgets;
2413 /* Check all observers */
2414 while (tmp && is_empty) {
2415 pending_ops = modest_progress_object_num_pending_operations (MODEST_PROGRESS_OBJECT(tmp->data));
2416 is_empty = pending_ops == 0;
2418 tmp = g_slist_next(tmp);
2425 on_account_removed (TnyAccountStore *account_store,
2426 TnyAccount *account,
2429 /* Do nothing if it's a transport account, because we only
2430 show the messages of a store account */
2431 if (tny_account_get_account_type(account) == TNY_ACCOUNT_TYPE_STORE) {
2432 const gchar *parent_acc = NULL;
2433 const gchar *our_acc = NULL;
2435 our_acc = modest_window_get_active_account (MODEST_WINDOW (user_data));
2436 parent_acc = modest_tny_account_get_parent_modest_account_name_for_server_account (account);
2438 /* Close this window if I'm showing a message of the removed account */
2439 if (our_acc && parent_acc && strcmp (parent_acc, our_acc) == 0)
2440 modest_ui_actions_on_close_window (NULL, MODEST_WINDOW (user_data));
2445 on_mail_operation_started (ModestMailOperation *mail_op,
2448 ModestMsgViewWindow *self;
2449 ModestMailOperationTypeOperation op_type;
2451 ModestMsgViewWindowPrivate *priv;
2452 GObject *source = NULL;
2454 self = MODEST_MSG_VIEW_WINDOW (user_data);
2455 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
2456 op_type = modest_mail_operation_get_type_operation (mail_op);
2457 tmp = priv->progress_widgets;
2458 source = modest_mail_operation_get_source(mail_op);
2459 if (G_OBJECT (self) == source) {
2460 if (op_type == MODEST_MAIL_OPERATION_TYPE_RECEIVE ||
2461 op_type == MODEST_MAIL_OPERATION_TYPE_OPEN ||
2462 op_type == MODEST_MAIL_OPERATION_TYPE_DELETE) {
2463 set_progress_hint (self, TRUE);
2465 modest_progress_object_add_operation (
2466 MODEST_PROGRESS_OBJECT (tmp->data),
2468 tmp = g_slist_next (tmp);
2472 g_object_unref (source);
2474 /* Update dimming rules */
2475 check_dimming_rules_after_change (self);
2479 on_mail_operation_finished (ModestMailOperation *mail_op,
2482 ModestMsgViewWindow *self;
2483 ModestMailOperationTypeOperation op_type;
2485 ModestMsgViewWindowPrivate *priv;
2487 self = MODEST_MSG_VIEW_WINDOW (user_data);
2488 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
2489 op_type = modest_mail_operation_get_type_operation (mail_op);
2490 tmp = priv->progress_widgets;
2492 if (op_type == MODEST_MAIL_OPERATION_TYPE_RECEIVE ||
2493 op_type == MODEST_MAIL_OPERATION_TYPE_OPEN ||
2494 op_type == MODEST_MAIL_OPERATION_TYPE_DELETE) {
2496 modest_progress_object_remove_operation (MODEST_PROGRESS_OBJECT (tmp->data),
2498 tmp = g_slist_next (tmp);
2501 /* If no more operations are being observed, NORMAL mode is enabled again */
2502 if (observers_empty (self)) {
2503 set_progress_hint (self, FALSE);
2507 /* Update dimming rules. We have to do this right here
2508 and not in view_msg_cb because at that point the
2509 transfer mode is still enabled so the dimming rule
2510 won't let the user delete the message that has been
2511 readed for example */
2512 check_dimming_rules_after_change (self);
2516 on_queue_changed (ModestMailOperationQueue *queue,
2517 ModestMailOperation *mail_op,
2518 ModestMailOperationQueueNotification type,
2519 ModestMsgViewWindow *self)
2521 ModestMsgViewWindowPrivate *priv;
2523 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
2525 /* If this operations was created by another window, do nothing */
2526 if (!modest_mail_operation_is_mine (mail_op, G_OBJECT(self)))
2529 if (type == MODEST_MAIL_OPERATION_QUEUE_OPERATION_ADDED) {
2530 priv->sighandlers = modest_signal_mgr_connect (priv->sighandlers,
2532 "operation-started",
2533 G_CALLBACK (on_mail_operation_started),
2535 priv->sighandlers = modest_signal_mgr_connect (priv->sighandlers,
2537 "operation-finished",
2538 G_CALLBACK (on_mail_operation_finished),
2540 } else if (type == MODEST_MAIL_OPERATION_QUEUE_OPERATION_REMOVED) {
2541 priv->sighandlers = modest_signal_mgr_disconnect (priv->sighandlers,
2543 "operation-started");
2544 priv->sighandlers = modest_signal_mgr_disconnect (priv->sighandlers,
2546 "operation-finished");
2551 modest_msg_view_window_get_attachments (ModestMsgViewWindow *win)
2553 ModestMsgViewWindowPrivate *priv;
2554 TnyList *selected_attachments = NULL;
2556 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (win), NULL);
2557 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (win);
2559 /* In Hildon 2.2 as there's no selection we assume we have all attachments selected */
2560 selected_attachments = modest_msg_view_get_attachments (MODEST_MSG_VIEW (priv->msg_view));
2562 return selected_attachments;
2566 ModestMsgViewWindow *self;
2568 gchar *attachment_uid;
2569 } DecodeAsyncHelper;
2572 on_decode_to_stream_async_handler (TnyMimePart *mime_part,
2578 DecodeAsyncHelper *helper = (DecodeAsyncHelper *) user_data;
2579 const gchar *content_type;
2581 /* It could happen that the window was closed */
2582 if (GTK_WIDGET_VISIBLE (helper->self))
2583 set_progress_hint (helper->self, FALSE);
2585 if (cancelled || err) {
2587 gchar *msg = g_strdup_printf (_KR("cerm_device_memory_full"), "");
2588 modest_platform_information_banner (NULL, NULL, msg);
2594 content_type = tny_mime_part_get_content_type (mime_part);
2595 if ((g_str_has_prefix (content_type, "message/rfc822") ||
2596 g_str_has_prefix (content_type, "multipart/") ||
2597 g_str_has_prefix (content_type, "text/"))) {
2598 ModestWindowMgr *mgr;
2599 ModestWindow *msg_win = NULL;
2602 const gchar *mailbox;
2603 TnyStream *file_stream;
2606 fd = g_open (helper->file_path, O_RDONLY, 0644);
2608 file_stream = tny_fs_stream_new (fd);
2610 mgr = modest_runtime_get_window_mgr ();
2612 account = g_strdup (modest_window_get_active_account (MODEST_WINDOW (helper->self)));
2613 mailbox = modest_window_get_active_mailbox (MODEST_WINDOW (helper->self));
2616 account = modest_account_mgr_get_default_account (modest_runtime_get_account_mgr ());
2618 msg = tny_camel_msg_new ();
2619 tny_camel_msg_parse (msg, file_stream);
2620 msg_win = modest_msg_view_window_new_for_attachment (TNY_MSG (msg), account, mailbox, helper->attachment_uid);
2621 modest_window_set_zoom (MODEST_WINDOW (msg_win),
2622 modest_window_get_zoom (MODEST_WINDOW (helper->self)));
2623 if (modest_window_mgr_register_window (mgr, msg_win, MODEST_WINDOW (helper->self)))
2624 gtk_widget_show_all (GTK_WIDGET (msg_win));
2626 gtk_widget_destroy (GTK_WIDGET (msg_win));
2627 g_object_unref (file_stream);
2632 /* make the file read-only */
2633 g_chmod(helper->file_path, 0444);
2635 /* Activate the file */
2636 modest_platform_activate_file (helper->file_path, tny_mime_part_get_content_type (mime_part));
2641 g_object_unref (helper->self);
2642 g_free (helper->file_path);
2643 g_free (helper->attachment_uid);
2644 g_slice_free (DecodeAsyncHelper, helper);
2648 modest_msg_view_window_view_attachment (ModestMsgViewWindow *window,
2649 TnyMimePart *mime_part)
2651 ModestMsgViewWindowPrivate *priv;
2652 const gchar *msg_uid;
2653 gchar *attachment_uid = NULL;
2654 gint attachment_index = 0;
2655 TnyList *attachments;
2657 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window));
2658 g_return_if_fail (TNY_IS_MIME_PART (mime_part) || (mime_part == NULL));
2659 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
2661 msg_uid = modest_msg_view_window_get_message_uid (MODEST_MSG_VIEW_WINDOW (window));
2662 attachments = modest_msg_view_get_attachments (MODEST_MSG_VIEW (priv->msg_view));
2663 attachment_index = modest_list_index (attachments, (GObject *) mime_part);
2664 g_object_unref (attachments);
2666 if (msg_uid && attachment_index >= 0) {
2667 attachment_uid = g_strdup_printf ("%s/%d", msg_uid, attachment_index);
2670 if (mime_part == NULL) {
2671 gboolean error = FALSE;
2672 TnyList *selected_attachments = modest_msg_view_get_selected_attachments (MODEST_MSG_VIEW (priv->msg_view));
2673 if (selected_attachments == NULL || tny_list_get_length (selected_attachments) == 0) {
2675 } else if (tny_list_get_length (selected_attachments) > 1) {
2676 hildon_banner_show_information (NULL, NULL, _("mcen_ib_unable_to_display_more"));
2680 iter = tny_list_create_iterator (selected_attachments);
2681 mime_part = (TnyMimePart *) tny_iterator_get_current (iter);
2682 g_object_unref (iter);
2684 if (selected_attachments)
2685 g_object_unref (selected_attachments);
2690 g_object_ref (mime_part);
2693 if (tny_mime_part_is_purged (mime_part))
2696 if (!modest_tny_mime_part_is_msg (mime_part) && tny_mime_part_get_filename (mime_part)) {
2697 gchar *filepath = NULL;
2698 const gchar *att_filename = tny_mime_part_get_filename (mime_part);
2699 gboolean show_error_banner = FALSE;
2700 TnyFsStream *temp_stream = NULL;
2701 temp_stream = modest_utils_create_temp_stream (att_filename, attachment_uid,
2704 if (temp_stream != NULL) {
2705 ModestAccountMgr *mgr;
2706 DecodeAsyncHelper *helper;
2707 gboolean decode_in_provider;
2708 ModestProtocol *protocol;
2709 const gchar *account;
2711 /* Activate progress hint */
2712 set_progress_hint (window, TRUE);
2714 helper = g_slice_new0 (DecodeAsyncHelper);
2715 helper->self = g_object_ref (window);
2716 helper->file_path = g_strdup (filepath);
2717 helper->attachment_uid = g_strdup (attachment_uid);
2719 decode_in_provider = FALSE;
2720 mgr = modest_runtime_get_account_mgr ();
2721 account = modest_window_get_active_account (MODEST_WINDOW (window));
2722 if (modest_account_mgr_account_is_multimailbox (mgr, account, &protocol)) {
2723 if (MODEST_IS_ACCOUNT_PROTOCOL (protocol)) {
2725 uri = g_strconcat ("file://", filepath, NULL);
2726 decode_in_provider =
2727 modest_account_protocol_decode_part_to_stream_async (
2728 MODEST_ACCOUNT_PROTOCOL (protocol),
2731 TNY_STREAM (temp_stream),
2732 on_decode_to_stream_async_handler,
2739 if (!decode_in_provider)
2740 tny_mime_part_decode_to_stream_async (mime_part, TNY_STREAM (temp_stream),
2741 on_decode_to_stream_async_handler,
2744 g_object_unref (temp_stream);
2745 /* NOTE: files in the temporary area will be automatically
2746 * cleaned after some time if they are no longer in use */
2749 const gchar *content_type;
2750 /* the file may already exist but it isn't writable,
2751 * let's try to open it anyway */
2752 content_type = tny_mime_part_get_content_type (mime_part);
2753 modest_platform_activate_file (filepath, content_type);
2755 g_warning ("%s: modest_utils_create_temp_stream failed", __FUNCTION__);
2756 show_error_banner = TRUE;
2761 if (show_error_banner)
2762 modest_platform_information_banner (NULL, NULL, _("mail_ib_file_operation_failed"));
2763 } else if (!modest_tny_mime_part_is_msg (mime_part)) {
2764 ModestWindowMgr *mgr;
2765 ModestWindow *msg_win = NULL;
2766 TnyMsg *current_msg;
2770 current_msg = modest_msg_view_window_get_message (MODEST_MSG_VIEW_WINDOW (window));
2771 mgr = modest_runtime_get_window_mgr ();
2772 header = tny_msg_get_header (TNY_MSG (current_msg));
2773 found = modest_window_mgr_find_registered_message_uid (mgr,
2778 g_debug ("window for this body is already being created");
2781 /* it's not found, so create a new window for it */
2782 modest_window_mgr_register_header (mgr, header, attachment_uid); /* register the uid before building the window */
2783 gchar *account = g_strdup (modest_window_get_active_account (MODEST_WINDOW (window)));
2784 const gchar *mailbox = modest_window_get_active_mailbox (MODEST_WINDOW (window));
2786 account = modest_account_mgr_get_default_account (modest_runtime_get_account_mgr ());
2788 msg_win = modest_msg_view_window_new_with_other_body (TNY_MSG (current_msg), TNY_MIME_PART (mime_part),
2789 account, mailbox, attachment_uid);
2791 modest_window_set_zoom (MODEST_WINDOW (msg_win),
2792 modest_window_get_zoom (MODEST_WINDOW (window)));
2793 if (modest_window_mgr_register_window (mgr, msg_win, MODEST_WINDOW (window)))
2794 gtk_widget_show_all (GTK_WIDGET (msg_win));
2796 gtk_widget_destroy (GTK_WIDGET (msg_win));
2798 g_object_unref (current_msg);
2800 /* message attachment */
2801 TnyHeader *header = NULL;
2802 ModestWindowMgr *mgr;
2803 ModestWindow *msg_win = NULL;
2806 header = tny_msg_get_header (TNY_MSG (mime_part));
2807 mgr = modest_runtime_get_window_mgr ();
2808 found = modest_window_mgr_find_registered_header (mgr, header, &msg_win);
2811 /* if it's found, but there is no msg_win, it's probably in the process of being created;
2812 * thus, we don't do anything */
2813 g_debug ("window for is already being created");
2815 /* it's not found, so create a new window for it */
2816 modest_window_mgr_register_header (mgr, header, attachment_uid); /* register the uid before building the window */
2817 gchar *account = g_strdup (modest_window_get_active_account (MODEST_WINDOW (window)));
2818 const gchar *mailbox = modest_window_get_active_mailbox (MODEST_WINDOW (window));
2820 account = modest_account_mgr_get_default_account (modest_runtime_get_account_mgr ());
2821 msg_win = modest_msg_view_window_new_for_attachment (TNY_MSG (mime_part), account,
2822 mailbox, attachment_uid);
2823 modest_window_set_zoom (MODEST_WINDOW (msg_win),
2824 modest_window_get_zoom (MODEST_WINDOW (window)));
2825 if (modest_window_mgr_register_window (mgr, msg_win, MODEST_WINDOW (window)))
2826 gtk_widget_show_all (GTK_WIDGET (msg_win));
2828 gtk_widget_destroy (GTK_WIDGET (msg_win));
2834 g_free (attachment_uid);
2836 g_object_unref (mime_part);
2848 GnomeVFSResult result;
2850 ModestMsgViewWindow *window;
2853 static void save_mime_part_info_free (SaveMimePartInfo *info, gboolean with_struct);
2854 static gboolean idle_save_mime_part_show_result (SaveMimePartInfo *info);
2855 static gpointer save_mime_part_to_file (SaveMimePartInfo *info);
2856 static void save_mime_parts_to_file_with_checks (GtkWindow *parent, SaveMimePartInfo *info);
2859 save_mime_part_info_free (SaveMimePartInfo *info, gboolean with_struct)
2863 for (node = info->pairs; node != NULL; node = g_list_next (node)) {
2864 SaveMimePartPair *pair = (SaveMimePartPair *) node->data;
2865 g_free (pair->filename);
2866 g_object_unref (pair->part);
2867 g_slice_free (SaveMimePartPair, pair);
2869 g_list_free (info->pairs);
2872 g_object_unref (info->window);
2873 info->window = NULL;
2875 g_slice_free (SaveMimePartInfo, info);
2880 idle_save_mime_part_show_result (SaveMimePartInfo *info)
2882 /* This is a GDK lock because we are an idle callback and
2883 * hildon_banner_show_information is or does Gtk+ code */
2885 gdk_threads_enter (); /* CHECKED */
2886 if (info->result == GNOME_VFS_OK) {
2887 hildon_banner_show_information (NULL, NULL, _CS("sfil_ib_saved"));
2888 } else if (info->result == GNOME_VFS_ERROR_NO_SPACE) {
2891 /* Check if the uri belongs to the external mmc */
2892 if (g_str_has_prefix (info->uri, g_getenv (MODEST_MMC1_VOLUMEPATH_ENV)))
2893 msg = g_strdup_printf (_KR("cerm_device_memory_full"), "");
2895 msg = g_strdup (_KR("cerm_memory_card_full"));
2896 modest_platform_information_banner (NULL, NULL, msg);
2899 hildon_banner_show_information (NULL, NULL, _("mail_ib_file_operation_failed"));
2901 save_mime_part_info_free (info, FALSE);
2902 gdk_threads_leave (); /* CHECKED */
2908 save_mime_part_to_file (SaveMimePartInfo *info)
2910 GnomeVFSHandle *handle;
2912 SaveMimePartPair *pair = (SaveMimePartPair *) info->pairs->data;
2914 info->result = gnome_vfs_create (&handle, pair->filename, GNOME_VFS_OPEN_WRITE, FALSE, 0644);
2915 if (info->result == GNOME_VFS_OK) {
2916 GError *error = NULL;
2917 gboolean decode_in_provider;
2919 ModestAccountMgr *mgr;
2920 const gchar *account;
2921 ModestProtocol *protocol = NULL;
2923 stream = tny_vfs_stream_new (handle);
2925 decode_in_provider = FALSE;
2926 mgr = modest_runtime_get_account_mgr ();
2927 account = modest_window_get_active_account (MODEST_WINDOW (info->window));
2928 if (modest_account_mgr_account_is_multimailbox (mgr, account, &protocol)) {
2929 if (MODEST_IS_ACCOUNT_PROTOCOL (protocol)) {
2930 decode_in_provider =
2931 modest_account_protocol_decode_part_to_stream (
2932 MODEST_ACCOUNT_PROTOCOL (protocol),
2940 if (!decode_in_provider)
2941 written = tny_mime_part_decode_to_stream (pair->part, stream, &error);
2944 g_warning ("modest: could not save attachment %s: %d (%s)\n", pair->filename, error?error->code:-1, error?error->message:"Unknown error");
2946 if ((error->domain == TNY_ERROR_DOMAIN) &&
2947 (error->code == TNY_IO_ERROR_WRITE) &&
2948 (errno == ENOSPC)) {
2949 info->result = GNOME_VFS_ERROR_NO_SPACE;
2951 info->result = GNOME_VFS_ERROR_IO;
2954 g_object_unref (G_OBJECT (stream));
2956 g_warning ("Could not create save attachment %s: %s\n",
2957 pair->filename, gnome_vfs_result_to_string (info->result));
2960 /* Go on saving remaining files */
2961 info->pairs = g_list_remove_link (info->pairs, info->pairs);
2962 if (info->pairs != NULL) {
2963 save_mime_part_to_file (info);
2965 g_idle_add ((GSourceFunc) idle_save_mime_part_show_result, info);
2972 save_mime_parts_to_file_with_checks (GtkWindow *parent,
2973 SaveMimePartInfo *info)
2975 gboolean is_ok = TRUE;
2976 gint replaced_files = 0;
2977 const GList *files = info->pairs;
2978 const GList *iter, *to_replace = NULL;
2980 for (iter = files; (iter != NULL) && (replaced_files < 2); iter = g_list_next(iter)) {
2981 SaveMimePartPair *pair = iter->data;
2982 gchar *unescaped = g_uri_unescape_string (pair->filename, NULL);
2984 if (modest_utils_file_exists (unescaped)) {
2986 if (replaced_files == 1)
2991 if (replaced_files) {
2994 if (replaced_files == 1) {
2995 SaveMimePartPair *pair = to_replace->data;
2996 const gchar *basename = strrchr (pair->filename, G_DIR_SEPARATOR) + 1;
2997 gchar *escaped_basename, *message;
2999 escaped_basename = g_uri_unescape_string (basename, NULL);
3000 message = g_strdup_printf ("%s\n%s",
3001 _FM("docm_nc_replace_file"),
3002 (escaped_basename) ? escaped_basename : "");
3003 response = modest_platform_run_confirmation_dialog (parent, message);
3005 g_free (escaped_basename);
3007 response = modest_platform_run_confirmation_dialog (parent,
3008 _FM("docm_nc_replace_multiple"));
3010 if (response != GTK_RESPONSE_OK)
3015 save_mime_part_info_free (info, TRUE);
3017 g_thread_create ((GThreadFunc)save_mime_part_to_file, info, FALSE, NULL);
3022 typedef struct _SaveAttachmentsInfo {
3023 TnyList *attachments_list;
3024 ModestMsgViewWindow *window;
3025 } SaveAttachmentsInfo;
3028 save_attachments_response (GtkDialog *dialog,
3032 TnyList *mime_parts;
3034 GList *files_to_save = NULL;
3035 gchar *current_folder;
3036 SaveAttachmentsInfo *sa_info = (SaveAttachmentsInfo *) user_data;
3038 mime_parts = TNY_LIST (sa_info->attachments_list);
3040 if (arg1 != GTK_RESPONSE_OK)
3043 chooser_uri = gtk_file_chooser_get_uri (GTK_FILE_CHOOSER (dialog));
3044 current_folder = gtk_file_chooser_get_current_folder_uri (GTK_FILE_CHOOSER (dialog));
3045 if (current_folder && *current_folder != '\0') {
3047 modest_conf_set_string (modest_runtime_get_conf (), MODEST_CONF_LATEST_SAVE_ATTACHMENT_PATH,
3048 current_folder,&err);
3050 g_debug ("Error storing latest used folder: %s", err->message);
3054 g_free (current_folder);
3056 if (!modest_utils_folder_writable (chooser_uri)) {
3057 const gchar *err_msg;
3059 #ifdef MODEST_PLATFORM_MAEMO
3060 if (modest_maemo_utils_in_usb_mode ()) {
3061 err_msg = dgettext ("hildon-status-bar-usb", "usbh_ib_mmc_usb_connected");
3063 err_msg = _FM("sfil_ib_readonly_location");
3066 err_msg = _FM("sfil_ib_readonly_location");
3068 hildon_banner_show_information (NULL, NULL, err_msg);
3072 iter = tny_list_create_iterator (mime_parts);
3073 while (!tny_iterator_is_done (iter)) {
3074 TnyMimePart *mime_part = (TnyMimePart *) tny_iterator_get_current (iter);
3076 if ((modest_tny_mime_part_is_attachment_for_modest (mime_part)) &&
3077 !tny_mime_part_is_purged (mime_part) &&
3078 (tny_mime_part_get_filename (mime_part) != NULL)) {
3079 SaveMimePartPair *pair;
3081 pair = g_slice_new0 (SaveMimePartPair);
3083 if (tny_list_get_length (mime_parts) > 1) {
3085 gnome_vfs_escape_slashes (tny_mime_part_get_filename (mime_part));
3086 pair->filename = g_build_filename (chooser_uri, escaped, NULL);
3089 pair->filename = g_strdup (chooser_uri);
3091 pair->part = mime_part;
3092 files_to_save = g_list_prepend (files_to_save, pair);
3094 tny_iterator_next (iter);
3096 g_object_unref (iter);
3099 if (files_to_save != NULL) {
3100 SaveMimePartInfo *info = g_slice_new0 (SaveMimePartInfo);
3101 info->pairs = files_to_save;
3102 info->result = TRUE;
3103 info->uri = g_strdup (chooser_uri);
3104 info->window = g_object_ref (sa_info->window);
3105 save_mime_parts_to_file_with_checks ((GtkWindow *) dialog, info);
3107 g_free (chooser_uri);
3110 /* Free and close the dialog */
3111 g_object_unref (mime_parts);
3112 g_object_unref (sa_info->window);
3113 g_slice_free (SaveAttachmentsInfo, sa_info);
3114 gtk_widget_destroy (GTK_WIDGET (dialog));
3118 modest_msg_view_window_save_attachments (ModestMsgViewWindow *window,
3119 TnyList *mime_parts)
3121 ModestMsgViewWindowPrivate *priv;
3122 GtkWidget *save_dialog = NULL;
3123 gchar *conf_folder = NULL;
3124 gchar *filename = NULL;
3125 gchar *save_multiple_str = NULL;
3126 const gchar *root_folder = "file:///";
3128 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window));
3129 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
3131 if (mime_parts == NULL) {
3132 /* In Hildon 2.2 save and delete operate over all the attachments as there's no
3133 * selection available */
3134 mime_parts = modest_msg_view_get_attachments (MODEST_MSG_VIEW (priv->msg_view));
3135 if (mime_parts && !modest_maemo_utils_select_attachments (GTK_WINDOW (window), mime_parts, FALSE)) {
3136 g_object_unref (mime_parts);
3139 if (mime_parts == NULL || tny_list_get_length (mime_parts) == 0) {
3141 g_object_unref (mime_parts);
3147 g_object_ref (mime_parts);
3150 /* prepare dialog */
3151 if (tny_list_get_length (mime_parts) == 1) {
3153 /* only one attachment selected */
3154 iter = tny_list_create_iterator (mime_parts);
3155 TnyMimePart *mime_part = (TnyMimePart *) tny_iterator_get_current (iter);
3156 g_object_unref (iter);
3157 if (!modest_tny_mime_part_is_msg (mime_part) &&
3158 modest_tny_mime_part_is_attachment_for_modest (mime_part) &&
3159 !tny_mime_part_is_purged (mime_part)) {
3160 filename = g_strdup (tny_mime_part_get_filename (mime_part));
3162 /* TODO: show any error? */
3163 g_warning ("%s: Tried to save a non-file attachment", __FUNCTION__);
3164 g_object_unref (mime_parts);
3167 g_object_unref (mime_part);
3169 gint num = tny_list_get_length (mime_parts);
3170 save_multiple_str = g_strdup_printf (dngettext("hildon-fm",
3171 "sfil_va_number_of_objects_attachment",
3172 "sfil_va_number_of_objects_attachments",
3176 save_dialog = hildon_file_chooser_dialog_new (GTK_WINDOW (window),
3177 GTK_FILE_CHOOSER_ACTION_SAVE);
3179 /* Get last used folder */
3180 conf_folder = modest_conf_get_string (modest_runtime_get_conf (),
3181 MODEST_CONF_LATEST_SAVE_ATTACHMENT_PATH, NULL);
3183 /* File chooser stops working if we select "file:///" as current folder */
3184 if (conf_folder && g_ascii_strcasecmp (root_folder, conf_folder) != 0) {
3185 g_free (conf_folder);
3189 if (conf_folder && conf_folder[0] != '\0') {
3190 gtk_file_chooser_set_current_folder_uri (GTK_FILE_CHOOSER (save_dialog), conf_folder);
3193 /* Set the default folder to documents folder */
3194 docs_folder = (gchar *) g_strdup(g_get_user_special_dir (G_USER_DIRECTORY_DOCUMENTS));
3197 docs_folder = g_build_filename (g_getenv (MYDOCS_ENV), DOCS_FOLDER, NULL);
3199 gtk_file_chooser_set_current_folder (GTK_FILE_CHOOSER (save_dialog), docs_folder);
3200 g_free (docs_folder);
3202 g_free (conf_folder);
3206 gtk_file_chooser_set_current_name (GTK_FILE_CHOOSER (save_dialog),
3211 /* if multiple, set multiple string */
3212 if (save_multiple_str) {
3213 g_object_set (G_OBJECT (save_dialog), "save-multiple", save_multiple_str, NULL);
3214 gtk_window_set_title (GTK_WINDOW (save_dialog), _FM("sfil_ti_save_objects_files"));
3215 g_free (save_multiple_str);
3218 /* We must run this asynchronously, because the hildon dialog
3219 performs a gtk_dialog_run by itself which leads to gdk
3221 SaveAttachmentsInfo *sa_info;
3222 sa_info = g_slice_new (SaveAttachmentsInfo);
3223 sa_info->attachments_list = mime_parts;
3224 sa_info->window = g_object_ref (window);
3225 g_signal_connect (save_dialog, "response",
3226 G_CALLBACK (save_attachments_response), sa_info);
3228 gtk_widget_show_all (save_dialog);
3232 show_remove_attachment_information (gpointer userdata)
3234 ModestMsgViewWindow *window = (ModestMsgViewWindow *) userdata;
3235 ModestMsgViewWindowPrivate *priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
3237 /* We're outside the main lock */
3238 gdk_threads_enter ();
3240 if (priv->remove_attachment_banner != NULL) {
3241 gtk_widget_destroy (priv->remove_attachment_banner);
3242 g_object_unref (priv->remove_attachment_banner);
3245 priv->remove_attachment_banner = g_object_ref (
3246 hildon_banner_show_animation (NULL, NULL, _("mcen_me_inbox_remove_attachments")));
3248 gdk_threads_leave ();
3254 modest_msg_view_window_remove_attachments (ModestMsgViewWindow *window, gboolean get_all)
3256 ModestMsgViewWindowPrivate *priv;
3257 TnyList *mime_parts = NULL, *tmp;
3258 gchar *confirmation_message;
3264 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window));
3265 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
3267 /* In hildon 2.2 we ignore the get_all flag as we always get all attachments. This is
3268 * because we don't have selection
3270 mime_parts = modest_msg_view_get_attachments (MODEST_MSG_VIEW (priv->msg_view));
3272 /* Remove already purged messages from mime parts list. We use
3273 a copy of the list to remove items in the original one */
3274 tmp = tny_list_copy (mime_parts);
3275 iter = tny_list_create_iterator (tmp);
3276 while (!tny_iterator_is_done (iter)) {
3277 TnyMimePart *part = TNY_MIME_PART (tny_iterator_get_current (iter));
3278 if (tny_mime_part_is_purged (part))
3279 tny_list_remove (mime_parts, (GObject *) part);
3281 g_object_unref (part);
3282 tny_iterator_next (iter);
3284 g_object_unref (tmp);
3285 g_object_unref (iter);
3287 if (!modest_maemo_utils_select_attachments (GTK_WINDOW (window), mime_parts, TRUE) ||
3288 tny_list_get_length (mime_parts) == 0) {
3289 g_object_unref (mime_parts);
3293 n_attachments = tny_list_get_length (mime_parts);
3294 if (n_attachments == 1) {
3298 iter = tny_list_create_iterator (mime_parts);
3299 part = (TnyMimePart *) tny_iterator_get_current (iter);
3300 g_object_unref (iter);
3301 if (modest_tny_mime_part_is_msg (part)) {
3303 header = tny_msg_get_header (TNY_MSG (part));
3304 filename = tny_header_dup_subject (header);
3305 g_object_unref (header);
3306 if (filename == NULL)
3307 filename = g_strdup (_("mail_va_no_subject"));
3309 filename = g_strdup (tny_mime_part_get_filename (TNY_MIME_PART (part)));
3311 confirmation_message = g_strdup_printf (_("mcen_nc_purge_file_text"), filename);
3313 g_object_unref (part);
3315 confirmation_message = g_strdup_printf (ngettext("mcen_nc_purge_file_text",
3316 "mcen_nc_purge_files_text",
3317 n_attachments), n_attachments);
3319 response = modest_platform_run_confirmation_dialog (GTK_WINDOW (window),
3320 confirmation_message);
3321 g_free (confirmation_message);
3323 if (response != GTK_RESPONSE_OK) {
3324 g_object_unref (mime_parts);
3328 priv->purge_timeout = g_timeout_add (2000, show_remove_attachment_information, window);
3330 iter = tny_list_create_iterator (mime_parts);
3331 while (!tny_iterator_is_done (iter)) {
3334 part = (TnyMimePart *) tny_iterator_get_current (iter);
3335 tny_mime_part_set_purged (TNY_MIME_PART (part));
3336 g_object_unref (part);
3337 tny_iterator_next (iter);
3339 g_object_unref (iter);
3341 msg = tny_msg_view_get_msg (TNY_MSG_VIEW (priv->msg_view));
3342 tny_msg_view_clear (TNY_MSG_VIEW (priv->msg_view));
3343 tny_msg_rewrite_cache (msg);
3344 tny_msg_view_set_msg (TNY_MSG_VIEW (priv->msg_view), msg);
3345 g_object_unref (msg);
3346 update_branding (MODEST_MSG_VIEW_WINDOW (window));
3348 g_object_unref (mime_parts);
3350 if (priv->purge_timeout > 0) {
3351 g_source_remove (priv->purge_timeout);
3352 priv->purge_timeout = 0;
3355 if (priv->remove_attachment_banner) {
3356 gtk_widget_destroy (priv->remove_attachment_banner);
3357 g_object_unref (priv->remove_attachment_banner);
3358 priv->remove_attachment_banner = NULL;
3364 update_window_title (ModestMsgViewWindow *window)
3366 ModestMsgViewWindowPrivate *priv;
3368 TnyHeader *header = NULL;
3369 gchar *subject = NULL;
3371 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
3373 /* Note that if the window is closed while we're retrieving
3374 the message, this widget could de deleted */
3375 if (!priv->msg_view)
3378 msg = tny_msg_view_get_msg (TNY_MSG_VIEW (priv->msg_view));
3380 if (priv->other_body) {
3383 description = modest_tny_mime_part_get_header_value (priv->other_body, "Content-Description");
3385 g_strstrip (description);
3386 subject = description;
3388 } else if (msg != NULL) {
3389 header = tny_msg_get_header (msg);
3390 subject = tny_header_dup_subject (header);
3391 g_object_unref (header);
3392 g_object_unref (msg);
3395 if ((subject == NULL)||(subject[0] == '\0')) {
3397 subject = g_strdup (_("mail_va_no_subject"));
3400 gtk_window_set_title (GTK_WINDOW (window), subject);
3405 on_move_focus (GtkWidget *widget,
3406 GtkDirectionType direction,
3409 g_signal_stop_emission_by_name (G_OBJECT (widget), "move-focus");
3413 fetch_image_open_stream (TnyStreamCache *self, gint64 *expected_size, gchar *uri)
3415 GnomeVFSResult result;
3416 GnomeVFSHandle *handle = NULL;
3417 GnomeVFSFileInfo *info = NULL;
3420 result = gnome_vfs_open (&handle, uri, GNOME_VFS_OPEN_READ);
3421 if (result != GNOME_VFS_OK) {
3426 info = gnome_vfs_file_info_new ();
3427 result = gnome_vfs_get_file_info_from_handle (handle, info, GNOME_VFS_FILE_INFO_DEFAULT);
3428 if (result != GNOME_VFS_OK || ! (info->valid_fields & GNOME_VFS_FILE_INFO_FIELDS_SIZE)) {
3429 /* We put a "safe" default size for going to cache */
3430 *expected_size = (300*1024);
3432 *expected_size = info->size;
3434 gnome_vfs_file_info_unref (info);
3436 stream = tny_vfs_stream_new (handle);
3445 TnyStream *output_stream;
3446 GtkWidget *msg_view;
3451 on_fetch_image_idle_refresh_view (gpointer userdata)
3454 FetchImageData *fidata = (FetchImageData *) userdata;
3456 gdk_threads_enter ();
3457 if (GTK_WIDGET_DRAWABLE (fidata->msg_view)) {
3458 ModestMsgViewWindowPrivate *priv;
3460 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (fidata->window);
3461 priv->fetching_images--;
3462 gtk_widget_queue_draw (fidata->msg_view);
3463 update_progress_hint (MODEST_MSG_VIEW_WINDOW (fidata->window));
3465 gdk_threads_leave ();
3467 g_object_unref (fidata->msg_view);
3468 g_object_unref (fidata->window);
3469 g_slice_free (FetchImageData, fidata);
3474 on_fetch_image_thread (gpointer userdata)
3476 FetchImageData *fidata = (FetchImageData *) userdata;
3477 TnyStreamCache *cache;
3478 TnyStream *cache_stream;
3480 cache = modest_runtime_get_images_cache ();
3482 tny_stream_cache_get_stream (cache,
3484 (TnyStreamCacheOpenStreamFetcher) fetch_image_open_stream,
3485 (gpointer) fidata->uri);
3486 g_free (fidata->cache_id);
3487 g_free (fidata->uri);
3489 if (cache_stream != NULL) {
3492 while (G_LIKELY (!tny_stream_is_eos (cache_stream))) {
3495 nb_read = tny_stream_read (cache_stream, buffer, sizeof (buffer));
3496 if (G_UNLIKELY (nb_read < 0)) {
3498 } else if (G_LIKELY (nb_read > 0)) {
3499 gssize nb_written = 0;
3501 while (G_UNLIKELY (nb_written < nb_read)) {
3504 len = tny_stream_write (fidata->output_stream, buffer + nb_written,
3505 nb_read - nb_written);
3506 if (G_UNLIKELY (len < 0))
3512 tny_stream_close (cache_stream);
3513 g_object_unref (cache_stream);
3516 tny_stream_close (fidata->output_stream);
3517 g_object_unref (fidata->output_stream);
3519 g_idle_add (on_fetch_image_idle_refresh_view, fidata);
3525 on_fetch_image (ModestMsgView *msgview,
3528 ModestMsgViewWindow *window)
3530 const gchar *current_account;
3531 ModestMsgViewWindowPrivate *priv;
3532 FetchImageData *fidata;
3534 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
3536 current_account = modest_window_get_active_account (MODEST_WINDOW (window));
3538 fidata = g_slice_new0 (FetchImageData);
3539 fidata->msg_view = g_object_ref (msgview);
3540 fidata->window = g_object_ref (window);
3541 fidata->uri = g_strdup (uri);
3542 fidata->cache_id = modest_images_cache_get_id (current_account, uri);
3543 fidata->output_stream = g_object_ref (stream);
3545 priv->fetching_images++;
3546 if (g_thread_create (on_fetch_image_thread, fidata, FALSE, NULL) == NULL) {
3547 g_object_unref (fidata->output_stream);
3548 g_free (fidata->cache_id);
3549 g_free (fidata->uri);
3550 g_object_unref (fidata->msg_view);
3551 g_slice_free (FetchImageData, fidata);
3552 tny_stream_close (stream);
3553 priv->fetching_images--;
3554 update_progress_hint (window);
3557 update_progress_hint (window);
3563 setup_menu (ModestMsgViewWindow *self)
3565 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW(self));
3567 /* Settings menu buttons */
3568 modest_hildon2_window_add_to_menu (MODEST_HILDON2_WINDOW (self), _("mcen_me_viewer_find"), NULL,
3569 APP_MENU_CALLBACK (modest_msg_view_window_show_find_toolbar),
3570 MODEST_DIMMING_CALLBACK (modest_ui_dimming_rules_on_find_in_msg));
3572 modest_hildon2_window_add_to_menu (MODEST_HILDON2_WINDOW (self),
3573 dngettext(GETTEXT_PACKAGE,
3574 "mcen_me_move_message",
3575 "mcen_me_move_messages",
3578 APP_MENU_CALLBACK (modest_ui_actions_on_move_to),
3579 MODEST_DIMMING_CALLBACK (modest_ui_dimming_rules_on_move_to));
3581 modest_hildon2_window_add_to_menu (MODEST_HILDON2_WINDOW (self), _("mcen_me_inbox_mark_as_read"), NULL,
3582 APP_MENU_CALLBACK (modest_ui_actions_on_mark_as_read),
3583 MODEST_DIMMING_CALLBACK (modest_ui_dimming_rules_on_mark_as_read_msg_in_view));
3585 modest_hildon2_window_add_to_menu (MODEST_HILDON2_WINDOW (self), _("mcen_me_inbox_mark_as_unread"), NULL,
3586 APP_MENU_CALLBACK (modest_ui_actions_on_mark_as_unread),
3587 MODEST_DIMMING_CALLBACK (modest_ui_dimming_rules_on_mark_as_unread_msg_in_view));
3589 modest_hildon2_window_add_to_menu (MODEST_HILDON2_WINDOW (self), _("mcen_me_viewer_save_attachments"), NULL,
3590 APP_MENU_CALLBACK (modest_ui_actions_save_attachments),
3591 MODEST_DIMMING_CALLBACK (modest_ui_dimming_rules_on_save_attachments));
3592 modest_hildon2_window_add_to_menu (MODEST_HILDON2_WINDOW (self), _("mcen_me_inbox_remove_attachments"), NULL,
3593 APP_MENU_CALLBACK (modest_ui_actions_remove_attachments),
3594 MODEST_DIMMING_CALLBACK (modest_ui_dimming_rules_on_remove_attachments));
3596 modest_hildon2_window_add_to_menu (MODEST_HILDON2_WINDOW (self), _("mcen_me_new_message"), "<Control>n",
3597 APP_MENU_CALLBACK (modest_ui_actions_on_new_msg),
3598 MODEST_DIMMING_CALLBACK (modest_ui_dimming_rules_on_new_msg));
3599 modest_hildon2_window_add_to_menu (MODEST_HILDON2_WINDOW (self), _("mcen_me_viewer_addtocontacts"), NULL,
3600 APP_MENU_CALLBACK (modest_ui_actions_add_to_contacts),
3601 MODEST_DIMMING_CALLBACK (modest_ui_dimming_rules_on_add_to_contacts));
3603 modest_hildon2_window_add_to_menu (MODEST_HILDON2_WINDOW (self), _("mcen_ti_message_properties"), NULL,
3604 APP_MENU_CALLBACK (modest_ui_actions_on_details),
3605 MODEST_DIMMING_CALLBACK (modest_ui_dimming_rules_on_details));
3609 modest_msg_view_window_add_to_contacts (ModestMsgViewWindow *self)
3611 ModestMsgViewWindowPrivate *priv;
3612 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
3613 GSList *recipients = NULL;
3615 gboolean contacts_to_add = FALSE;
3617 msg = tny_msg_view_get_msg (TNY_MSG_VIEW (priv->msg_view));
3621 header = modest_msg_view_window_get_header (self);
3624 recipients = modest_tny_msg_header_get_all_recipients_list (header);
3625 g_object_unref (header);
3627 recipients = modest_tny_msg_get_all_recipients_list (msg);
3628 g_object_unref (msg);
3631 if (recipients != NULL) {
3632 GtkWidget *picker_dialog;
3633 GtkWidget *selector;
3635 gchar *selected = NULL;
3637 selector = hildon_touch_selector_new_text ();
3638 g_object_ref (selector);
3640 for (node = recipients; node != NULL; node = g_slist_next (node)) {
3641 if (!modest_address_book_has_address ((const gchar *) node->data)) {
3642 hildon_touch_selector_append_text (HILDON_TOUCH_SELECTOR (selector),
3643 (const gchar *) node->data);
3644 contacts_to_add = TRUE;
3648 if (contacts_to_add) {
3651 picker_dialog = hildon_picker_dialog_new (GTK_WINDOW (self));
3652 gtk_window_set_title (GTK_WINDOW (picker_dialog), _("mcen_me_viewer_addtocontacts"));
3654 hildon_picker_dialog_set_selector (HILDON_PICKER_DIALOG (picker_dialog),
3655 HILDON_TOUCH_SELECTOR (selector));
3657 picker_result = gtk_dialog_run (GTK_DIALOG (picker_dialog));
3659 if (picker_result == GTK_RESPONSE_OK) {
3660 selected = hildon_touch_selector_get_current_text (HILDON_TOUCH_SELECTOR (selector));
3662 gtk_widget_destroy (picker_dialog);
3665 modest_address_book_add_address (selected, (GtkWindow *) self);
3670 g_object_unref (selector);
3675 if (recipients) {g_slist_foreach (recipients, (GFunc) g_free, NULL); g_slist_free (recipients);}
3679 _modest_msg_view_window_map_event (GtkWidget *widget,
3683 ModestMsgViewWindow *self = (ModestMsgViewWindow *) userdata;
3685 update_progress_hint (self);
3691 modest_msg_view_window_fetch_images (ModestMsgViewWindow *self)
3693 ModestMsgViewWindowPrivate *priv;
3694 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
3696 modest_msg_view_request_fetch_images (MODEST_MSG_VIEW (priv->msg_view));
3700 modest_msg_view_window_has_blocked_external_images (ModestMsgViewWindow *self)
3702 ModestMsgViewWindowPrivate *priv;
3703 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
3705 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self), FALSE);
3707 return modest_msg_view_has_blocked_external_images (MODEST_MSG_VIEW (priv->msg_view));
3711 modest_msg_view_window_reload (ModestMsgViewWindow *self)
3713 ModestMsgViewWindowPrivate *priv;
3714 const gchar *msg_uid;
3715 TnyHeader *header = NULL;
3716 TnyFolder *folder = NULL;
3718 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self));
3720 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
3722 header = modest_msg_view_window_get_header (MODEST_MSG_VIEW_WINDOW (self));
3726 folder = tny_header_get_folder (header);
3727 g_object_unref (header);
3732 msg_uid = modest_msg_view_window_get_message_uid (self);
3734 GtkTreeRowReference *row_reference;
3736 if (priv->row_reference && gtk_tree_row_reference_valid (priv->row_reference)) {
3737 row_reference = priv->row_reference;
3739 row_reference = NULL;
3741 if (!message_reader (self, priv, NULL, msg_uid, folder, row_reference))
3742 g_warning ("Shouldn't happen, trying to reload a message failed");
3745 g_object_unref (folder);
3749 update_branding (ModestMsgViewWindow *self)
3751 const gchar *account;
3752 const gchar *mailbox;
3753 ModestAccountMgr *mgr;
3754 ModestProtocol *protocol = NULL;
3755 gchar *service_name = NULL;
3756 const GdkPixbuf *service_icon = NULL;
3757 ModestMsgViewWindowPrivate *priv;
3759 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
3761 account = modest_window_get_active_account (MODEST_WINDOW (self));
3762 mailbox = modest_window_get_active_mailbox (MODEST_WINDOW (self));
3764 mgr = modest_runtime_get_account_mgr ();
3766 if (modest_account_mgr_account_is_multimailbox (mgr, account, &protocol)) {
3767 if (MODEST_IS_ACCOUNT_PROTOCOL (protocol)) {
3768 service_name = modest_account_protocol_get_service_name (MODEST_ACCOUNT_PROTOCOL (protocol),
3770 service_icon = modest_account_protocol_get_service_icon (MODEST_ACCOUNT_PROTOCOL (protocol),
3771 account, mailbox, MODEST_ICON_SIZE_SMALL);
3775 modest_msg_view_set_branding (MODEST_MSG_VIEW (priv->msg_view), service_name, service_icon);
3776 g_free (service_name);