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 <modest-hildon2-window-mgr.h>
71 #include <tny-camel-msg.h>
73 #define MYDOCS_ENV "MYDOCSDIR"
74 #define DOCS_FOLDER ".documents"
76 typedef struct _ModestMsgViewWindowPrivate ModestMsgViewWindowPrivate;
77 struct _ModestMsgViewWindowPrivate {
80 GtkWidget *main_scroll;
81 GtkWidget *find_toolbar;
84 /* Progress observers */
85 GSList *progress_widgets;
88 GtkWidget *prev_toolitem;
89 GtkWidget *next_toolitem;
90 gboolean progress_hint;
93 /* Optimized view enabled */
94 gboolean optimized_view;
96 /* Whether this was created via the *_new_for_search_result() function. */
97 gboolean is_search_result;
99 /* Whether the message is in outbox */
102 /* A reference to the @model of the header view
103 * to allow selecting previous/next messages,
104 * if the message is currently selected in the header view.
106 const gchar *header_folder_id;
107 GtkTreeModel *header_model;
108 GtkTreeRowReference *row_reference;
109 GtkTreeRowReference *next_row_reference;
111 gulong clipboard_change_handler;
112 gulong queue_change_handler;
113 gulong account_removed_handler;
114 gulong row_changed_handler;
115 gulong row_deleted_handler;
116 gulong row_inserted_handler;
117 gulong rows_reordered_handler;
120 GtkWidget *remove_attachment_banner;
123 TnyMimePart *other_body;
128 static void modest_msg_view_window_class_init (ModestMsgViewWindowClass *klass);
129 static void modest_msg_view_window_init (ModestMsgViewWindow *obj);
130 static void modest_header_view_observer_init (ModestHeaderViewObserverIface *iface_class);
131 static void modest_msg_view_window_finalize (GObject *obj);
132 static void modest_msg_view_window_show_find_toolbar (GtkWidget *obj, gpointer data);
133 static void modest_msg_view_window_find_toolbar_close (GtkWidget *widget,
134 ModestMsgViewWindow *obj);
135 static void modest_msg_view_window_find_toolbar_search (GtkWidget *widget,
136 ModestMsgViewWindow *obj);
137 static void modest_msg_view_window_toggle_find_toolbar (GtkWidget *obj,
139 static void modest_msg_view_window_disconnect_signals (ModestWindow *self);
141 static gdouble modest_msg_view_window_get_zoom (ModestWindow *window);
142 static void modest_msg_view_window_set_zoom (ModestWindow *window,
144 static gboolean modest_msg_view_window_zoom_minus (ModestWindow *window);
145 static gboolean modest_msg_view_window_zoom_plus (ModestWindow *window);
146 static gboolean modest_msg_view_window_key_event (GtkWidget *window,
149 static void modest_msg_view_window_update_priority (ModestMsgViewWindow *window);
151 static void modest_msg_view_window_show_toolbar (ModestWindow *window,
152 gboolean show_toolbar);
154 static void modest_msg_view_window_clipboard_owner_change (GtkClipboard *clipboard,
156 ModestMsgViewWindow *window);
158 static void modest_msg_view_window_on_row_changed (GtkTreeModel *header_model,
161 ModestMsgViewWindow *window);
163 static void modest_msg_view_window_on_row_deleted (GtkTreeModel *header_model,
165 ModestMsgViewWindow *window);
167 static void modest_msg_view_window_on_row_inserted (GtkTreeModel *header_model,
168 GtkTreePath *tree_path,
169 GtkTreeIter *tree_iter,
170 ModestMsgViewWindow *window);
172 static void modest_msg_view_window_on_row_reordered (GtkTreeModel *header_model,
176 ModestMsgViewWindow *window);
178 static void modest_msg_view_window_update_model_replaced (ModestHeaderViewObserver *window,
180 const gchar *tny_folder_id);
182 static void on_queue_changed (ModestMailOperationQueue *queue,
183 ModestMailOperation *mail_op,
184 ModestMailOperationQueueNotification type,
185 ModestMsgViewWindow *self);
187 static void on_account_removed (TnyAccountStore *account_store,
191 static void on_move_focus (GtkWidget *widget,
192 GtkDirectionType direction,
195 static void view_msg_cb (ModestMailOperation *mail_op,
202 static void set_progress_hint (ModestMsgViewWindow *self,
205 static void update_window_title (ModestMsgViewWindow *window);
207 static void init_window (ModestMsgViewWindow *obj);
209 static gboolean msg_is_visible (TnyHeader *header, gboolean check_outbox);
211 static void check_dimming_rules_after_change (ModestMsgViewWindow *window);
213 static gboolean on_fetch_image (ModestMsgView *msgview,
216 ModestMsgViewWindow *window);
218 static gboolean modest_msg_view_window_scroll_child (ModestMsgViewWindow *self,
219 GtkScrollType scroll_type,
222 static gboolean message_reader (ModestMsgViewWindow *window,
223 ModestMsgViewWindowPrivate *priv,
225 const gchar *msg_uid,
227 GtkTreeRowReference *row_reference);
229 static void setup_menu (ModestMsgViewWindow *self);
230 static gboolean _modest_msg_view_window_map_event (GtkWidget *widget,
233 static void update_branding (ModestMsgViewWindow *self);
234 static void sync_flags (ModestMsgViewWindow *self);
236 /* list my signals */
243 static const GtkToggleActionEntry msg_view_toggle_action_entries [] = {
244 { "FindInMessage", MODEST_TOOLBAR_ICON_FIND, N_("qgn_toolb_gene_find"), "<CTRL>F", NULL, G_CALLBACK (modest_msg_view_window_toggle_find_toolbar), FALSE },
247 #define MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE((o), \
248 MODEST_TYPE_MSG_VIEW_WINDOW, \
249 ModestMsgViewWindowPrivate))
251 static GtkWindowClass *parent_class = NULL;
253 /* uncomment the following if you have defined any signals */
254 static guint signals[LAST_SIGNAL] = {0};
257 modest_msg_view_window_get_type (void)
259 static GType my_type = 0;
261 static const GTypeInfo my_info = {
262 sizeof(ModestMsgViewWindowClass),
263 NULL, /* base init */
264 NULL, /* base finalize */
265 (GClassInitFunc) modest_msg_view_window_class_init,
266 NULL, /* class finalize */
267 NULL, /* class data */
268 sizeof(ModestMsgViewWindow),
270 (GInstanceInitFunc) modest_msg_view_window_init,
273 my_type = g_type_register_static (MODEST_TYPE_HILDON2_WINDOW,
274 "ModestMsgViewWindow",
277 static const GInterfaceInfo modest_header_view_observer_info =
279 (GInterfaceInitFunc) modest_header_view_observer_init,
280 NULL, /* interface_finalize */
281 NULL /* interface_data */
284 g_type_add_interface_static (my_type,
285 MODEST_TYPE_HEADER_VIEW_OBSERVER,
286 &modest_header_view_observer_info);
292 save_state (ModestWindow *self)
294 modest_widget_memory_save (modest_runtime_get_conf (),
296 MODEST_CONF_MSG_VIEW_WINDOW_KEY);
300 modest_msg_view_window_scroll_child (ModestMsgViewWindow *self,
301 GtkScrollType scroll_type,
305 ModestMsgViewWindowPrivate *priv;
308 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(self);
310 switch (scroll_type) {
311 case GTK_SCROLL_STEP_UP:
314 case GTK_SCROLL_STEP_DOWN:
317 case GTK_SCROLL_PAGE_UP:
320 case GTK_SCROLL_PAGE_DOWN:
323 case GTK_SCROLL_START:
334 modest_maemo_utils_scroll_pannable((HildonPannableArea *) priv->main_scroll, 0, step);
336 return (gboolean) step;
340 add_scroll_binding (GtkBindingSet *binding_set,
342 GtkScrollType scroll)
344 guint keypad_keyval = keyval - GDK_Left + GDK_KP_Left;
346 gtk_binding_entry_add_signal (binding_set, keyval, 0,
348 GTK_TYPE_SCROLL_TYPE, scroll,
349 G_TYPE_BOOLEAN, FALSE);
350 gtk_binding_entry_add_signal (binding_set, keypad_keyval, 0,
352 GTK_TYPE_SCROLL_TYPE, scroll,
353 G_TYPE_BOOLEAN, FALSE);
357 modest_msg_view_window_class_init (ModestMsgViewWindowClass *klass)
359 GObjectClass *gobject_class;
360 HildonWindowClass *hildon_window_class;
361 ModestWindowClass *modest_window_class;
362 GtkBindingSet *binding_set;
364 gobject_class = (GObjectClass*) klass;
365 hildon_window_class = (HildonWindowClass *) klass;
366 modest_window_class = (ModestWindowClass *) klass;
368 parent_class = g_type_class_peek_parent (klass);
369 gobject_class->finalize = modest_msg_view_window_finalize;
371 modest_window_class->set_zoom_func = modest_msg_view_window_set_zoom;
372 modest_window_class->get_zoom_func = modest_msg_view_window_get_zoom;
373 modest_window_class->zoom_plus_func = modest_msg_view_window_zoom_plus;
374 modest_window_class->zoom_minus_func = modest_msg_view_window_zoom_minus;
375 modest_window_class->show_toolbar_func = modest_msg_view_window_show_toolbar;
376 modest_window_class->disconnect_signals_func = modest_msg_view_window_disconnect_signals;
378 modest_window_class->save_state_func = save_state;
380 klass->scroll_child = modest_msg_view_window_scroll_child;
382 signals[MSG_CHANGED_SIGNAL] =
383 g_signal_new ("msg-changed",
384 G_TYPE_FROM_CLASS (gobject_class),
386 G_STRUCT_OFFSET (ModestMsgViewWindowClass, msg_changed),
388 modest_marshal_VOID__POINTER_POINTER,
389 G_TYPE_NONE, 2, G_TYPE_POINTER, G_TYPE_POINTER);
391 signals[SCROLL_CHILD_SIGNAL] =
392 g_signal_new ("scroll-child",
393 G_TYPE_FROM_CLASS (gobject_class),
394 G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
395 G_STRUCT_OFFSET (ModestMsgViewWindowClass, scroll_child),
397 modest_marshal_BOOLEAN__ENUM_BOOLEAN,
398 G_TYPE_BOOLEAN, 2, GTK_TYPE_SCROLL_TYPE, G_TYPE_BOOLEAN);
400 binding_set = gtk_binding_set_by_class (klass);
401 add_scroll_binding (binding_set, GDK_Up, GTK_SCROLL_STEP_UP);
402 add_scroll_binding (binding_set, GDK_Down, GTK_SCROLL_STEP_DOWN);
403 add_scroll_binding (binding_set, GDK_Page_Up, GTK_SCROLL_PAGE_UP);
404 add_scroll_binding (binding_set, GDK_Page_Down, GTK_SCROLL_PAGE_DOWN);
405 add_scroll_binding (binding_set, GDK_Home, GTK_SCROLL_START);
406 add_scroll_binding (binding_set, GDK_End, GTK_SCROLL_END);
408 g_type_class_add_private (gobject_class, sizeof(ModestMsgViewWindowPrivate));
412 static void modest_header_view_observer_init(
413 ModestHeaderViewObserverIface *iface_class)
415 iface_class->update_func = modest_msg_view_window_update_model_replaced;
419 modest_msg_view_window_init (ModestMsgViewWindow *obj)
421 ModestMsgViewWindowPrivate *priv;
422 ModestWindowPrivate *parent_priv = NULL;
423 GtkActionGroup *action_group = NULL;
424 GError *error = NULL;
426 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(obj);
427 parent_priv = MODEST_WINDOW_GET_PRIVATE(obj);
428 parent_priv->ui_manager = gtk_ui_manager_new();
430 action_group = gtk_action_group_new ("ModestMsgViewWindowActions");
431 gtk_action_group_set_translation_domain (action_group, GETTEXT_PACKAGE);
433 /* Add common actions */
434 gtk_action_group_add_actions (action_group,
435 modest_action_entries,
436 G_N_ELEMENTS (modest_action_entries),
438 gtk_action_group_add_toggle_actions (action_group,
439 msg_view_toggle_action_entries,
440 G_N_ELEMENTS (msg_view_toggle_action_entries),
443 gtk_ui_manager_insert_action_group (parent_priv->ui_manager, action_group, 0);
444 g_object_unref (action_group);
446 /* Load the UI definition */
447 gtk_ui_manager_add_ui_from_file (parent_priv->ui_manager, MODEST_UIDIR "modest-msg-view-window-ui.xml",
450 g_printerr ("modest: could not merge modest-msg-view-window-ui.xml: %s\n", error->message);
451 g_error_free (error);
456 /* Add accelerators */
457 gtk_window_add_accel_group (GTK_WINDOW (obj),
458 gtk_ui_manager_get_accel_group (parent_priv->ui_manager));
460 priv->is_search_result = FALSE;
461 priv->is_outbox = FALSE;
463 priv->msg_view = NULL;
464 priv->header_model = NULL;
465 priv->header_folder_id = NULL;
466 priv->clipboard_change_handler = 0;
467 priv->queue_change_handler = 0;
468 priv->account_removed_handler = 0;
469 priv->row_changed_handler = 0;
470 priv->row_deleted_handler = 0;
471 priv->row_inserted_handler = 0;
472 priv->rows_reordered_handler = 0;
473 priv->progress_hint = FALSE;
474 priv->fetching_images = 0;
476 priv->optimized_view = FALSE;
477 priv->purge_timeout = 0;
478 priv->remove_attachment_banner = NULL;
479 priv->msg_uid = NULL;
480 priv->other_body = NULL;
482 priv->sighandlers = NULL;
485 init_window (MODEST_MSG_VIEW_WINDOW(obj));
487 hildon_program_add_window (hildon_program_get_instance(),
493 update_progress_hint (ModestMsgViewWindow *self)
495 ModestMsgViewWindowPrivate *priv;
496 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(self);
498 if (GTK_WIDGET_VISIBLE (self)) {
499 hildon_gtk_window_set_progress_indicator (GTK_WINDOW (self),
500 (priv->progress_hint || (priv->fetching_images > 0))?1:0);
505 set_progress_hint (ModestMsgViewWindow *self,
508 ModestWindowPrivate *parent_priv;
509 ModestMsgViewWindowPrivate *priv;
511 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self));
513 parent_priv = MODEST_WINDOW_GET_PRIVATE(self);
514 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(self);
516 /* Sets current progress hint */
517 priv->progress_hint = enabled;
519 update_progress_hint (self);
525 init_window (ModestMsgViewWindow *obj)
527 GtkWidget *main_vbox;
528 ModestMsgViewWindowPrivate *priv;
530 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(obj);
532 priv->msg_view = GTK_WIDGET (tny_platform_factory_new_msg_view (modest_tny_platform_factory_get_instance ()));
533 modest_msg_view_set_shadow_type (MODEST_MSG_VIEW (priv->msg_view), GTK_SHADOW_NONE);
534 main_vbox = gtk_vbox_new (FALSE, 6);
535 priv->main_scroll = hildon_pannable_area_new ();
536 g_object_set (G_OBJECT (priv->main_scroll),
537 "mov-mode", HILDON_MOVEMENT_MODE_BOTH,
540 gtk_container_add (GTK_CONTAINER (priv->main_scroll), priv->msg_view);
541 gtk_box_pack_start (GTK_BOX(main_vbox), priv->main_scroll, TRUE, TRUE, 0);
542 gtk_container_add (GTK_CONTAINER(obj), main_vbox);
544 /* NULL-ize fields if the window is destroyed */
545 g_signal_connect (priv->msg_view, "destroy", G_CALLBACK (gtk_widget_destroyed), &(priv->msg_view));
547 gtk_widget_show_all (GTK_WIDGET(main_vbox));
551 modest_msg_view_window_disconnect_signals (ModestWindow *self)
553 ModestMsgViewWindowPrivate *priv;
554 GtkWidget *header_view = NULL;
555 GtkWindow *parent_window = NULL;
557 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
559 if (gtk_clipboard_get (GDK_SELECTION_PRIMARY) &&
560 g_signal_handler_is_connected (gtk_clipboard_get (GDK_SELECTION_PRIMARY),
561 priv->clipboard_change_handler))
562 g_signal_handler_disconnect (gtk_clipboard_get (GDK_SELECTION_PRIMARY),
563 priv->clipboard_change_handler);
565 if (g_signal_handler_is_connected (G_OBJECT (modest_runtime_get_mail_operation_queue ()),
566 priv->queue_change_handler))
567 g_signal_handler_disconnect (G_OBJECT (modest_runtime_get_mail_operation_queue ()),
568 priv->queue_change_handler);
570 if (g_signal_handler_is_connected (G_OBJECT (modest_runtime_get_account_store ()),
571 priv->account_removed_handler))
572 g_signal_handler_disconnect (G_OBJECT (modest_runtime_get_account_store ()),
573 priv->account_removed_handler);
575 if (priv->header_model) {
576 if (g_signal_handler_is_connected(G_OBJECT (priv->header_model),
577 priv->row_changed_handler))
578 g_signal_handler_disconnect(G_OBJECT (priv->header_model),
579 priv->row_changed_handler);
581 if (g_signal_handler_is_connected(G_OBJECT (priv->header_model),
582 priv->row_deleted_handler))
583 g_signal_handler_disconnect(G_OBJECT (priv->header_model),
584 priv->row_deleted_handler);
586 if (g_signal_handler_is_connected(G_OBJECT (priv->header_model),
587 priv->row_inserted_handler))
588 g_signal_handler_disconnect(G_OBJECT (priv->header_model),
589 priv->row_inserted_handler);
591 if (g_signal_handler_is_connected(G_OBJECT (priv->header_model),
592 priv->rows_reordered_handler))
593 g_signal_handler_disconnect(G_OBJECT (priv->header_model),
594 priv->rows_reordered_handler);
597 modest_signal_mgr_disconnect_all_and_destroy (priv->sighandlers);
598 priv->sighandlers = NULL;
600 parent_window = gtk_window_get_transient_for (GTK_WINDOW (self));
601 if (parent_window && MODEST_IS_HEADER_WINDOW (parent_window)) {
602 header_view = GTK_WIDGET (modest_header_window_get_header_view (MODEST_HEADER_WINDOW (parent_window)));
604 modest_header_view_remove_observer(MODEST_HEADER_VIEW (header_view),
605 MODEST_HEADER_VIEW_OBSERVER(self));
611 modest_msg_view_window_finalize (GObject *obj)
613 ModestMsgViewWindowPrivate *priv;
615 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (obj);
617 /* Sanity check: shouldn't be needed, the window mgr should
618 call this function before */
619 modest_msg_view_window_disconnect_signals (MODEST_WINDOW (obj));
621 if (priv->other_body != NULL) {
622 g_object_unref (priv->other_body);
623 priv->other_body = NULL;
626 if (priv->header_model != NULL) {
627 g_object_unref (priv->header_model);
628 priv->header_model = NULL;
631 if (priv->remove_attachment_banner) {
632 gtk_widget_destroy (priv->remove_attachment_banner);
633 g_object_unref (priv->remove_attachment_banner);
634 priv->remove_attachment_banner = NULL;
637 if (priv->purge_timeout > 0) {
638 g_source_remove (priv->purge_timeout);
639 priv->purge_timeout = 0;
642 if (priv->row_reference) {
643 gtk_tree_row_reference_free (priv->row_reference);
644 priv->row_reference = NULL;
647 if (priv->next_row_reference) {
648 gtk_tree_row_reference_free (priv->next_row_reference);
649 priv->next_row_reference = NULL;
653 g_free (priv->msg_uid);
654 priv->msg_uid = NULL;
657 G_OBJECT_CLASS(parent_class)->finalize (obj);
661 select_next_valid_row (GtkTreeModel *model,
662 GtkTreeRowReference **row_reference,
666 GtkTreeIter tmp_iter;
668 GtkTreePath *next = NULL;
669 gboolean retval = FALSE, finished;
671 g_return_val_if_fail (gtk_tree_row_reference_valid (*row_reference), FALSE);
673 path = gtk_tree_row_reference_get_path (*row_reference);
674 gtk_tree_model_get_iter (model, &tmp_iter, path);
675 gtk_tree_row_reference_free (*row_reference);
676 *row_reference = NULL;
680 TnyHeader *header = NULL;
682 if (gtk_tree_model_iter_next (model, &tmp_iter)) {
683 gtk_tree_model_get (model, &tmp_iter,
684 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
688 if (msg_is_visible (header, is_outbox)) {
689 next = gtk_tree_model_get_path (model, &tmp_iter);
690 *row_reference = gtk_tree_row_reference_new (model, next);
691 gtk_tree_path_free (next);
695 g_object_unref (header);
698 } else if (cycle && gtk_tree_model_get_iter_first (model, &tmp_iter)) {
699 next = gtk_tree_model_get_path (model, &tmp_iter);
701 /* Ensure that we are not selecting the same */
702 if (gtk_tree_path_compare (path, next) != 0) {
703 gtk_tree_model_get (model, &tmp_iter,
704 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
707 if (msg_is_visible (header, is_outbox)) {
708 *row_reference = gtk_tree_row_reference_new (model, next);
712 g_object_unref (header);
716 /* If we ended up in the same message
717 then there is no valid next
721 gtk_tree_path_free (next);
723 /* If there are no more messages and we don't
724 want to start again in the first one then
725 there is no valid next message */
731 gtk_tree_path_free (path);
736 /* TODO: This should be in _init(), with the parameters as properties. */
738 modest_msg_view_window_construct (ModestMsgViewWindow *self,
739 const gchar *modest_account_name,
740 const gchar *mailbox,
741 const gchar *msg_uid)
744 ModestMsgViewWindowPrivate *priv = NULL;
745 ModestWindowPrivate *parent_priv = NULL;
746 ModestDimmingRulesGroup *toolbar_rules_group = NULL;
747 ModestDimmingRulesGroup *clipboard_rules_group = NULL;
749 obj = G_OBJECT (self);
750 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(obj);
751 parent_priv = MODEST_WINDOW_GET_PRIVATE(obj);
753 priv->msg_uid = g_strdup (msg_uid);
756 parent_priv->menubar = NULL;
758 toolbar_rules_group = modest_dimming_rules_group_new (MODEST_DIMMING_RULES_TOOLBAR, TRUE);
759 clipboard_rules_group = modest_dimming_rules_group_new (MODEST_DIMMING_RULES_CLIPBOARD, FALSE);
762 /* Add common dimming rules */
763 modest_dimming_rules_group_add_rules (toolbar_rules_group,
764 modest_msg_view_toolbar_dimming_entries,
765 G_N_ELEMENTS (modest_msg_view_toolbar_dimming_entries),
766 MODEST_WINDOW (self));
767 modest_dimming_rules_group_add_rules (clipboard_rules_group,
768 modest_msg_view_clipboard_dimming_entries,
769 G_N_ELEMENTS (modest_msg_view_clipboard_dimming_entries),
770 MODEST_WINDOW (self));
772 /* Insert dimming rules group for this window */
773 modest_ui_dimming_manager_insert_rules_group (parent_priv->ui_dimming_manager, toolbar_rules_group);
774 modest_ui_dimming_manager_insert_rules_group (parent_priv->ui_dimming_manager, clipboard_rules_group);
775 g_object_unref (toolbar_rules_group);
776 g_object_unref (clipboard_rules_group);
778 /* g_signal_connect (G_OBJECT(obj), "delete-event", G_CALLBACK(on_delete_event), obj); */
780 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);
781 g_signal_connect (G_OBJECT(priv->msg_view), "activate_link",
782 G_CALLBACK (modest_ui_actions_on_msg_link_clicked), obj);
783 g_signal_connect (G_OBJECT(priv->msg_view), "link_hover",
784 G_CALLBACK (modest_ui_actions_on_msg_link_hover), obj);
785 g_signal_connect (G_OBJECT(priv->msg_view), "attachment_clicked",
786 G_CALLBACK (modest_ui_actions_on_msg_attachment_clicked), obj);
787 g_signal_connect (G_OBJECT(priv->msg_view), "recpt_activated",
788 G_CALLBACK (modest_ui_actions_on_msg_recpt_activated), obj);
789 g_signal_connect (G_OBJECT(priv->msg_view), "show_details",
790 G_CALLBACK (modest_ui_actions_on_details), obj);
791 g_signal_connect (G_OBJECT(priv->msg_view), "link_contextual",
792 G_CALLBACK (modest_ui_actions_on_msg_link_contextual), obj);
793 g_signal_connect (G_OBJECT(priv->msg_view), "limit_error",
794 G_CALLBACK (modest_ui_actions_on_limit_error), obj);
795 g_signal_connect (G_OBJECT (priv->msg_view), "fetch_image",
796 G_CALLBACK (on_fetch_image), obj);
798 g_signal_connect (G_OBJECT (obj), "key-release-event",
799 G_CALLBACK (modest_msg_view_window_key_event),
802 g_signal_connect (G_OBJECT (obj), "key-press-event",
803 G_CALLBACK (modest_msg_view_window_key_event),
806 g_signal_connect (G_OBJECT (obj), "move-focus",
807 G_CALLBACK (on_move_focus), obj);
809 g_signal_connect (G_OBJECT (obj), "map-event",
810 G_CALLBACK (_modest_msg_view_window_map_event),
813 /* Mail Operation Queue */
814 priv->queue_change_handler = g_signal_connect (G_OBJECT (modest_runtime_get_mail_operation_queue ()),
816 G_CALLBACK (on_queue_changed),
819 /* Account manager */
820 priv->account_removed_handler = g_signal_connect (G_OBJECT (modest_runtime_get_account_store ()),
822 G_CALLBACK(on_account_removed),
825 modest_window_set_active_account (MODEST_WINDOW(obj), modest_account_name);
826 modest_window_set_active_mailbox (MODEST_WINDOW(obj), mailbox);
828 /* First add out toolbar ... */
829 modest_msg_view_window_show_toolbar (MODEST_WINDOW (obj), TRUE);
831 /* ... and later the find toolbar. This way find toolbar will
832 be shown over the other */
833 priv->find_toolbar = hildon_find_toolbar_new (NULL);
834 hildon_window_add_toolbar (HILDON_WINDOW (obj), GTK_TOOLBAR (priv->find_toolbar));
835 gtk_widget_set_no_show_all (priv->find_toolbar, TRUE);
836 g_signal_connect (G_OBJECT (priv->find_toolbar), "close",
837 G_CALLBACK (modest_msg_view_window_find_toolbar_close), obj);
838 g_signal_connect (G_OBJECT (priv->find_toolbar), "search",
839 G_CALLBACK (modest_msg_view_window_find_toolbar_search), obj);
840 priv->last_search = NULL;
842 /* Init the clipboard actions dim status */
843 modest_msg_view_grab_focus(MODEST_MSG_VIEW (priv->msg_view));
845 update_window_title (MODEST_MSG_VIEW_WINDOW (obj));
850 /* FIXME: parameter checks */
852 modest_msg_view_window_new_with_header_model (TnyMsg *msg,
853 const gchar *modest_account_name,
854 const gchar *mailbox,
855 const gchar *msg_uid,
857 GtkTreeRowReference *row_reference)
859 ModestMsgViewWindow *window = NULL;
860 ModestMsgViewWindowPrivate *priv = NULL;
861 TnyFolder *header_folder = NULL;
862 ModestHeaderView *header_view = NULL;
863 ModestWindowMgr *mgr = NULL;
866 modest_tny_mime_part_to_string (TNY_MIME_PART (msg), 0);
869 mgr = modest_runtime_get_window_mgr ();
870 window = MODEST_MSG_VIEW_WINDOW (modest_window_mgr_get_msg_view_window (mgr));
871 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), NULL);
873 modest_msg_view_window_construct (window, modest_account_name, mailbox, msg_uid);
875 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
877 /* Remember the message list's TreeModel so we can detect changes
878 * and change the list selection when necessary: */
879 header_folder = modest_header_view_get_folder (header_view);
881 priv->is_outbox = (modest_tny_folder_guess_folder_type (header_folder) ==
882 TNY_FOLDER_TYPE_OUTBOX);
883 priv->header_folder_id = tny_folder_get_id (header_folder);
884 g_object_unref(header_folder);
887 /* Setup row references and connect signals */
888 priv->header_model = g_object_ref (model);
890 if (row_reference && gtk_tree_row_reference_valid (row_reference)) {
891 priv->row_reference = gtk_tree_row_reference_copy (row_reference);
892 priv->next_row_reference = gtk_tree_row_reference_copy (row_reference);
893 select_next_valid_row (model, &(priv->next_row_reference), TRUE, priv->is_outbox);
895 priv->row_reference = NULL;
896 priv->next_row_reference = NULL;
899 /* Connect signals */
900 priv->row_changed_handler =
901 g_signal_connect (GTK_TREE_MODEL(model), "row-changed",
902 G_CALLBACK(modest_msg_view_window_on_row_changed),
904 priv->row_deleted_handler =
905 g_signal_connect (GTK_TREE_MODEL(model), "row-deleted",
906 G_CALLBACK(modest_msg_view_window_on_row_deleted),
908 priv->row_inserted_handler =
909 g_signal_connect (GTK_TREE_MODEL(model), "row-inserted",
910 G_CALLBACK(modest_msg_view_window_on_row_inserted),
912 priv->rows_reordered_handler =
913 g_signal_connect(GTK_TREE_MODEL(model), "rows-reordered",
914 G_CALLBACK(modest_msg_view_window_on_row_reordered),
917 if (header_view != NULL){
918 modest_header_view_add_observer(header_view,
919 MODEST_HEADER_VIEW_OBSERVER(window));
922 tny_msg_view_set_msg (TNY_MSG_VIEW (priv->msg_view), msg);
923 update_window_title (MODEST_MSG_VIEW_WINDOW (window));
924 update_branding (MODEST_MSG_VIEW_WINDOW (window));
926 /* gtk_widget_show_all (GTK_WIDGET (window)); */
927 modest_msg_view_window_update_priority (window);
928 /* Check dimming rules */
929 modest_ui_actions_check_toolbar_dimming_rules (MODEST_WINDOW (window));
930 modest_ui_actions_check_menu_dimming_rules (MODEST_WINDOW (window));
931 modest_window_check_dimming_rules_group (MODEST_WINDOW (window), MODEST_DIMMING_RULES_CLIPBOARD);
933 return MODEST_WINDOW(window);
937 modest_msg_view_window_new_from_uid (const gchar *modest_account_name,
938 const gchar *mailbox,
939 const gchar *msg_uid)
941 ModestMsgViewWindow *window = NULL;
942 ModestMsgViewWindowPrivate *priv = NULL;
943 ModestWindowMgr *mgr = NULL;
945 TnyAccount *account = NULL;
947 mgr = modest_runtime_get_window_mgr ();
948 window = MODEST_MSG_VIEW_WINDOW (modest_window_mgr_get_msg_view_window (mgr));
949 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), NULL);
951 modest_msg_view_window_construct (window, modest_account_name, mailbox, msg_uid);
953 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 ()),
962 if (is_merge || account) {
963 TnyFolder *folder = NULL;
965 /* Try to get the message, if it's already downloaded
966 we don't need to connect */
968 folder = modest_tny_folder_store_find_folder_from_uri (TNY_FOLDER_STORE (account), msg_uid);
970 ModestTnyAccountStore *account_store;
971 ModestTnyLocalFoldersAccount *local_folders_account;
973 account_store = modest_runtime_get_account_store ();
974 local_folders_account = MODEST_TNY_LOCAL_FOLDERS_ACCOUNT (
975 modest_tny_account_store_get_local_folders_account (account_store));
976 folder = modest_tny_local_folders_account_get_merged_outbox (local_folders_account);
977 g_object_unref (local_folders_account);
981 gboolean device_online;
983 device = modest_runtime_get_device();
984 device_online = tny_device_is_online (device);
986 message_reader (window, priv, NULL, msg_uid, folder, NULL);
988 TnyMsg *msg = tny_folder_find_msg (folder, msg_uid, NULL);
990 tny_msg_view_set_msg (TNY_MSG_VIEW (priv->msg_view), msg);
991 update_window_title (MODEST_MSG_VIEW_WINDOW (window));
992 update_branding (MODEST_MSG_VIEW_WINDOW (window));
993 g_object_unref (msg);
994 /* Sync flags to server */
995 sync_flags (MODEST_MSG_VIEW_WINDOW (window));
997 message_reader (window, priv, NULL, msg_uid, folder, NULL);
1000 g_object_unref (folder);
1005 /* Check dimming rules */
1006 modest_ui_actions_check_toolbar_dimming_rules (MODEST_WINDOW (window));
1007 modest_ui_actions_check_menu_dimming_rules (MODEST_WINDOW (window));
1008 modest_window_check_dimming_rules_group (MODEST_WINDOW (window), MODEST_DIMMING_RULES_CLIPBOARD);
1010 return MODEST_WINDOW(window);
1014 modest_msg_view_window_new_from_header_view (ModestHeaderView *header_view,
1015 const gchar *modest_account_name,
1016 const gchar *mailbox,
1017 const gchar *msg_uid,
1018 GtkTreeRowReference *row_reference)
1020 ModestMsgViewWindow *window = NULL;
1021 ModestMsgViewWindowPrivate *priv = NULL;
1022 TnyFolder *header_folder = NULL;
1023 ModestWindowMgr *mgr = NULL;
1027 mgr = modest_runtime_get_window_mgr ();
1028 window = MODEST_MSG_VIEW_WINDOW (modest_window_mgr_get_msg_view_window (mgr));
1029 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), NULL);
1031 modest_msg_view_window_construct (window, modest_account_name, mailbox, msg_uid);
1033 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1035 /* Remember the message list's TreeModel so we can detect changes
1036 * and change the list selection when necessary: */
1038 if (header_view != NULL){
1039 header_folder = modest_header_view_get_folder(header_view);
1040 /* This could happen if the header folder was
1041 unseleted before opening this msg window (for
1042 example if the user selects an account in the
1043 folder view of the main window */
1044 if (header_folder) {
1045 priv->is_outbox = (modest_tny_folder_guess_folder_type (header_folder) ==
1046 TNY_FOLDER_TYPE_OUTBOX);
1047 priv->header_folder_id = tny_folder_get_id(header_folder);
1048 g_object_unref(header_folder);
1052 /* Setup row references and connect signals */
1053 priv->header_model = gtk_tree_view_get_model (GTK_TREE_VIEW (header_view));
1054 g_object_ref (priv->header_model);
1056 if (row_reference && gtk_tree_row_reference_valid (row_reference)) {
1057 priv->row_reference = gtk_tree_row_reference_copy (row_reference);
1058 priv->next_row_reference = gtk_tree_row_reference_copy (row_reference);
1059 select_next_valid_row (priv->header_model, &(priv->next_row_reference), TRUE, priv->is_outbox);
1061 priv->row_reference = NULL;
1062 priv->next_row_reference = NULL;
1065 /* Connect signals */
1066 priv->row_changed_handler =
1067 g_signal_connect (GTK_TREE_MODEL(priv->header_model), "row-changed",
1068 G_CALLBACK(modest_msg_view_window_on_row_changed),
1070 priv->row_deleted_handler =
1071 g_signal_connect (GTK_TREE_MODEL(priv->header_model), "row-deleted",
1072 G_CALLBACK(modest_msg_view_window_on_row_deleted),
1074 priv->row_inserted_handler =
1075 g_signal_connect (GTK_TREE_MODEL(priv->header_model), "row-inserted",
1076 G_CALLBACK(modest_msg_view_window_on_row_inserted),
1078 priv->rows_reordered_handler =
1079 g_signal_connect(GTK_TREE_MODEL(priv->header_model), "rows-reordered",
1080 G_CALLBACK(modest_msg_view_window_on_row_reordered),
1083 if (header_view != NULL){
1084 modest_header_view_add_observer(header_view,
1085 MODEST_HEADER_VIEW_OBSERVER(window));
1088 tny_msg_view_set_msg (TNY_MSG_VIEW (priv->msg_view), NULL);
1089 update_branding (MODEST_MSG_VIEW_WINDOW (window));
1091 if (priv->row_reference) {
1092 path = gtk_tree_row_reference_get_path (priv->row_reference);
1093 if (gtk_tree_model_get_iter (priv->header_model, &iter, path)) {
1095 gtk_tree_model_get (priv->header_model, &iter,
1096 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
1098 message_reader (window, priv, header, NULL, NULL, priv->row_reference);
1099 g_object_unref (header);
1101 gtk_tree_path_free (path);
1103 /* Check dimming rules */
1104 modest_ui_actions_check_toolbar_dimming_rules (MODEST_WINDOW (window));
1105 modest_ui_actions_check_menu_dimming_rules (MODEST_WINDOW (window));
1106 modest_window_check_dimming_rules_group (MODEST_WINDOW (window), MODEST_DIMMING_RULES_CLIPBOARD);
1108 return MODEST_WINDOW(window);
1112 modest_msg_view_window_new_for_search_result (TnyMsg *msg,
1113 const gchar *modest_account_name,
1114 const gchar *mailbox,
1115 const gchar *msg_uid)
1117 ModestMsgViewWindow *window = NULL;
1118 ModestMsgViewWindowPrivate *priv = NULL;
1119 ModestWindowMgr *mgr = NULL;
1121 mgr = modest_runtime_get_window_mgr ();
1122 window = MODEST_MSG_VIEW_WINDOW (modest_window_mgr_get_msg_view_window (mgr));
1123 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), NULL);
1124 modest_msg_view_window_construct (window, modest_account_name, mailbox, msg_uid);
1126 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1128 /* Remember that this is a search result,
1129 * so we can disable some UI appropriately: */
1130 priv->is_search_result = TRUE;
1132 tny_msg_view_set_msg (TNY_MSG_VIEW (priv->msg_view), msg);
1133 update_branding (MODEST_MSG_VIEW_WINDOW (window));
1135 update_window_title (window);
1136 /* gtk_widget_show_all (GTK_WIDGET (window));*/
1137 modest_msg_view_window_update_priority (window);
1139 /* Check dimming rules */
1140 modest_ui_actions_check_toolbar_dimming_rules (MODEST_WINDOW (window));
1141 modest_ui_actions_check_menu_dimming_rules (MODEST_WINDOW (window));
1142 modest_window_check_dimming_rules_group (MODEST_WINDOW (window), MODEST_DIMMING_RULES_CLIPBOARD);
1144 return MODEST_WINDOW(window);
1148 modest_msg_view_window_is_other_body (ModestMsgViewWindow *self)
1150 ModestMsgViewWindowPrivate *priv = NULL;
1152 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self), FALSE);
1153 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
1155 return (priv->other_body != NULL);
1159 modest_msg_view_window_new_with_other_body (TnyMsg *msg,
1160 TnyMimePart *other_body,
1161 const gchar *modest_account_name,
1162 const gchar *mailbox,
1163 const gchar *msg_uid)
1165 GObject *obj = NULL;
1166 ModestMsgViewWindowPrivate *priv;
1167 ModestWindowMgr *mgr = NULL;
1169 g_return_val_if_fail (msg, NULL);
1170 mgr = modest_runtime_get_window_mgr ();
1171 obj = G_OBJECT (modest_window_mgr_get_msg_view_window (mgr));
1172 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (obj);
1173 modest_msg_view_window_construct (MODEST_MSG_VIEW_WINDOW (obj),
1174 modest_account_name, mailbox, msg_uid);
1177 priv->other_body = g_object_ref (other_body);
1178 modest_msg_view_set_msg_with_other_body (MODEST_MSG_VIEW (priv->msg_view), msg, other_body);
1180 tny_msg_view_set_msg (TNY_MSG_VIEW (priv->msg_view), msg);
1182 update_window_title (MODEST_MSG_VIEW_WINDOW (obj));
1183 update_branding (MODEST_MSG_VIEW_WINDOW (obj));
1185 /* gtk_widget_show_all (GTK_WIDGET (obj)); */
1187 /* Check dimming rules */
1188 modest_ui_actions_check_toolbar_dimming_rules (MODEST_WINDOW (obj));
1189 modest_ui_actions_check_menu_dimming_rules (MODEST_WINDOW (obj));
1190 modest_window_check_dimming_rules_group (MODEST_WINDOW (obj), MODEST_DIMMING_RULES_CLIPBOARD);
1192 return MODEST_WINDOW(obj);
1196 modest_msg_view_window_new_for_attachment (TnyMsg *msg,
1197 const gchar *modest_account_name,
1198 const gchar *mailbox,
1199 const gchar *msg_uid)
1201 return modest_msg_view_window_new_with_other_body (msg, NULL, modest_account_name, mailbox, msg_uid);
1205 modest_msg_view_window_on_row_changed (GtkTreeModel *header_model,
1208 ModestMsgViewWindow *window)
1210 check_dimming_rules_after_change (window);
1214 modest_msg_view_window_on_row_deleted(GtkTreeModel *header_model,
1216 ModestMsgViewWindow *window)
1218 check_dimming_rules_after_change (window);
1220 /* The window could have dissapeared */
1223 check_dimming_rules_after_change (ModestMsgViewWindow *window)
1225 modest_ui_actions_check_menu_dimming_rules (MODEST_WINDOW (window));
1226 modest_ui_actions_check_toolbar_dimming_rules (MODEST_WINDOW (window));
1230 /* On insertions we check if the folder still has the message we are
1231 * showing or do not. If do not, we do nothing. Which means we are still
1232 * not attached to any header folder and thus next/prev buttons are
1233 * still dimmed. Once the message that is shown by msg-view is found, the
1234 * new model of header-view will be attached and the references will be set.
1235 * On each further insertions dimming rules will be checked. However
1236 * this requires extra CPU time at least works.
1237 * (An message might be deleted from TnyFolder and thus will not be
1238 * inserted into the model again for example if it is removed by the
1239 * imap server and the header view is refreshed.)
1242 modest_msg_view_window_on_row_inserted (GtkTreeModel *model,
1243 GtkTreePath *tree_path,
1244 GtkTreeIter *tree_iter,
1245 ModestMsgViewWindow *window)
1247 ModestMsgViewWindowPrivate *priv = NULL;
1248 TnyHeader *header = NULL;
1250 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window));
1251 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1253 g_assert (model == priv->header_model);
1255 /* Check if the newly inserted message is the same we are actually
1256 * showing. IF not, we should remain detached from the header model
1257 * and thus prev and next toolbar buttons should remain dimmed. */
1258 gtk_tree_model_get (model, tree_iter,
1259 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
1262 if (TNY_IS_HEADER (header)) {
1265 uid = modest_tny_folder_get_header_unique_id (header);
1266 if (!g_str_equal(priv->msg_uid, uid)) {
1267 check_dimming_rules_after_change (window);
1269 g_object_unref (G_OBJECT(header));
1273 g_object_unref(G_OBJECT(header));
1276 if (priv->row_reference) {
1277 gtk_tree_row_reference_free (priv->row_reference);
1280 /* Setup row_reference for the actual msg. */
1281 priv->row_reference = gtk_tree_row_reference_new (priv->header_model, tree_path);
1282 if (priv->row_reference == NULL) {
1283 g_warning("%s: No reference for msg header item.", __FUNCTION__);
1287 /* Now set up next_row_reference. */
1288 if (priv->next_row_reference) {
1289 gtk_tree_row_reference_free (priv->next_row_reference);
1292 priv->next_row_reference = gtk_tree_row_reference_copy (priv->row_reference);
1293 select_next_valid_row (priv->header_model,
1294 &(priv->next_row_reference), FALSE, priv->is_outbox);
1296 /* Connect the remaining callbacks to become able to detect
1297 * changes in header-view. */
1298 priv->row_changed_handler =
1299 g_signal_connect (priv->header_model, "row-changed",
1300 G_CALLBACK (modest_msg_view_window_on_row_changed),
1302 priv->row_deleted_handler =
1303 g_signal_connect (priv->header_model, "row-deleted",
1304 G_CALLBACK (modest_msg_view_window_on_row_deleted),
1306 priv->rows_reordered_handler =
1307 g_signal_connect (priv->header_model, "rows-reordered",
1308 G_CALLBACK (modest_msg_view_window_on_row_reordered),
1311 check_dimming_rules_after_change (window);
1315 modest_msg_view_window_on_row_reordered (GtkTreeModel *header_model,
1319 ModestMsgViewWindow *window)
1321 ModestMsgViewWindowPrivate *priv = NULL;
1322 gboolean already_changed = FALSE;
1324 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(window);
1326 /* If the current row was reordered select the proper next
1327 valid row. The same if the next row reference changes */
1328 if (!priv->row_reference ||
1329 !gtk_tree_row_reference_valid (priv->row_reference))
1332 if (priv->next_row_reference &&
1333 gtk_tree_row_reference_valid (priv->next_row_reference)) {
1334 GtkTreePath *cur, *next;
1335 /* Check that the order is still the correct one */
1336 cur = gtk_tree_row_reference_get_path (priv->row_reference);
1337 next = gtk_tree_row_reference_get_path (priv->next_row_reference);
1338 gtk_tree_path_next (cur);
1339 if (gtk_tree_path_compare (cur, next) != 0) {
1340 gtk_tree_row_reference_free (priv->next_row_reference);
1341 priv->next_row_reference = gtk_tree_row_reference_copy (priv->row_reference);
1342 select_next_valid_row (header_model, &(priv->next_row_reference), FALSE, priv->is_outbox);
1343 already_changed = TRUE;
1345 gtk_tree_path_free (cur);
1346 gtk_tree_path_free (next);
1348 if (priv->next_row_reference)
1349 gtk_tree_row_reference_free (priv->next_row_reference);
1350 /* Update next row reference */
1351 priv->next_row_reference = gtk_tree_row_reference_copy (priv->row_reference);
1352 select_next_valid_row (header_model, &(priv->next_row_reference), FALSE, priv->is_outbox);
1353 already_changed = TRUE;
1356 check_dimming_rules_after_change (window);
1359 /* The modest_msg_view_window_update_model_replaced implements update
1360 * function for ModestHeaderViewObserver. Checks whether the TnyFolder
1361 * actually belongs to the header-view is the same as the TnyFolder of
1362 * the message of msg-view or not. If they are different, there is
1363 * nothing to do. If they are the same, then the model has replaced and
1364 * the reference in msg-view shall be replaced from the old model to
1365 * the new model. In this case the view will be detached from it's
1366 * header folder. From this point the next/prev buttons are dimmed.
1369 modest_msg_view_window_update_model_replaced (ModestHeaderViewObserver *observer,
1370 GtkTreeModel *model,
1371 const gchar *tny_folder_id)
1373 ModestMsgViewWindowPrivate *priv = NULL;
1374 ModestMsgViewWindow *window = NULL;
1376 g_assert(MODEST_IS_HEADER_VIEW_OBSERVER(observer));
1377 g_assert(MODEST_IS_MSG_VIEW_WINDOW(observer));
1379 window = MODEST_MSG_VIEW_WINDOW(observer);
1380 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(window);
1382 /* If there is an other folder in the header-view then we do
1383 * not care about it's model (msg list). Else if the
1384 * header-view shows the folder the msg shown by us is in, we
1385 * shall replace our model reference and make some check. */
1386 if(model == NULL || tny_folder_id == NULL ||
1387 (priv->header_folder_id && !g_str_equal(tny_folder_id, priv->header_folder_id)))
1390 /* Model is changed(replaced), so we should forget the old
1391 * one. Because there might be other references and there
1392 * might be some change on the model even if we unreferenced
1393 * it, we need to disconnect our signals here. */
1394 if (priv->header_model) {
1395 if (g_signal_handler_is_connected(G_OBJECT (priv->header_model),
1396 priv->row_changed_handler))
1397 g_signal_handler_disconnect(G_OBJECT (priv->header_model),
1398 priv->row_changed_handler);
1399 if (g_signal_handler_is_connected(G_OBJECT (priv->header_model),
1400 priv->row_deleted_handler))
1401 g_signal_handler_disconnect(G_OBJECT (priv->header_model),
1402 priv->row_deleted_handler);
1403 if (g_signal_handler_is_connected(G_OBJECT (priv->header_model),
1404 priv->row_inserted_handler))
1405 g_signal_handler_disconnect(G_OBJECT (priv->header_model),
1406 priv->row_inserted_handler);
1407 if (g_signal_handler_is_connected(G_OBJECT (priv->header_model),
1408 priv->rows_reordered_handler))
1409 g_signal_handler_disconnect(G_OBJECT (priv->header_model),
1410 priv->rows_reordered_handler);
1413 if (priv->row_reference)
1414 gtk_tree_row_reference_free (priv->row_reference);
1415 if (priv->next_row_reference)
1416 gtk_tree_row_reference_free (priv->next_row_reference);
1417 g_object_unref(priv->header_model);
1420 priv->row_changed_handler = 0;
1421 priv->row_deleted_handler = 0;
1422 priv->row_inserted_handler = 0;
1423 priv->rows_reordered_handler = 0;
1424 priv->next_row_reference = NULL;
1425 priv->row_reference = NULL;
1426 priv->header_model = NULL;
1429 priv->header_model = g_object_ref (model);
1431 /* Also we must connect to the new model for row insertions.
1432 * Only for insertions now. We will need other ones only after
1433 * the msg is show by msg-view is added to the new model. */
1434 priv->row_inserted_handler =
1435 g_signal_connect (priv->header_model, "row-inserted",
1436 G_CALLBACK(modest_msg_view_window_on_row_inserted),
1439 modest_ui_actions_check_menu_dimming_rules(MODEST_WINDOW(window));
1440 modest_ui_actions_check_toolbar_dimming_rules(MODEST_WINDOW(window));
1444 modest_msg_view_window_toolbar_on_transfer_mode (ModestMsgViewWindow *self)
1446 ModestMsgViewWindowPrivate *priv= NULL;
1448 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self), FALSE);
1449 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
1451 return priv->progress_hint;
1455 modest_msg_view_window_get_header (ModestMsgViewWindow *self)
1457 ModestMsgViewWindowPrivate *priv= NULL;
1459 TnyHeader *header = NULL;
1460 GtkTreePath *path = NULL;
1463 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self), NULL);
1464 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
1466 /* If the message was not obtained from a treemodel,
1467 * for instance if it was opened directly by the search UI:
1469 if (priv->header_model == NULL ||
1470 priv->row_reference == NULL ||
1471 !gtk_tree_row_reference_valid (priv->row_reference)) {
1472 msg = modest_msg_view_window_get_message (self);
1474 header = tny_msg_get_header (msg);
1475 g_object_unref (msg);
1480 /* Get iter of the currently selected message in the header view: */
1481 path = gtk_tree_row_reference_get_path (priv->row_reference);
1482 g_return_val_if_fail (path != NULL, NULL);
1483 gtk_tree_model_get_iter (priv->header_model,
1487 /* Get current message header */
1488 gtk_tree_model_get (priv->header_model, &iter,
1489 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
1492 gtk_tree_path_free (path);
1497 modest_msg_view_window_get_message (ModestMsgViewWindow *self)
1499 ModestMsgViewWindowPrivate *priv;
1501 g_return_val_if_fail (self, NULL);
1503 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(self);
1505 return tny_msg_view_get_msg (TNY_MSG_VIEW (priv->msg_view));
1509 modest_msg_view_window_get_message_uid (ModestMsgViewWindow *self)
1511 ModestMsgViewWindowPrivate *priv;
1513 g_return_val_if_fail (self, NULL);
1515 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
1517 return (const gchar*) priv->msg_uid;
1520 /* Used for the Ctrl+F accelerator */
1522 modest_msg_view_window_toggle_find_toolbar (GtkWidget *obj,
1525 ModestMsgViewWindow *window = MODEST_MSG_VIEW_WINDOW (data);
1526 ModestMsgViewWindowPrivate *priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1528 if (GTK_WIDGET_VISIBLE (priv->find_toolbar)) {
1529 modest_msg_view_window_find_toolbar_close (obj, data);
1531 modest_msg_view_window_show_find_toolbar (obj, data);
1535 /* Handler for menu option */
1537 modest_msg_view_window_show_find_toolbar (GtkWidget *obj,
1540 ModestMsgViewWindow *window = MODEST_MSG_VIEW_WINDOW (data);
1541 ModestMsgViewWindowPrivate *priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1543 gtk_widget_show (priv->find_toolbar);
1544 hildon_find_toolbar_highlight_entry (HILDON_FIND_TOOLBAR (priv->find_toolbar), TRUE);
1547 /* Handler for click on the "X" close button in find toolbar */
1549 modest_msg_view_window_find_toolbar_close (GtkWidget *widget,
1550 ModestMsgViewWindow *obj)
1552 ModestMsgViewWindowPrivate *priv;
1554 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (obj);
1557 gtk_widget_hide (priv->find_toolbar);
1558 modest_msg_view_grab_focus (MODEST_MSG_VIEW (priv->msg_view));
1562 modest_msg_view_window_find_toolbar_search (GtkWidget *widget,
1563 ModestMsgViewWindow *obj)
1565 gchar *current_search;
1566 ModestMsgViewWindowPrivate *priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (obj);
1568 if (modest_mime_part_view_is_empty (MODEST_MIME_PART_VIEW (priv->msg_view))) {
1569 hildon_banner_show_information (NULL, NULL, _("mail_ib_nothing_to_find"));
1573 g_object_get (G_OBJECT (widget), "prefix", ¤t_search, NULL);
1575 if ((current_search == NULL) || (strcmp (current_search, "") == 0)) {
1576 g_free (current_search);
1577 hildon_banner_show_information (NULL, NULL, _CS("ecdg_ib_find_rep_enter_text"));
1581 if ((priv->last_search == NULL) || (strcmp (priv->last_search, current_search) != 0)) {
1583 g_free (priv->last_search);
1584 priv->last_search = g_strdup (current_search);
1585 result = modest_isearch_view_search (MODEST_ISEARCH_VIEW (priv->msg_view),
1588 hildon_banner_show_information (NULL, NULL,
1589 _HL("ckct_ib_find_no_matches"));
1590 g_free (priv->last_search);
1591 priv->last_search = NULL;
1593 hildon_find_toolbar_highlight_entry (HILDON_FIND_TOOLBAR (priv->find_toolbar), TRUE);
1596 if (!modest_isearch_view_search_next (MODEST_ISEARCH_VIEW (priv->msg_view))) {
1597 hildon_banner_show_information (NULL, NULL,
1598 _HL("ckct_ib_find_search_complete"));
1599 g_free (priv->last_search);
1600 priv->last_search = NULL;
1602 hildon_find_toolbar_highlight_entry (HILDON_FIND_TOOLBAR (priv->find_toolbar), TRUE);
1606 g_free (current_search);
1611 modest_msg_view_window_set_zoom (ModestWindow *window,
1614 ModestMsgViewWindowPrivate *priv;
1615 ModestWindowPrivate *parent_priv;
1617 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window));
1619 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1620 parent_priv = MODEST_WINDOW_GET_PRIVATE (window);
1621 modest_zoomable_set_zoom (MODEST_ZOOMABLE (priv->msg_view), zoom);
1626 modest_msg_view_window_get_zoom (ModestWindow *window)
1628 ModestMsgViewWindowPrivate *priv;
1630 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), 1.0);
1632 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1633 return modest_zoomable_get_zoom (MODEST_ZOOMABLE (priv->msg_view));
1637 modest_msg_view_window_zoom_plus (ModestWindow *window)
1640 ModestMsgViewWindowPrivate *priv;
1644 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), 1.0);
1645 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1647 zoom_level = modest_zoomable_get_zoom (MODEST_ZOOMABLE (priv->msg_view));
1649 if (zoom_level >= 2.0) {
1650 hildon_banner_show_information (NULL, NULL,
1651 _CS("ckct_ib_max_zoom_level_reached"));
1653 } else if (zoom_level >= 1.5) {
1655 } else if (zoom_level >= 1.2) {
1657 } else if (zoom_level >= 1.0) {
1659 } else if (zoom_level >= 0.8) {
1661 } else if (zoom_level >= 0.5) {
1667 /* set zoom level */
1668 int_zoom = (gint) rint (zoom_level*100.0+0.1);
1669 banner_text = g_strdup_printf (_HL("wdgt_ib_zoom"), int_zoom);
1670 modest_platform_information_banner (GTK_WIDGET (window), NULL, banner_text);
1671 g_free (banner_text);
1672 modest_zoomable_set_zoom (MODEST_ZOOMABLE (priv->msg_view), zoom_level);
1678 modest_msg_view_window_zoom_minus (ModestWindow *window)
1681 ModestMsgViewWindowPrivate *priv;
1685 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), 1.0);
1686 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1688 zoom_level = modest_zoomable_get_zoom (MODEST_ZOOMABLE (priv->msg_view));
1690 if (zoom_level <= 0.5) {
1691 hildon_banner_show_information (NULL, NULL,
1692 _CS("ckct_ib_min_zoom_level_reached"));
1694 } else if (zoom_level <= 0.8) {
1696 } else if (zoom_level <= 1.0) {
1698 } else if (zoom_level <= 1.2) {
1700 } else if (zoom_level <= 1.5) {
1702 } else if (zoom_level <= 2.0) {
1708 /* set zoom level */
1709 int_zoom = (gint) rint (zoom_level*100.0+0.1);
1710 banner_text = g_strdup_printf (_HL("wdgt_ib_zoom"), int_zoom);
1711 modest_platform_information_banner (GTK_WIDGET (window), NULL, banner_text);
1712 g_free (banner_text);
1713 modest_zoomable_set_zoom (MODEST_ZOOMABLE (priv->msg_view), zoom_level);
1719 modest_msg_view_window_key_event (GtkWidget *window,
1725 focus = gtk_window_get_focus (GTK_WINDOW (window));
1727 /* for the find toolbar case */
1728 if (focus && GTK_IS_ENTRY (focus)) {
1729 if (event->keyval == GDK_BackSpace) {
1731 copy = gdk_event_copy ((GdkEvent *) event);
1732 gtk_widget_event (focus, copy);
1733 gdk_event_free (copy);
1743 modest_msg_view_window_last_message_selected (ModestMsgViewWindow *window)
1746 ModestMsgViewWindowPrivate *priv;
1747 GtkTreeIter tmp_iter;
1748 gboolean is_last_selected;
1750 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), TRUE);
1751 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1753 /*if no model (so no rows at all), then virtually we are the last*/
1754 if (!priv->header_model || !priv->row_reference)
1757 if (!gtk_tree_row_reference_valid (priv->row_reference))
1760 path = gtk_tree_row_reference_get_path (priv->row_reference);
1764 is_last_selected = TRUE;
1765 while (is_last_selected) {
1767 gtk_tree_path_next (path);
1768 if (!gtk_tree_model_get_iter (priv->header_model, &tmp_iter, path))
1770 gtk_tree_model_get (priv->header_model, &tmp_iter,
1771 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
1774 if (msg_is_visible (header, priv->is_outbox))
1775 is_last_selected = FALSE;
1776 g_object_unref(G_OBJECT(header));
1779 gtk_tree_path_free (path);
1780 return is_last_selected;
1784 modest_msg_view_window_has_headers_model (ModestMsgViewWindow *window)
1786 ModestMsgViewWindowPrivate *priv;
1788 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), TRUE);
1789 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1791 return priv->header_model != NULL;
1795 modest_msg_view_window_is_search_result (ModestMsgViewWindow *window)
1797 ModestMsgViewWindowPrivate *priv;
1799 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), TRUE);
1800 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1802 return priv->is_search_result;
1806 msg_is_visible (TnyHeader *header, gboolean check_outbox)
1808 if ((tny_header_get_flags(header) & TNY_HEADER_FLAG_DELETED))
1810 if (!check_outbox) {
1813 ModestTnySendQueueStatus status;
1814 status = modest_tny_all_send_queues_get_msg_status (header);
1815 return ((status != MODEST_TNY_SEND_QUEUE_FAILED) &&
1816 (status != MODEST_TNY_SEND_QUEUE_SENDING));
1821 modest_msg_view_window_first_message_selected (ModestMsgViewWindow *window)
1824 ModestMsgViewWindowPrivate *priv;
1825 gboolean is_first_selected;
1826 GtkTreeIter tmp_iter;
1828 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), TRUE);
1829 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1831 /*if no model (so no rows at all), then virtually we are the first*/
1832 if (!priv->header_model || !priv->row_reference)
1835 if (!gtk_tree_row_reference_valid (priv->row_reference))
1838 path = gtk_tree_row_reference_get_path (priv->row_reference);
1842 is_first_selected = TRUE;
1843 while (is_first_selected) {
1845 if(!gtk_tree_path_prev (path))
1847 /* Here the 'if' is needless for logic, but let make sure
1848 * iter is valid for gtk_tree_model_get. */
1849 if (!gtk_tree_model_get_iter (priv->header_model, &tmp_iter, path))
1851 gtk_tree_model_get (priv->header_model, &tmp_iter,
1852 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
1855 if (msg_is_visible (header, priv->is_outbox))
1856 is_first_selected = FALSE;
1857 g_object_unref(G_OBJECT(header));
1860 gtk_tree_path_free (path);
1861 return is_first_selected;
1868 GtkTreeRowReference *row_reference;
1872 message_reader_performer (gboolean canceled,
1874 GtkWindow *parent_window,
1875 TnyAccount *account,
1878 ModestMailOperation *mail_op = NULL;
1879 MsgReaderInfo *info;
1881 info = (MsgReaderInfo *) user_data;
1882 if (canceled || err) {
1883 update_window_title (MODEST_MSG_VIEW_WINDOW (parent_window));
1887 /* Register the header - it'll be unregistered in the callback */
1889 modest_window_mgr_register_header (modest_runtime_get_window_mgr (), info->header, NULL);
1891 /* New mail operation */
1892 mail_op = modest_mail_operation_new_with_error_handling (G_OBJECT(parent_window),
1893 modest_ui_actions_disk_operations_error_handler,
1896 modest_mail_operation_queue_add (modest_runtime_get_mail_operation_queue (), mail_op);
1898 modest_mail_operation_get_msg (mail_op, info->header, TRUE, view_msg_cb, info->row_reference);
1900 modest_mail_operation_find_msg (mail_op, info->folder, info->msg_uid, TRUE, view_msg_cb, NULL);
1901 g_object_unref (mail_op);
1903 /* Update dimming rules */
1904 modest_ui_actions_check_toolbar_dimming_rules (MODEST_WINDOW (parent_window));
1905 modest_ui_actions_check_menu_dimming_rules (MODEST_WINDOW (parent_window));
1908 /* Frees. The row_reference will be freed by the view_msg_cb callback */
1909 g_free (info->msg_uid);
1911 g_object_unref (info->folder);
1913 g_object_unref (info->header);
1914 g_slice_free (MsgReaderInfo, info);
1919 * Reads the message whose summary item is @header. It takes care of
1920 * several things, among others:
1922 * If the message was not previously downloaded then ask the user
1923 * before downloading. If there is no connection launch the connection
1924 * dialog. Update toolbar dimming rules.
1926 * Returns: TRUE if the mail operation was started, otherwise if the
1927 * user do not want to download the message, or if the user do not
1928 * want to connect, then the operation is not issued
1931 message_reader (ModestMsgViewWindow *window,
1932 ModestMsgViewWindowPrivate *priv,
1934 const gchar *msg_uid,
1936 GtkTreeRowReference *row_reference)
1938 ModestWindowMgr *mgr;
1939 TnyAccount *account = NULL;
1940 MsgReaderInfo *info;
1942 /* We set the header from model while we're loading */
1943 tny_header_view_set_header (TNY_HEADER_VIEW (priv->msg_view), header);
1944 gtk_window_set_title (GTK_WINDOW (window), _CS("ckdg_pb_updating"));
1950 g_object_ref (folder);
1952 mgr = modest_runtime_get_window_mgr ();
1953 /* Msg download completed */
1954 if (!header || !(tny_header_get_flags (header) & TNY_HEADER_FLAG_CACHED)) {
1956 /* Ask the user if he wants to download the message if
1958 if (!tny_device_is_online (modest_runtime_get_device())) {
1959 GtkResponseType response;
1961 response = modest_platform_run_confirmation_dialog (GTK_WINDOW (window),
1962 _("mcen_nc_get_msg"));
1963 if (response == GTK_RESPONSE_CANCEL) {
1964 update_window_title (window);
1969 folder = tny_header_get_folder (header);
1971 info = g_slice_new (MsgReaderInfo);
1972 info->msg_uid = g_strdup (msg_uid);
1974 info->header = g_object_ref (header);
1976 info->header = NULL;
1978 info->folder = g_object_ref (folder);
1980 info->folder = NULL;
1981 if (row_reference) {
1982 info->row_reference = gtk_tree_row_reference_copy (row_reference);
1984 info->row_reference = NULL;
1987 /* Offer the connection dialog if necessary */
1988 modest_platform_connect_if_remote_and_perform ((GtkWindow *) window,
1990 TNY_FOLDER_STORE (folder),
1991 message_reader_performer,
1994 g_object_unref (folder);
2000 folder = tny_header_get_folder (header);
2003 account = tny_folder_get_account (folder);
2005 info = g_slice_new (MsgReaderInfo);
2006 info->msg_uid = g_strdup (msg_uid);
2008 info->folder = g_object_ref (folder);
2010 info->folder = NULL;
2012 info->header = g_object_ref (header);
2014 info->header = NULL;
2016 info->row_reference = gtk_tree_row_reference_copy (row_reference);
2018 info->row_reference = NULL;
2020 message_reader_performer (FALSE, NULL, (GtkWindow *) window, account, info);
2022 g_object_unref (account);
2024 g_object_unref (folder);
2030 modest_msg_view_window_select_next_message (ModestMsgViewWindow *window)
2032 ModestMsgViewWindowPrivate *priv;
2033 GtkTreePath *path= NULL;
2034 GtkTreeIter tmp_iter;
2036 gboolean retval = TRUE;
2037 GtkTreeRowReference *row_reference = NULL;
2039 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), FALSE);
2040 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
2042 if (!priv->row_reference)
2045 /* Update the next row reference if it's not valid. This could
2046 happen if for example the header which it was pointing to,
2047 was deleted. The best place to do it is in the row-deleted
2048 handler but the tinymail model do not work like the glib
2049 tree models and reports the deletion when the row is still
2051 if (!gtk_tree_row_reference_valid (priv->next_row_reference)) {
2052 if (priv->next_row_reference) {
2053 gtk_tree_row_reference_free (priv->next_row_reference);
2055 if (gtk_tree_row_reference_valid (priv->row_reference)) {
2056 priv->next_row_reference = gtk_tree_row_reference_copy (priv->row_reference);
2057 select_next_valid_row (priv->header_model, &(priv->next_row_reference), FALSE, priv->is_outbox);
2059 priv->next_row_reference = NULL;
2062 if (priv->next_row_reference)
2063 path = gtk_tree_row_reference_get_path (priv->next_row_reference);
2067 row_reference = gtk_tree_row_reference_copy (priv->next_row_reference);
2069 gtk_tree_model_get_iter (priv->header_model,
2072 gtk_tree_path_free (path);
2074 gtk_tree_model_get (priv->header_model, &tmp_iter,
2075 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
2078 /* Read the message & show it */
2079 if (!message_reader (window, priv, header, NULL, NULL, row_reference)) {
2082 gtk_tree_row_reference_free (row_reference);
2085 g_object_unref (header);
2091 modest_msg_view_window_select_previous_message (ModestMsgViewWindow *window)
2093 ModestMsgViewWindowPrivate *priv = NULL;
2095 gboolean finished = FALSE;
2096 gboolean retval = FALSE;
2098 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), FALSE);
2099 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
2101 if (priv->row_reference && !gtk_tree_row_reference_valid (priv->row_reference)) {
2102 gtk_tree_row_reference_free (priv->row_reference);
2103 priv->row_reference = NULL;
2106 /* Return inmediatly if there is no header model */
2107 if (!priv->header_model || !priv->row_reference)
2110 path = gtk_tree_row_reference_get_path (priv->row_reference);
2111 while (!finished && gtk_tree_path_prev (path)) {
2115 gtk_tree_model_get_iter (priv->header_model, &iter, path);
2116 gtk_tree_model_get (priv->header_model, &iter,
2117 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
2121 if (msg_is_visible (header, priv->is_outbox)) {
2122 GtkTreeRowReference *row_reference;
2123 row_reference = gtk_tree_row_reference_new (priv->header_model, path);
2124 /* Read the message & show it */
2125 retval = message_reader (window, priv, header, NULL, NULL, row_reference);
2126 gtk_tree_row_reference_free (row_reference);
2130 g_object_unref (header);
2134 gtk_tree_path_free (path);
2139 view_msg_cb (ModestMailOperation *mail_op,
2146 ModestMsgViewWindow *self = NULL;
2147 ModestMsgViewWindowPrivate *priv = NULL;
2148 GtkTreeRowReference *row_reference = NULL;
2150 /* Unregister the header (it was registered before creating the mail operation) */
2151 modest_window_mgr_unregister_header (modest_runtime_get_window_mgr (), header);
2153 row_reference = (GtkTreeRowReference *) user_data;
2156 gtk_tree_row_reference_free (row_reference);
2157 self = (ModestMsgViewWindow *) modest_mail_operation_get_source (mail_op);
2159 /* Restore window title */
2160 update_window_title (self);
2161 modest_ui_actions_on_close_window (NULL, MODEST_WINDOW (self));
2162 g_object_unref (self);
2167 /* If there was any error */
2168 if (!modest_ui_actions_msg_retrieval_check (mail_op, header, msg)) {
2170 gtk_tree_row_reference_free (row_reference);
2171 self = (ModestMsgViewWindow *) modest_mail_operation_get_source (mail_op);
2173 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
2174 /* First we check if the parent is a folder window */
2175 if (priv->msg_uid && !modest_hildon2_window_mgr_get_folder_window (MODEST_HILDON2_WINDOW_MGR (modest_runtime_get_window_mgr ()))) {
2177 TnyAccount *account = NULL;
2178 GtkWidget *header_window = NULL;
2180 is_merge = g_str_has_prefix (priv->msg_uid, "merge:");
2182 /* Get the account */
2184 account = tny_account_store_find_account (TNY_ACCOUNT_STORE (modest_runtime_get_account_store ()),
2187 if (is_merge || account) {
2188 TnyFolder *folder = NULL;
2190 /* Try to get the message, if it's already downloaded
2191 we don't need to connect */
2193 folder = modest_tny_folder_store_find_folder_from_uri (TNY_FOLDER_STORE (account),
2196 ModestTnyAccountStore *account_store;
2197 ModestTnyLocalFoldersAccount *local_folders_account;
2199 account_store = modest_runtime_get_account_store ();
2200 local_folders_account = MODEST_TNY_LOCAL_FOLDERS_ACCOUNT (
2201 modest_tny_account_store_get_local_folders_account (account_store));
2202 folder = modest_tny_local_folders_account_get_merged_outbox (local_folders_account);
2203 g_object_unref (local_folders_account);
2205 if (account) g_object_unref (account);
2208 header_window = (GtkWidget *)
2209 modest_header_window_new (
2211 modest_window_get_active_account (MODEST_WINDOW (self)),
2212 modest_window_get_active_mailbox (MODEST_WINDOW (self)));
2213 if (!modest_window_mgr_register_window (modest_runtime_get_window_mgr (),
2214 MODEST_WINDOW (header_window),
2216 gtk_widget_destroy (GTK_WIDGET (header_window));
2218 gtk_widget_show_all (GTK_WIDGET (header_window));
2220 g_object_unref (folder);
2226 /* Restore window title */
2227 update_window_title (self);
2228 modest_ui_actions_on_close_window (NULL, MODEST_WINDOW (self));
2229 g_object_unref (self);
2234 /* Get the window */
2235 self = (ModestMsgViewWindow *) modest_mail_operation_get_source (mail_op);
2236 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self));
2237 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
2239 /* Update the row reference */
2240 if (priv->row_reference != NULL) {
2241 gtk_tree_row_reference_free (priv->row_reference);
2242 priv->row_reference = (row_reference && gtk_tree_row_reference_valid (row_reference))?gtk_tree_row_reference_copy (row_reference):NULL;
2243 if (priv->next_row_reference != NULL) {
2244 gtk_tree_row_reference_free (priv->next_row_reference);
2246 if (priv->row_reference) {
2247 priv->next_row_reference = gtk_tree_row_reference_copy (priv->row_reference);
2248 select_next_valid_row (priv->header_model, &(priv->next_row_reference), TRUE, priv->is_outbox);
2250 priv->next_row_reference = NULL;
2254 /* Mark header as read */
2255 if (!(tny_header_get_flags (header) & TNY_HEADER_FLAG_SEEN))
2256 tny_header_set_flag (header, TNY_HEADER_FLAG_SEEN);
2258 /* Set new message */
2259 if (priv->msg_view != NULL && TNY_IS_MSG_VIEW (priv->msg_view)) {
2260 tny_msg_view_set_msg (TNY_MSG_VIEW (priv->msg_view), msg);
2261 modest_msg_view_window_update_priority (self);
2262 update_window_title (MODEST_MSG_VIEW_WINDOW (self));
2263 update_branding (MODEST_MSG_VIEW_WINDOW (self));
2264 modest_msg_view_grab_focus (MODEST_MSG_VIEW (priv->msg_view));
2267 /* Set the new message uid of the window */
2268 if (priv->msg_uid) {
2269 g_free (priv->msg_uid);
2270 priv->msg_uid = modest_tny_folder_get_header_unique_id (header);
2273 /* Notify the observers */
2274 g_signal_emit (G_OBJECT (self), signals[MSG_CHANGED_SIGNAL],
2275 0, priv->header_model, priv->row_reference);
2277 /* Sync the flags if the message is not opened from a header
2278 model, i.e, if it's opened from a notification */
2279 if (!priv->header_model)
2283 g_object_unref (self);
2285 gtk_tree_row_reference_free (row_reference);
2289 modest_msg_view_window_get_folder_type (ModestMsgViewWindow *window)
2291 ModestMsgViewWindowPrivate *priv;
2293 TnyFolderType folder_type;
2295 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
2297 folder_type = TNY_FOLDER_TYPE_UNKNOWN;
2299 msg = tny_msg_view_get_msg (TNY_MSG_VIEW (priv->msg_view));
2303 folder = tny_msg_get_folder (msg);
2305 folder_type = modest_tny_folder_guess_folder_type (folder);
2306 g_object_unref (folder);
2308 g_object_unref (msg);
2316 modest_msg_view_window_update_priority (ModestMsgViewWindow *window)
2318 ModestMsgViewWindowPrivate *priv;
2319 TnyHeader *header = NULL;
2320 TnyHeaderFlags flags = 0;
2322 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
2324 if (priv->header_model && priv->row_reference && gtk_tree_row_reference_valid (priv->row_reference)) {
2326 GtkTreePath *path = NULL;
2328 path = gtk_tree_row_reference_get_path (priv->row_reference);
2329 g_return_if_fail (path != NULL);
2330 gtk_tree_model_get_iter (priv->header_model,
2332 gtk_tree_row_reference_get_path (priv->row_reference));
2334 gtk_tree_model_get (priv->header_model, &iter, TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
2336 gtk_tree_path_free (path);
2339 msg = tny_msg_view_get_msg (TNY_MSG_VIEW (priv->msg_view));
2341 header = tny_msg_get_header (msg);
2342 g_object_unref (msg);
2347 flags = tny_header_get_flags (header);
2348 g_object_unref(G_OBJECT(header));
2351 modest_msg_view_set_priority (MODEST_MSG_VIEW(priv->msg_view), flags);
2356 toolbar_resize (ModestMsgViewWindow *self)
2358 ModestMsgViewWindowPrivate *priv = NULL;
2359 ModestWindowPrivate *parent_priv = NULL;
2361 gint static_button_size;
2362 ModestWindowMgr *mgr;
2364 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self));
2365 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
2366 parent_priv = MODEST_WINDOW_GET_PRIVATE(self);
2368 mgr = modest_runtime_get_window_mgr ();
2369 static_button_size = modest_window_mgr_get_fullscreen_mode (mgr)?120:120;
2371 if (parent_priv->toolbar) {
2372 /* Set expandable and homogeneous tool buttons */
2373 widget = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/ToolbarMessageReply");
2374 gtk_tool_item_set_expand (GTK_TOOL_ITEM (widget), TRUE);
2375 gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (widget), TRUE);
2376 widget = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/ToolbarMessageReplyAll");
2377 gtk_tool_item_set_expand (GTK_TOOL_ITEM (widget), TRUE);
2378 gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (widget), TRUE);
2379 widget = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/ToolbarMessageForward");
2380 gtk_tool_item_set_expand (GTK_TOOL_ITEM (widget), TRUE);
2381 gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (widget), TRUE);
2382 widget = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/ToolbarDeleteMessage");
2383 gtk_tool_item_set_expand (GTK_TOOL_ITEM (widget), TRUE);
2384 gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (widget), TRUE);
2385 widget = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/ToolbarDownloadExternalImages");
2386 gtk_tool_item_set_expand (GTK_TOOL_ITEM (widget), TRUE);
2387 gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (widget), TRUE);
2388 gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (priv->next_toolitem), TRUE);
2389 gtk_tool_item_set_expand (GTK_TOOL_ITEM (priv->next_toolitem), TRUE);
2390 gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (priv->prev_toolitem), TRUE);
2391 gtk_tool_item_set_expand (GTK_TOOL_ITEM (priv->prev_toolitem), TRUE);
2396 modest_msg_view_window_show_toolbar (ModestWindow *self,
2397 gboolean show_toolbar)
2399 ModestMsgViewWindowPrivate *priv = NULL;
2400 ModestWindowPrivate *parent_priv;
2402 parent_priv = MODEST_WINDOW_GET_PRIVATE(self);
2403 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(self);
2405 /* Set optimized view status */
2406 priv->optimized_view = !show_toolbar;
2408 if (!parent_priv->toolbar) {
2409 parent_priv->toolbar = gtk_ui_manager_get_widget (parent_priv->ui_manager,
2411 gtk_toolbar_set_icon_size (GTK_TOOLBAR (parent_priv->toolbar), HILDON_ICON_SIZE_FINGER);
2412 gtk_widget_set_no_show_all (parent_priv->toolbar, TRUE);
2414 priv->next_toolitem = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/ToolbarMessageNext");
2415 priv->prev_toolitem = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/ToolbarMessageBack");
2416 toolbar_resize (MODEST_MSG_VIEW_WINDOW (self));
2419 hildon_window_add_toolbar (HILDON_WINDOW (self),
2420 GTK_TOOLBAR (parent_priv->toolbar));
2425 /* Quick hack: this prevents toolbar icons "dance" when progress bar show status is changed */
2426 /* TODO: resize mode migth be GTK_RESIZE_QUEUE, in order to avoid unneccesary shows */
2427 gtk_container_set_resize_mode (GTK_CONTAINER(parent_priv->toolbar), GTK_RESIZE_IMMEDIATE);
2429 gtk_widget_show (GTK_WIDGET (parent_priv->toolbar));
2430 if (modest_msg_view_window_transfer_mode_enabled (MODEST_MSG_VIEW_WINDOW (self)))
2431 set_progress_hint (MODEST_MSG_VIEW_WINDOW (self), TRUE);
2433 set_progress_hint (MODEST_MSG_VIEW_WINDOW (self), FALSE);
2436 gtk_widget_set_no_show_all (parent_priv->toolbar, TRUE);
2437 gtk_widget_hide (GTK_WIDGET (parent_priv->toolbar));
2442 modest_msg_view_window_clipboard_owner_change (GtkClipboard *clipboard,
2444 ModestMsgViewWindow *window)
2446 if (!GTK_WIDGET_VISIBLE (window))
2449 modest_window_check_dimming_rules_group (MODEST_WINDOW (window), MODEST_DIMMING_RULES_CLIPBOARD);
2453 modest_msg_view_window_transfer_mode_enabled (ModestMsgViewWindow *self)
2455 ModestMsgViewWindowPrivate *priv;
2457 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self), FALSE);
2458 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(self);
2460 return priv->progress_hint;
2464 observers_empty (ModestMsgViewWindow *self)
2467 ModestMsgViewWindowPrivate *priv;
2468 gboolean is_empty = TRUE;
2469 guint pending_ops = 0;
2471 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(self);
2472 tmp = priv->progress_widgets;
2474 /* Check all observers */
2475 while (tmp && is_empty) {
2476 pending_ops = modest_progress_object_num_pending_operations (MODEST_PROGRESS_OBJECT(tmp->data));
2477 is_empty = pending_ops == 0;
2479 tmp = g_slist_next(tmp);
2486 on_account_removed (TnyAccountStore *account_store,
2487 TnyAccount *account,
2490 /* Do nothing if it's a transport account, because we only
2491 show the messages of a store account */
2492 if (tny_account_get_account_type(account) == TNY_ACCOUNT_TYPE_STORE) {
2493 const gchar *parent_acc = NULL;
2494 const gchar *our_acc = NULL;
2496 our_acc = modest_window_get_active_account (MODEST_WINDOW (user_data));
2497 parent_acc = modest_tny_account_get_parent_modest_account_name_for_server_account (account);
2499 /* Close this window if I'm showing a message of the removed account */
2500 if (our_acc && parent_acc && strcmp (parent_acc, our_acc) == 0)
2501 modest_ui_actions_on_close_window (NULL, MODEST_WINDOW (user_data));
2506 on_mail_operation_started (ModestMailOperation *mail_op,
2509 ModestMsgViewWindow *self;
2510 ModestMailOperationTypeOperation op_type;
2512 ModestMsgViewWindowPrivate *priv;
2513 GObject *source = NULL;
2515 self = MODEST_MSG_VIEW_WINDOW (user_data);
2516 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
2517 op_type = modest_mail_operation_get_type_operation (mail_op);
2518 tmp = priv->progress_widgets;
2519 source = modest_mail_operation_get_source(mail_op);
2520 if (G_OBJECT (self) == source) {
2521 if (op_type == MODEST_MAIL_OPERATION_TYPE_RECEIVE ||
2522 op_type == MODEST_MAIL_OPERATION_TYPE_OPEN ||
2523 op_type == MODEST_MAIL_OPERATION_TYPE_DELETE) {
2524 set_progress_hint (self, TRUE);
2526 modest_progress_object_add_operation (
2527 MODEST_PROGRESS_OBJECT (tmp->data),
2529 tmp = g_slist_next (tmp);
2533 g_object_unref (source);
2535 /* Update dimming rules */
2536 check_dimming_rules_after_change (self);
2540 on_mail_operation_finished (ModestMailOperation *mail_op,
2543 ModestMsgViewWindow *self;
2544 ModestMailOperationTypeOperation op_type;
2546 ModestMsgViewWindowPrivate *priv;
2548 self = MODEST_MSG_VIEW_WINDOW (user_data);
2549 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
2550 op_type = modest_mail_operation_get_type_operation (mail_op);
2551 tmp = priv->progress_widgets;
2553 if (op_type == MODEST_MAIL_OPERATION_TYPE_RECEIVE ||
2554 op_type == MODEST_MAIL_OPERATION_TYPE_OPEN ||
2555 op_type == MODEST_MAIL_OPERATION_TYPE_DELETE) {
2557 modest_progress_object_remove_operation (MODEST_PROGRESS_OBJECT (tmp->data),
2559 tmp = g_slist_next (tmp);
2562 /* If no more operations are being observed, NORMAL mode is enabled again */
2563 if (observers_empty (self)) {
2564 set_progress_hint (self, FALSE);
2568 /* Update dimming rules. We have to do this right here
2569 and not in view_msg_cb because at that point the
2570 transfer mode is still enabled so the dimming rule
2571 won't let the user delete the message that has been
2572 readed for example */
2573 check_dimming_rules_after_change (self);
2577 on_queue_changed (ModestMailOperationQueue *queue,
2578 ModestMailOperation *mail_op,
2579 ModestMailOperationQueueNotification type,
2580 ModestMsgViewWindow *self)
2582 ModestMsgViewWindowPrivate *priv;
2584 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
2586 /* If this operations was created by another window, do nothing */
2587 if (!modest_mail_operation_is_mine (mail_op, G_OBJECT(self)))
2590 if (type == MODEST_MAIL_OPERATION_QUEUE_OPERATION_ADDED) {
2591 priv->sighandlers = modest_signal_mgr_connect (priv->sighandlers,
2593 "operation-started",
2594 G_CALLBACK (on_mail_operation_started),
2596 priv->sighandlers = modest_signal_mgr_connect (priv->sighandlers,
2598 "operation-finished",
2599 G_CALLBACK (on_mail_operation_finished),
2601 } else if (type == MODEST_MAIL_OPERATION_QUEUE_OPERATION_REMOVED) {
2602 priv->sighandlers = modest_signal_mgr_disconnect (priv->sighandlers,
2604 "operation-started");
2605 priv->sighandlers = modest_signal_mgr_disconnect (priv->sighandlers,
2607 "operation-finished");
2612 modest_msg_view_window_get_attachments (ModestMsgViewWindow *win)
2614 ModestMsgViewWindowPrivate *priv;
2615 TnyList *selected_attachments = NULL;
2617 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (win), NULL);
2618 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (win);
2620 /* In Hildon 2.2 as there's no selection we assume we have all attachments selected */
2621 selected_attachments = modest_msg_view_get_attachments (MODEST_MSG_VIEW (priv->msg_view));
2623 return selected_attachments;
2627 ModestMsgViewWindow *self;
2629 gchar *attachment_uid;
2630 } DecodeAsyncHelper;
2633 on_decode_to_stream_async_handler (TnyMimePart *mime_part,
2639 DecodeAsyncHelper *helper = (DecodeAsyncHelper *) user_data;
2640 const gchar *content_type;
2642 if (cancelled || err) {
2645 if ((err->domain == TNY_ERROR_DOMAIN) &&
2646 (err->code == TNY_IO_ERROR_WRITE) &&
2647 (errno == ENOSPC)) {
2648 msg = g_strdup_printf (_KR("cerm_device_memory_full"), "");
2650 msg = g_strdup (_("mail_ib_file_operation_failed"));
2652 modest_platform_information_banner (NULL, NULL, msg);
2658 /* It could happen that the window was closed. So we
2659 assume it is a cancelation */
2660 if (!GTK_WIDGET_VISIBLE (helper->self))
2663 /* Remove the progress hint */
2664 set_progress_hint (helper->self, FALSE);
2666 content_type = tny_mime_part_get_content_type (mime_part);
2667 if (g_str_has_prefix (content_type, "message/rfc822")) {
2668 ModestWindowMgr *mgr;
2669 ModestWindow *msg_win = NULL;
2672 const gchar *mailbox;
2673 TnyStream *file_stream;
2676 fd = g_open (helper->file_path, O_RDONLY, 0644);
2678 file_stream = tny_fs_stream_new (fd);
2680 mgr = modest_runtime_get_window_mgr ();
2682 account = g_strdup (modest_window_get_active_account (MODEST_WINDOW (helper->self)));
2683 mailbox = modest_window_get_active_mailbox (MODEST_WINDOW (helper->self));
2686 account = modest_account_mgr_get_default_account (modest_runtime_get_account_mgr ());
2688 msg = tny_camel_msg_new ();
2689 tny_camel_msg_parse (msg, file_stream);
2690 msg_win = modest_msg_view_window_new_for_attachment (TNY_MSG (msg), account, mailbox, helper->attachment_uid);
2691 modest_window_set_zoom (MODEST_WINDOW (msg_win),
2692 modest_window_get_zoom (MODEST_WINDOW (helper->self)));
2693 if (modest_window_mgr_register_window (mgr, msg_win, MODEST_WINDOW (helper->self)))
2694 gtk_widget_show_all (GTK_WIDGET (msg_win));
2696 gtk_widget_destroy (GTK_WIDGET (msg_win));
2697 g_object_unref (msg);
2698 g_object_unref (file_stream);
2700 modest_platform_information_banner (NULL, NULL, _("mail_ib_file_operation_failed"));
2705 /* make the file read-only */
2706 g_chmod(helper->file_path, 0444);
2708 /* Activate the file */
2709 modest_platform_activate_file (helper->file_path, tny_mime_part_get_content_type (mime_part));
2714 g_object_unref (helper->self);
2715 g_free (helper->file_path);
2716 g_free (helper->attachment_uid);
2717 g_slice_free (DecodeAsyncHelper, helper);
2721 modest_msg_view_window_view_attachment (ModestMsgViewWindow *window,
2722 TnyMimePart *mime_part)
2724 ModestMsgViewWindowPrivate *priv;
2725 const gchar *msg_uid;
2726 gchar *attachment_uid = NULL;
2727 gint attachment_index = 0;
2728 TnyList *attachments;
2730 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window));
2731 g_return_if_fail (TNY_IS_MIME_PART (mime_part) || (mime_part == NULL));
2732 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
2734 msg_uid = modest_msg_view_window_get_message_uid (MODEST_MSG_VIEW_WINDOW (window));
2735 attachments = modest_msg_view_get_attachments (MODEST_MSG_VIEW (priv->msg_view));
2736 attachment_index = modest_list_index (attachments, (GObject *) mime_part);
2737 g_object_unref (attachments);
2739 if (msg_uid && attachment_index >= 0) {
2740 attachment_uid = g_strdup_printf ("%s/%d", msg_uid, attachment_index);
2743 if (mime_part == NULL) {
2744 gboolean error = FALSE;
2745 TnyList *selected_attachments = modest_msg_view_get_selected_attachments (MODEST_MSG_VIEW (priv->msg_view));
2746 if (selected_attachments == NULL || tny_list_get_length (selected_attachments) == 0) {
2748 } else if (tny_list_get_length (selected_attachments) > 1) {
2749 hildon_banner_show_information (NULL, NULL, _("mcen_ib_unable_to_display_more"));
2753 iter = tny_list_create_iterator (selected_attachments);
2754 mime_part = (TnyMimePart *) tny_iterator_get_current (iter);
2755 g_object_unref (iter);
2757 if (selected_attachments)
2758 g_object_unref (selected_attachments);
2763 g_object_ref (mime_part);
2766 if (tny_mime_part_is_purged (mime_part))
2769 if (!modest_tny_mime_part_is_msg (mime_part) && tny_mime_part_get_filename (mime_part)) {
2770 gchar *filepath = NULL;
2771 const gchar *att_filename = tny_mime_part_get_filename (mime_part);
2772 gboolean show_error_banner = FALSE;
2773 TnyFsStream *temp_stream = NULL;
2774 temp_stream = modest_utils_create_temp_stream (att_filename, attachment_uid,
2777 if (temp_stream != NULL) {
2778 ModestAccountMgr *mgr;
2779 DecodeAsyncHelper *helper;
2780 gboolean decode_in_provider;
2781 ModestProtocol *protocol;
2782 const gchar *account;
2784 /* Activate progress hint */
2785 set_progress_hint (window, TRUE);
2787 helper = g_slice_new0 (DecodeAsyncHelper);
2788 helper->self = g_object_ref (window);
2789 helper->file_path = g_strdup (filepath);
2790 helper->attachment_uid = g_strdup (attachment_uid);
2792 decode_in_provider = FALSE;
2793 mgr = modest_runtime_get_account_mgr ();
2794 account = modest_window_get_active_account (MODEST_WINDOW (window));
2795 if (modest_account_mgr_account_is_multimailbox (mgr, account, &protocol)) {
2796 if (MODEST_IS_ACCOUNT_PROTOCOL (protocol)) {
2798 uri = g_strconcat ("file://", filepath, NULL);
2799 decode_in_provider =
2800 modest_account_protocol_decode_part_to_stream_async (
2801 MODEST_ACCOUNT_PROTOCOL (protocol),
2804 TNY_STREAM (temp_stream),
2805 on_decode_to_stream_async_handler,
2812 if (!decode_in_provider)
2813 tny_mime_part_decode_to_stream_async (mime_part, TNY_STREAM (temp_stream),
2814 on_decode_to_stream_async_handler,
2817 g_object_unref (temp_stream);
2818 /* NOTE: files in the temporary area will be automatically
2819 * cleaned after some time if they are no longer in use */
2822 const gchar *content_type;
2823 /* the file may already exist but it isn't writable,
2824 * let's try to open it anyway */
2825 content_type = tny_mime_part_get_content_type (mime_part);
2826 modest_platform_activate_file (filepath, content_type);
2828 g_warning ("%s: modest_utils_create_temp_stream failed", __FUNCTION__);
2829 show_error_banner = TRUE;
2834 if (show_error_banner)
2835 modest_platform_information_banner (NULL, NULL, _("mail_ib_file_operation_failed"));
2836 } else if (!modest_tny_mime_part_is_msg (mime_part)) {
2837 ModestWindowMgr *mgr;
2838 ModestWindow *msg_win = NULL;
2839 TnyMsg *current_msg;
2843 current_msg = modest_msg_view_window_get_message (MODEST_MSG_VIEW_WINDOW (window));
2844 mgr = modest_runtime_get_window_mgr ();
2845 header = tny_msg_get_header (TNY_MSG (current_msg));
2846 found = modest_window_mgr_find_registered_message_uid (mgr,
2851 g_debug ("window for this body is already being created");
2854 /* it's not found, so create a new window for it */
2855 modest_window_mgr_register_header (mgr, header, attachment_uid); /* register the uid before building the window */
2856 gchar *account = g_strdup (modest_window_get_active_account (MODEST_WINDOW (window)));
2857 const gchar *mailbox = modest_window_get_active_mailbox (MODEST_WINDOW (window));
2859 account = modest_account_mgr_get_default_account (modest_runtime_get_account_mgr ());
2861 msg_win = modest_msg_view_window_new_with_other_body (TNY_MSG (current_msg), TNY_MIME_PART (mime_part),
2862 account, mailbox, attachment_uid);
2864 modest_window_set_zoom (MODEST_WINDOW (msg_win),
2865 modest_window_get_zoom (MODEST_WINDOW (window)));
2866 if (modest_window_mgr_register_window (mgr, msg_win, MODEST_WINDOW (window)))
2867 gtk_widget_show_all (GTK_WIDGET (msg_win));
2869 gtk_widget_destroy (GTK_WIDGET (msg_win));
2871 g_object_unref (current_msg);
2873 /* message attachment */
2874 TnyHeader *header = NULL;
2875 ModestWindowMgr *mgr;
2876 ModestWindow *msg_win = NULL;
2879 header = tny_msg_get_header (TNY_MSG (mime_part));
2880 mgr = modest_runtime_get_window_mgr ();
2881 found = modest_window_mgr_find_registered_header (mgr, header, &msg_win);
2884 /* if it's found, but there is no msg_win, it's probably in the process of being created;
2885 * thus, we don't do anything */
2886 g_debug ("window for is already being created");
2888 /* it's not found, so create a new window for it */
2889 modest_window_mgr_register_header (mgr, header, attachment_uid); /* register the uid before building the window */
2890 gchar *account = g_strdup (modest_window_get_active_account (MODEST_WINDOW (window)));
2891 const gchar *mailbox = modest_window_get_active_mailbox (MODEST_WINDOW (window));
2893 account = modest_account_mgr_get_default_account (modest_runtime_get_account_mgr ());
2894 msg_win = modest_msg_view_window_new_for_attachment (TNY_MSG (mime_part), account,
2895 mailbox, attachment_uid);
2896 modest_window_set_zoom (MODEST_WINDOW (msg_win),
2897 modest_window_get_zoom (MODEST_WINDOW (window)));
2898 if (modest_window_mgr_register_window (mgr, msg_win, MODEST_WINDOW (window)))
2899 gtk_widget_show_all (GTK_WIDGET (msg_win));
2901 gtk_widget_destroy (GTK_WIDGET (msg_win));
2907 g_free (attachment_uid);
2909 g_object_unref (mime_part);
2921 GnomeVFSResult result;
2923 ModestMsgViewWindow *window;
2926 static void save_mime_part_info_free (SaveMimePartInfo *info, gboolean with_struct);
2927 static gboolean idle_save_mime_part_show_result (SaveMimePartInfo *info);
2928 static gpointer save_mime_part_to_file (SaveMimePartInfo *info);
2929 static void save_mime_parts_to_file_with_checks (GtkWindow *parent, SaveMimePartInfo *info);
2932 save_mime_part_info_free (SaveMimePartInfo *info, gboolean with_struct)
2936 for (node = info->pairs; node != NULL; node = g_list_next (node)) {
2937 SaveMimePartPair *pair = (SaveMimePartPair *) node->data;
2938 g_free (pair->filename);
2939 g_object_unref (pair->part);
2940 g_slice_free (SaveMimePartPair, pair);
2942 g_list_free (info->pairs);
2945 g_object_unref (info->window);
2946 info->window = NULL;
2948 g_slice_free (SaveMimePartInfo, info);
2953 idle_save_mime_part_show_result (SaveMimePartInfo *info)
2955 /* This is a GDK lock because we are an idle callback and
2956 * hildon_banner_show_information is or does Gtk+ code */
2958 gdk_threads_enter (); /* CHECKED */
2959 if (info->result == GNOME_VFS_OK) {
2960 hildon_banner_show_information (NULL, NULL, _CS("sfil_ib_saved"));
2961 } else if (info->result == GNOME_VFS_ERROR_NO_SPACE) {
2964 /* Check if the uri belongs to the external mmc */
2965 if (g_str_has_prefix (info->uri, g_getenv (MODEST_MMC1_VOLUMEPATH_ENV)))
2966 msg = g_strdup_printf (_KR("cerm_device_memory_full"), "");
2968 msg = g_strdup (_KR("cerm_memory_card_full"));
2969 modest_platform_information_banner (NULL, NULL, msg);
2972 hildon_banner_show_information (NULL, NULL, _("mail_ib_file_operation_failed"));
2974 save_mime_part_info_free (info, FALSE);
2975 gdk_threads_leave (); /* CHECKED */
2981 save_mime_part_to_file (SaveMimePartInfo *info)
2983 GnomeVFSHandle *handle;
2985 SaveMimePartPair *pair = (SaveMimePartPair *) info->pairs->data;
2987 info->result = gnome_vfs_create (&handle, pair->filename, GNOME_VFS_OPEN_WRITE, FALSE, 0644);
2988 if (info->result == GNOME_VFS_OK) {
2989 GError *error = NULL;
2990 gboolean decode_in_provider;
2992 ModestAccountMgr *mgr;
2993 const gchar *account;
2994 ModestProtocol *protocol = NULL;
2996 stream = tny_vfs_stream_new (handle);
2998 decode_in_provider = FALSE;
2999 mgr = modest_runtime_get_account_mgr ();
3000 account = modest_window_get_active_account (MODEST_WINDOW (info->window));
3001 if (modest_account_mgr_account_is_multimailbox (mgr, account, &protocol)) {
3002 if (MODEST_IS_ACCOUNT_PROTOCOL (protocol)) {
3003 decode_in_provider =
3004 modest_account_protocol_decode_part_to_stream (
3005 MODEST_ACCOUNT_PROTOCOL (protocol),
3013 if (!decode_in_provider)
3014 written = tny_mime_part_decode_to_stream (pair->part, stream, &error);
3017 g_warning ("modest: could not save attachment %s: %d (%s)\n", pair->filename, error?error->code:-1, error?error->message:"Unknown error");
3019 if ((error->domain == TNY_ERROR_DOMAIN) &&
3020 (error->code == TNY_IO_ERROR_WRITE) &&
3021 (errno == ENOSPC)) {
3022 info->result = GNOME_VFS_ERROR_NO_SPACE;
3024 info->result = GNOME_VFS_ERROR_IO;
3027 g_object_unref (G_OBJECT (stream));
3029 g_warning ("Could not create save attachment %s: %s\n",
3030 pair->filename, gnome_vfs_result_to_string (info->result));
3033 /* Go on saving remaining files */
3034 info->pairs = g_list_remove_link (info->pairs, info->pairs);
3035 if (info->pairs != NULL) {
3036 save_mime_part_to_file (info);
3038 g_idle_add ((GSourceFunc) idle_save_mime_part_show_result, info);
3045 save_mime_parts_to_file_with_checks (GtkWindow *parent,
3046 SaveMimePartInfo *info)
3048 gboolean is_ok = TRUE;
3049 gint replaced_files = 0;
3050 const GList *files = info->pairs;
3051 const GList *iter, *to_replace = NULL;
3053 for (iter = files; (iter != NULL) && (replaced_files < 2); iter = g_list_next(iter)) {
3054 SaveMimePartPair *pair = iter->data;
3055 gchar *unescaped = g_uri_unescape_string (pair->filename, NULL);
3057 if (modest_utils_file_exists (unescaped)) {
3059 if (replaced_files == 1)
3064 if (replaced_files) {
3067 if (replaced_files == 1) {
3068 SaveMimePartPair *pair = to_replace->data;
3069 const gchar *basename = strrchr (pair->filename, G_DIR_SEPARATOR) + 1;
3070 gchar *escaped_basename, *message;
3072 escaped_basename = g_uri_unescape_string (basename, NULL);
3073 message = g_strdup_printf ("%s\n%s",
3074 _FM("docm_nc_replace_file"),
3075 (escaped_basename) ? escaped_basename : "");
3076 response = modest_platform_run_confirmation_dialog (parent, message);
3078 g_free (escaped_basename);
3080 response = modest_platform_run_confirmation_dialog (parent,
3081 _FM("docm_nc_replace_multiple"));
3083 if (response != GTK_RESPONSE_OK)
3088 save_mime_part_info_free (info, TRUE);
3090 g_thread_create ((GThreadFunc)save_mime_part_to_file, info, FALSE, NULL);
3095 typedef struct _SaveAttachmentsInfo {
3096 TnyList *attachments_list;
3097 ModestMsgViewWindow *window;
3098 } SaveAttachmentsInfo;
3101 save_attachments_response (GtkDialog *dialog,
3105 TnyList *mime_parts;
3107 GList *files_to_save = NULL;
3108 gchar *current_folder;
3109 SaveAttachmentsInfo *sa_info = (SaveAttachmentsInfo *) user_data;
3111 mime_parts = TNY_LIST (sa_info->attachments_list);
3113 if (arg1 != GTK_RESPONSE_OK)
3116 chooser_uri = gtk_file_chooser_get_uri (GTK_FILE_CHOOSER (dialog));
3117 current_folder = gtk_file_chooser_get_current_folder_uri (GTK_FILE_CHOOSER (dialog));
3118 if (current_folder && *current_folder != '\0') {
3120 modest_conf_set_string (modest_runtime_get_conf (), MODEST_CONF_LATEST_SAVE_ATTACHMENT_PATH,
3121 current_folder,&err);
3123 g_debug ("Error storing latest used folder: %s", err->message);
3127 g_free (current_folder);
3129 if (!modest_utils_folder_writable (chooser_uri)) {
3130 const gchar *err_msg;
3132 #ifdef MODEST_PLATFORM_MAEMO
3133 if (modest_maemo_utils_in_usb_mode ()) {
3134 err_msg = dgettext ("hildon-status-bar-usb", "usbh_ib_mmc_usb_connected");
3136 err_msg = _FM("sfil_ib_readonly_location");
3139 err_msg = _FM("sfil_ib_readonly_location");
3141 hildon_banner_show_information (NULL, NULL, err_msg);
3145 iter = tny_list_create_iterator (mime_parts);
3146 while (!tny_iterator_is_done (iter)) {
3147 TnyMimePart *mime_part = (TnyMimePart *) tny_iterator_get_current (iter);
3149 if ((modest_tny_mime_part_is_attachment_for_modest (mime_part)) &&
3150 !tny_mime_part_is_purged (mime_part) &&
3151 (tny_mime_part_get_filename (mime_part) != NULL)) {
3152 SaveMimePartPair *pair;
3154 pair = g_slice_new0 (SaveMimePartPair);
3156 if (tny_list_get_length (mime_parts) > 1) {
3158 gnome_vfs_escape_slashes (tny_mime_part_get_filename (mime_part));
3159 pair->filename = g_build_filename (chooser_uri, escaped, NULL);
3162 pair->filename = g_strdup (chooser_uri);
3164 pair->part = mime_part;
3165 files_to_save = g_list_prepend (files_to_save, pair);
3167 tny_iterator_next (iter);
3169 g_object_unref (iter);
3172 if (files_to_save != NULL) {
3173 SaveMimePartInfo *info = g_slice_new0 (SaveMimePartInfo);
3174 info->pairs = files_to_save;
3175 info->result = TRUE;
3176 info->uri = g_strdup (chooser_uri);
3177 info->window = g_object_ref (sa_info->window);
3178 save_mime_parts_to_file_with_checks ((GtkWindow *) dialog, info);
3180 g_free (chooser_uri);
3183 /* Free and close the dialog */
3184 g_object_unref (mime_parts);
3185 g_object_unref (sa_info->window);
3186 g_slice_free (SaveAttachmentsInfo, sa_info);
3187 gtk_widget_destroy (GTK_WIDGET (dialog));
3191 modest_msg_view_window_save_attachments (ModestMsgViewWindow *window,
3192 TnyList *mime_parts)
3194 ModestMsgViewWindowPrivate *priv;
3195 GtkWidget *save_dialog = NULL;
3196 gchar *conf_folder = NULL;
3197 gchar *filename = NULL;
3198 gchar *save_multiple_str = NULL;
3199 const gchar *root_folder = "file:///";
3201 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window));
3202 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
3204 if (mime_parts == NULL) {
3205 /* In Hildon 2.2 save and delete operate over all the attachments as there's no
3206 * selection available */
3207 mime_parts = modest_msg_view_get_attachments (MODEST_MSG_VIEW (priv->msg_view));
3208 if (mime_parts && !modest_maemo_utils_select_attachments (GTK_WINDOW (window), mime_parts, FALSE)) {
3209 g_object_unref (mime_parts);
3212 if (mime_parts == NULL || tny_list_get_length (mime_parts) == 0) {
3214 g_object_unref (mime_parts);
3220 g_object_ref (mime_parts);
3223 /* prepare dialog */
3224 if (tny_list_get_length (mime_parts) == 1) {
3226 /* only one attachment selected */
3227 iter = tny_list_create_iterator (mime_parts);
3228 TnyMimePart *mime_part = (TnyMimePart *) tny_iterator_get_current (iter);
3229 g_object_unref (iter);
3230 if (!modest_tny_mime_part_is_msg (mime_part) &&
3231 modest_tny_mime_part_is_attachment_for_modest (mime_part) &&
3232 !tny_mime_part_is_purged (mime_part)) {
3233 filename = g_strdup (tny_mime_part_get_filename (mime_part));
3235 /* TODO: show any error? */
3236 g_warning ("%s: Tried to save a non-file attachment", __FUNCTION__);
3237 g_object_unref (mime_parts);
3240 g_object_unref (mime_part);
3242 gint num = tny_list_get_length (mime_parts);
3243 save_multiple_str = g_strdup_printf (dngettext("hildon-fm",
3244 "sfil_va_number_of_objects_attachment",
3245 "sfil_va_number_of_objects_attachments",
3249 save_dialog = hildon_file_chooser_dialog_new (GTK_WINDOW (window),
3250 GTK_FILE_CHOOSER_ACTION_SAVE);
3252 /* Get last used folder */
3253 conf_folder = modest_conf_get_string (modest_runtime_get_conf (),
3254 MODEST_CONF_LATEST_SAVE_ATTACHMENT_PATH, NULL);
3256 /* File chooser stops working if we select "file:///" as current folder */
3257 if (conf_folder && g_ascii_strcasecmp (root_folder, conf_folder) != 0) {
3258 g_free (conf_folder);
3262 if (conf_folder && conf_folder[0] != '\0') {
3263 gtk_file_chooser_set_current_folder_uri (GTK_FILE_CHOOSER (save_dialog), conf_folder);
3266 /* Set the default folder to documents folder */
3267 docs_folder = (gchar *) g_strdup(g_get_user_special_dir (G_USER_DIRECTORY_DOCUMENTS));
3270 docs_folder = g_build_filename (g_getenv (MYDOCS_ENV), DOCS_FOLDER, NULL);
3272 gtk_file_chooser_set_current_folder (GTK_FILE_CHOOSER (save_dialog), docs_folder);
3273 g_free (docs_folder);
3275 g_free (conf_folder);
3279 gtk_file_chooser_set_current_name (GTK_FILE_CHOOSER (save_dialog),
3284 /* if multiple, set multiple string */
3285 if (save_multiple_str) {
3286 g_object_set (G_OBJECT (save_dialog), "save-multiple", save_multiple_str, NULL);
3287 gtk_window_set_title (GTK_WINDOW (save_dialog), _FM("sfil_ti_save_objects_files"));
3288 g_free (save_multiple_str);
3291 /* We must run this asynchronously, because the hildon dialog
3292 performs a gtk_dialog_run by itself which leads to gdk
3294 SaveAttachmentsInfo *sa_info;
3295 sa_info = g_slice_new (SaveAttachmentsInfo);
3296 sa_info->attachments_list = mime_parts;
3297 sa_info->window = g_object_ref (window);
3298 g_signal_connect (save_dialog, "response",
3299 G_CALLBACK (save_attachments_response), sa_info);
3301 gtk_widget_show_all (save_dialog);
3305 show_remove_attachment_information (gpointer userdata)
3307 ModestMsgViewWindow *window = (ModestMsgViewWindow *) userdata;
3308 ModestMsgViewWindowPrivate *priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
3310 /* We're outside the main lock */
3311 gdk_threads_enter ();
3313 if (priv->remove_attachment_banner != NULL) {
3314 gtk_widget_destroy (priv->remove_attachment_banner);
3315 g_object_unref (priv->remove_attachment_banner);
3318 priv->remove_attachment_banner = g_object_ref (
3319 hildon_banner_show_animation (NULL, NULL, _("mcen_me_inbox_remove_attachments")));
3321 gdk_threads_leave ();
3327 modest_msg_view_window_remove_attachments (ModestMsgViewWindow *window, gboolean get_all)
3329 ModestMsgViewWindowPrivate *priv;
3330 TnyList *mime_parts = NULL, *tmp;
3331 gchar *confirmation_message;
3337 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window));
3338 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
3340 /* In hildon 2.2 we ignore the get_all flag as we always get all attachments. This is
3341 * because we don't have selection
3343 mime_parts = modest_msg_view_get_attachments (MODEST_MSG_VIEW (priv->msg_view));
3345 /* Remove already purged messages from mime parts list. We use
3346 a copy of the list to remove items in the original one */
3347 tmp = tny_list_copy (mime_parts);
3348 iter = tny_list_create_iterator (tmp);
3349 while (!tny_iterator_is_done (iter)) {
3350 TnyMimePart *part = TNY_MIME_PART (tny_iterator_get_current (iter));
3351 if (tny_mime_part_is_purged (part))
3352 tny_list_remove (mime_parts, (GObject *) part);
3354 g_object_unref (part);
3355 tny_iterator_next (iter);
3357 g_object_unref (tmp);
3358 g_object_unref (iter);
3360 if (!modest_maemo_utils_select_attachments (GTK_WINDOW (window), mime_parts, TRUE) ||
3361 tny_list_get_length (mime_parts) == 0) {
3362 g_object_unref (mime_parts);
3366 n_attachments = tny_list_get_length (mime_parts);
3367 if (n_attachments == 1) {
3371 iter = tny_list_create_iterator (mime_parts);
3372 part = (TnyMimePart *) tny_iterator_get_current (iter);
3373 g_object_unref (iter);
3374 if (modest_tny_mime_part_is_msg (part)) {
3376 header = tny_msg_get_header (TNY_MSG (part));
3377 filename = tny_header_dup_subject (header);
3378 g_object_unref (header);
3379 if (filename == NULL)
3380 filename = g_strdup (_("mail_va_no_subject"));
3382 filename = g_strdup (tny_mime_part_get_filename (TNY_MIME_PART (part)));
3384 confirmation_message = g_strdup_printf (_("mcen_nc_purge_file_text"), filename);
3386 g_object_unref (part);
3388 confirmation_message = g_strdup_printf (ngettext("mcen_nc_purge_file_text",
3389 "mcen_nc_purge_files_text",
3390 n_attachments), n_attachments);
3392 response = modest_platform_run_confirmation_dialog (GTK_WINDOW (window),
3393 confirmation_message);
3394 g_free (confirmation_message);
3396 if (response != GTK_RESPONSE_OK) {
3397 g_object_unref (mime_parts);
3401 priv->purge_timeout = g_timeout_add (2000, show_remove_attachment_information, window);
3403 iter = tny_list_create_iterator (mime_parts);
3404 while (!tny_iterator_is_done (iter)) {
3407 part = (TnyMimePart *) tny_iterator_get_current (iter);
3408 tny_mime_part_set_purged (TNY_MIME_PART (part));
3409 g_object_unref (part);
3410 tny_iterator_next (iter);
3412 g_object_unref (iter);
3414 msg = tny_msg_view_get_msg (TNY_MSG_VIEW (priv->msg_view));
3415 tny_msg_view_clear (TNY_MSG_VIEW (priv->msg_view));
3416 tny_msg_rewrite_cache (msg);
3417 tny_msg_view_set_msg (TNY_MSG_VIEW (priv->msg_view), msg);
3418 g_object_unref (msg);
3419 update_branding (MODEST_MSG_VIEW_WINDOW (window));
3421 g_object_unref (mime_parts);
3423 if (priv->purge_timeout > 0) {
3424 g_source_remove (priv->purge_timeout);
3425 priv->purge_timeout = 0;
3428 if (priv->remove_attachment_banner) {
3429 gtk_widget_destroy (priv->remove_attachment_banner);
3430 g_object_unref (priv->remove_attachment_banner);
3431 priv->remove_attachment_banner = NULL;
3437 update_window_title (ModestMsgViewWindow *window)
3439 ModestMsgViewWindowPrivate *priv;
3441 TnyHeader *header = NULL;
3442 gchar *subject = NULL;
3444 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
3446 /* Note that if the window is closed while we're retrieving
3447 the message, this widget could de deleted */
3448 if (!priv->msg_view)
3451 msg = tny_msg_view_get_msg (TNY_MSG_VIEW (priv->msg_view));
3453 if (priv->other_body) {
3456 description = modest_tny_mime_part_get_header_value (priv->other_body, "Content-Description");
3458 g_strstrip (description);
3459 subject = description;
3461 } else if (msg != NULL) {
3462 header = tny_msg_get_header (msg);
3463 subject = tny_header_dup_subject (header);
3464 g_object_unref (header);
3465 g_object_unref (msg);
3468 if ((subject == NULL)||(subject[0] == '\0')) {
3470 subject = g_strdup (_("mail_va_no_subject"));
3473 gtk_window_set_title (GTK_WINDOW (window), subject);
3478 on_move_focus (GtkWidget *widget,
3479 GtkDirectionType direction,
3482 g_signal_stop_emission_by_name (G_OBJECT (widget), "move-focus");
3486 fetch_image_open_stream (TnyStreamCache *self, gint64 *expected_size, gchar *uri)
3488 GnomeVFSResult result;
3489 GnomeVFSHandle *handle = NULL;
3490 GnomeVFSFileInfo *info = NULL;
3493 result = gnome_vfs_open (&handle, uri, GNOME_VFS_OPEN_READ);
3494 if (result != GNOME_VFS_OK) {
3499 info = gnome_vfs_file_info_new ();
3500 result = gnome_vfs_get_file_info_from_handle (handle, info, GNOME_VFS_FILE_INFO_DEFAULT);
3501 if (result != GNOME_VFS_OK || ! (info->valid_fields & GNOME_VFS_FILE_INFO_FIELDS_SIZE)) {
3502 /* We put a "safe" default size for going to cache */
3503 *expected_size = (300*1024);
3505 *expected_size = info->size;
3507 gnome_vfs_file_info_unref (info);
3509 stream = tny_vfs_stream_new (handle);
3518 TnyStream *output_stream;
3519 GtkWidget *msg_view;
3524 on_fetch_image_idle_refresh_view (gpointer userdata)
3527 FetchImageData *fidata = (FetchImageData *) userdata;
3529 gdk_threads_enter ();
3530 if (GTK_WIDGET_DRAWABLE (fidata->msg_view)) {
3531 ModestMsgViewWindowPrivate *priv;
3533 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (fidata->window);
3534 priv->fetching_images--;
3535 gtk_widget_queue_draw (fidata->msg_view);
3536 update_progress_hint (MODEST_MSG_VIEW_WINDOW (fidata->window));
3538 gdk_threads_leave ();
3540 g_object_unref (fidata->msg_view);
3541 g_object_unref (fidata->window);
3542 g_slice_free (FetchImageData, fidata);
3547 on_fetch_image_thread (gpointer userdata)
3549 FetchImageData *fidata = (FetchImageData *) userdata;
3550 TnyStreamCache *cache;
3551 TnyStream *cache_stream;
3553 cache = modest_runtime_get_images_cache ();
3555 tny_stream_cache_get_stream (cache,
3557 (TnyStreamCacheOpenStreamFetcher) fetch_image_open_stream,
3558 (gpointer) fidata->uri);
3559 g_free (fidata->cache_id);
3560 g_free (fidata->uri);
3562 if (cache_stream != NULL) {
3565 while (G_LIKELY (!tny_stream_is_eos (cache_stream))) {
3568 nb_read = tny_stream_read (cache_stream, buffer, sizeof (buffer));
3569 if (G_UNLIKELY (nb_read < 0)) {
3571 } else if (G_LIKELY (nb_read > 0)) {
3572 gssize nb_written = 0;
3574 while (G_UNLIKELY (nb_written < nb_read)) {
3577 len = tny_stream_write (fidata->output_stream, buffer + nb_written,
3578 nb_read - nb_written);
3579 if (G_UNLIKELY (len < 0))
3585 tny_stream_close (cache_stream);
3586 g_object_unref (cache_stream);
3589 tny_stream_close (fidata->output_stream);
3590 g_object_unref (fidata->output_stream);
3592 g_idle_add (on_fetch_image_idle_refresh_view, fidata);
3598 on_fetch_image (ModestMsgView *msgview,
3601 ModestMsgViewWindow *window)
3603 const gchar *current_account;
3604 ModestMsgViewWindowPrivate *priv;
3605 FetchImageData *fidata;
3607 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
3609 current_account = modest_window_get_active_account (MODEST_WINDOW (window));
3611 fidata = g_slice_new0 (FetchImageData);
3612 fidata->msg_view = g_object_ref (msgview);
3613 fidata->window = g_object_ref (window);
3614 fidata->uri = g_strdup (uri);
3615 fidata->cache_id = modest_images_cache_get_id (current_account, uri);
3616 fidata->output_stream = g_object_ref (stream);
3618 priv->fetching_images++;
3619 if (g_thread_create (on_fetch_image_thread, fidata, FALSE, NULL) == NULL) {
3620 g_object_unref (fidata->output_stream);
3621 g_free (fidata->cache_id);
3622 g_free (fidata->uri);
3623 g_object_unref (fidata->msg_view);
3624 g_slice_free (FetchImageData, fidata);
3625 tny_stream_close (stream);
3626 priv->fetching_images--;
3627 update_progress_hint (window);
3630 update_progress_hint (window);
3636 setup_menu (ModestMsgViewWindow *self)
3638 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW(self));
3640 /* Settings menu buttons */
3641 modest_hildon2_window_add_to_menu (MODEST_HILDON2_WINDOW (self), _("mcen_me_viewer_find"), NULL,
3642 APP_MENU_CALLBACK (modest_msg_view_window_show_find_toolbar),
3643 MODEST_DIMMING_CALLBACK (modest_ui_dimming_rules_on_find_in_msg));
3645 modest_hildon2_window_add_to_menu (MODEST_HILDON2_WINDOW (self),
3646 dngettext(GETTEXT_PACKAGE,
3647 "mcen_me_move_message",
3648 "mcen_me_move_messages",
3651 APP_MENU_CALLBACK (modest_ui_actions_on_move_to),
3652 MODEST_DIMMING_CALLBACK (modest_ui_dimming_rules_on_move_to));
3654 modest_hildon2_window_add_to_menu (MODEST_HILDON2_WINDOW (self), _("mcen_me_inbox_mark_as_read"), NULL,
3655 APP_MENU_CALLBACK (modest_ui_actions_on_mark_as_read),
3656 MODEST_DIMMING_CALLBACK (modest_ui_dimming_rules_on_mark_as_read_msg_in_view));
3658 modest_hildon2_window_add_to_menu (MODEST_HILDON2_WINDOW (self), _("mcen_me_inbox_mark_as_unread"), NULL,
3659 APP_MENU_CALLBACK (modest_ui_actions_on_mark_as_unread),
3660 MODEST_DIMMING_CALLBACK (modest_ui_dimming_rules_on_mark_as_unread_msg_in_view));
3662 modest_hildon2_window_add_to_menu (MODEST_HILDON2_WINDOW (self), _("mcen_me_viewer_save_attachments"), NULL,
3663 APP_MENU_CALLBACK (modest_ui_actions_save_attachments),
3664 MODEST_DIMMING_CALLBACK (modest_ui_dimming_rules_on_save_attachments));
3665 modest_hildon2_window_add_to_menu (MODEST_HILDON2_WINDOW (self), _("mcen_me_inbox_remove_attachments"), NULL,
3666 APP_MENU_CALLBACK (modest_ui_actions_remove_attachments),
3667 MODEST_DIMMING_CALLBACK (modest_ui_dimming_rules_on_remove_attachments));
3669 modest_hildon2_window_add_to_menu (MODEST_HILDON2_WINDOW (self), _("mcen_me_new_message"), "<Control>n",
3670 APP_MENU_CALLBACK (modest_ui_actions_on_new_msg),
3671 MODEST_DIMMING_CALLBACK (modest_ui_dimming_rules_on_new_msg));
3672 modest_hildon2_window_add_to_menu (MODEST_HILDON2_WINDOW (self), _("mcen_me_viewer_addtocontacts"), NULL,
3673 APP_MENU_CALLBACK (modest_ui_actions_add_to_contacts),
3674 MODEST_DIMMING_CALLBACK (modest_ui_dimming_rules_on_add_to_contacts));
3676 modest_hildon2_window_add_to_menu (MODEST_HILDON2_WINDOW (self), _("mcen_ti_message_properties"), NULL,
3677 APP_MENU_CALLBACK (modest_ui_actions_on_details),
3678 MODEST_DIMMING_CALLBACK (modest_ui_dimming_rules_on_details));
3682 modest_msg_view_window_add_to_contacts (ModestMsgViewWindow *self)
3684 ModestMsgViewWindowPrivate *priv;
3685 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
3686 GSList *recipients = NULL;
3689 msg = tny_msg_view_get_msg (TNY_MSG_VIEW (priv->msg_view));
3693 header = modest_msg_view_window_get_header (self);
3696 recipients = modest_tny_msg_header_get_all_recipients_list (header);
3697 g_object_unref (header);
3699 recipients = modest_tny_msg_get_all_recipients_list (msg);
3700 g_object_unref (msg);
3704 /* Offer the user to add recipients to the address book */
3705 modest_address_book_add_address_list_with_selector (recipients, (GtkWindow *) self);
3706 g_slist_foreach (recipients, (GFunc) g_free, NULL); g_slist_free (recipients);
3711 _modest_msg_view_window_map_event (GtkWidget *widget,
3715 ModestMsgViewWindow *self = (ModestMsgViewWindow *) userdata;
3717 update_progress_hint (self);
3723 modest_msg_view_window_fetch_images (ModestMsgViewWindow *self)
3725 ModestMsgViewWindowPrivate *priv;
3726 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
3728 modest_msg_view_request_fetch_images (MODEST_MSG_VIEW (priv->msg_view));
3732 modest_msg_view_window_has_blocked_external_images (ModestMsgViewWindow *self)
3734 ModestMsgViewWindowPrivate *priv;
3735 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
3737 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self), FALSE);
3739 return modest_msg_view_has_blocked_external_images (MODEST_MSG_VIEW (priv->msg_view));
3743 modest_msg_view_window_reload (ModestMsgViewWindow *self)
3745 ModestMsgViewWindowPrivate *priv;
3746 const gchar *msg_uid;
3747 TnyHeader *header = NULL;
3748 TnyFolder *folder = NULL;
3750 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self));
3752 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
3754 header = modest_msg_view_window_get_header (MODEST_MSG_VIEW_WINDOW (self));
3758 folder = tny_header_get_folder (header);
3759 g_object_unref (header);
3764 msg_uid = modest_msg_view_window_get_message_uid (self);
3766 GtkTreeRowReference *row_reference;
3768 if (priv->row_reference && gtk_tree_row_reference_valid (priv->row_reference)) {
3769 row_reference = priv->row_reference;
3771 row_reference = NULL;
3773 if (!message_reader (self, priv, NULL, msg_uid, folder, row_reference))
3774 g_warning ("Shouldn't happen, trying to reload a message failed");
3777 g_object_unref (folder);
3781 update_branding (ModestMsgViewWindow *self)
3783 const gchar *account;
3784 const gchar *mailbox;
3785 ModestAccountMgr *mgr;
3786 ModestProtocol *protocol = NULL;
3787 gchar *service_name = NULL;
3788 const GdkPixbuf *service_icon = NULL;
3789 ModestMsgViewWindowPrivate *priv;
3791 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
3793 account = modest_window_get_active_account (MODEST_WINDOW (self));
3794 mailbox = modest_window_get_active_mailbox (MODEST_WINDOW (self));
3796 mgr = modest_runtime_get_account_mgr ();
3798 if (modest_account_mgr_account_is_multimailbox (mgr, account, &protocol)) {
3799 if (MODEST_IS_ACCOUNT_PROTOCOL (protocol)) {
3800 service_name = modest_account_protocol_get_service_name (MODEST_ACCOUNT_PROTOCOL (protocol),
3802 service_icon = modest_account_protocol_get_service_icon (MODEST_ACCOUNT_PROTOCOL (protocol),
3803 account, mailbox, MODEST_ICON_SIZE_SMALL);
3807 modest_msg_view_set_branding (MODEST_MSG_VIEW (priv->msg_view), service_name, service_icon);
3808 g_free (service_name);
3812 sync_flags (ModestMsgViewWindow *self)
3814 TnyHeader *header = NULL;
3816 header = modest_msg_view_window_get_header (self);
3818 TnyMsg *msg = modest_msg_view_window_get_message (self);
3820 header = tny_msg_get_header (msg);
3821 g_object_unref (msg);
3826 TnyFolder *folder = tny_header_get_folder (header);
3829 ModestMailOperation *mail_op;
3831 /* Sync folder, we need this to save the seen flag */
3832 mail_op = modest_mail_operation_new (NULL);
3833 modest_mail_operation_queue_add (modest_runtime_get_mail_operation_queue (),
3835 modest_mail_operation_sync_folder (mail_op, folder, FALSE, NULL, NULL);
3836 g_object_unref (mail_op);
3837 g_object_unref (folder);
3839 g_object_unref (header);