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));
1884 modest_ui_actions_on_close_window (NULL, MODEST_WINDOW (parent_window));
1888 /* Register the header - it'll be unregistered in the callback */
1890 modest_window_mgr_register_header (modest_runtime_get_window_mgr (), info->header, NULL);
1892 /* New mail operation */
1893 mail_op = modest_mail_operation_new_with_error_handling (G_OBJECT(parent_window),
1894 modest_ui_actions_disk_operations_error_handler,
1897 modest_mail_operation_queue_add (modest_runtime_get_mail_operation_queue (), mail_op);
1899 modest_mail_operation_get_msg (mail_op, info->header, TRUE, view_msg_cb, info->row_reference);
1901 modest_mail_operation_find_msg (mail_op, info->folder, info->msg_uid, TRUE, view_msg_cb, NULL);
1902 g_object_unref (mail_op);
1904 /* Update dimming rules */
1905 modest_ui_actions_check_toolbar_dimming_rules (MODEST_WINDOW (parent_window));
1906 modest_ui_actions_check_menu_dimming_rules (MODEST_WINDOW (parent_window));
1909 /* Frees. The row_reference will be freed by the view_msg_cb callback */
1910 g_free (info->msg_uid);
1912 g_object_unref (info->folder);
1914 g_object_unref (info->header);
1915 g_slice_free (MsgReaderInfo, info);
1920 * Reads the message whose summary item is @header. It takes care of
1921 * several things, among others:
1923 * If the message was not previously downloaded then ask the user
1924 * before downloading. If there is no connection launch the connection
1925 * dialog. Update toolbar dimming rules.
1927 * Returns: TRUE if the mail operation was started, otherwise if the
1928 * user do not want to download the message, or if the user do not
1929 * want to connect, then the operation is not issued
1932 message_reader (ModestMsgViewWindow *window,
1933 ModestMsgViewWindowPrivate *priv,
1935 const gchar *msg_uid,
1937 GtkTreeRowReference *row_reference)
1939 ModestWindowMgr *mgr;
1940 TnyAccount *account = NULL;
1941 MsgReaderInfo *info;
1943 /* We set the header from model while we're loading */
1944 tny_header_view_set_header (TNY_HEADER_VIEW (priv->msg_view), header);
1945 gtk_window_set_title (GTK_WINDOW (window), _CS("ckdg_pb_updating"));
1951 g_object_ref (folder);
1953 mgr = modest_runtime_get_window_mgr ();
1954 /* Msg download completed */
1955 if (!header || !(tny_header_get_flags (header) & TNY_HEADER_FLAG_CACHED)) {
1957 /* Ask the user if he wants to download the message if
1959 if (!tny_device_is_online (modest_runtime_get_device())) {
1960 GtkResponseType response;
1962 response = modest_platform_run_confirmation_dialog (GTK_WINDOW (window),
1963 _("mcen_nc_get_msg"));
1964 if (response == GTK_RESPONSE_CANCEL) {
1965 update_window_title (window);
1970 folder = tny_header_get_folder (header);
1972 info = g_slice_new (MsgReaderInfo);
1973 info->msg_uid = g_strdup (msg_uid);
1975 info->header = g_object_ref (header);
1977 info->header = NULL;
1979 info->folder = g_object_ref (folder);
1981 info->folder = NULL;
1982 if (row_reference) {
1983 info->row_reference = gtk_tree_row_reference_copy (row_reference);
1985 info->row_reference = NULL;
1988 /* Offer the connection dialog if necessary */
1989 modest_platform_connect_if_remote_and_perform ((GtkWindow *) window,
1991 TNY_FOLDER_STORE (folder),
1992 message_reader_performer,
1995 g_object_unref (folder);
2001 folder = tny_header_get_folder (header);
2004 account = tny_folder_get_account (folder);
2006 info = g_slice_new (MsgReaderInfo);
2007 info->msg_uid = g_strdup (msg_uid);
2009 info->folder = g_object_ref (folder);
2011 info->folder = NULL;
2013 info->header = g_object_ref (header);
2015 info->header = NULL;
2017 info->row_reference = gtk_tree_row_reference_copy (row_reference);
2019 info->row_reference = NULL;
2021 message_reader_performer (FALSE, NULL, (GtkWindow *) window, account, info);
2023 g_object_unref (account);
2025 g_object_unref (folder);
2031 modest_msg_view_window_select_next_message (ModestMsgViewWindow *window)
2033 ModestMsgViewWindowPrivate *priv;
2034 GtkTreePath *path= NULL;
2035 GtkTreeIter tmp_iter;
2037 gboolean retval = TRUE;
2038 GtkTreeRowReference *row_reference = NULL;
2040 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), FALSE);
2041 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
2043 if (!priv->row_reference)
2046 /* Update the next row reference if it's not valid. This could
2047 happen if for example the header which it was pointing to,
2048 was deleted. The best place to do it is in the row-deleted
2049 handler but the tinymail model do not work like the glib
2050 tree models and reports the deletion when the row is still
2052 if (!gtk_tree_row_reference_valid (priv->next_row_reference)) {
2053 if (priv->next_row_reference) {
2054 gtk_tree_row_reference_free (priv->next_row_reference);
2056 if (gtk_tree_row_reference_valid (priv->row_reference)) {
2057 priv->next_row_reference = gtk_tree_row_reference_copy (priv->row_reference);
2058 select_next_valid_row (priv->header_model, &(priv->next_row_reference), FALSE, priv->is_outbox);
2060 priv->next_row_reference = NULL;
2063 if (priv->next_row_reference)
2064 path = gtk_tree_row_reference_get_path (priv->next_row_reference);
2068 row_reference = gtk_tree_row_reference_copy (priv->next_row_reference);
2070 gtk_tree_model_get_iter (priv->header_model,
2073 gtk_tree_path_free (path);
2075 gtk_tree_model_get (priv->header_model, &tmp_iter,
2076 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
2079 /* Read the message & show it */
2080 if (!message_reader (window, priv, header, NULL, NULL, row_reference)) {
2083 gtk_tree_row_reference_free (row_reference);
2086 g_object_unref (header);
2092 modest_msg_view_window_select_previous_message (ModestMsgViewWindow *window)
2094 ModestMsgViewWindowPrivate *priv = NULL;
2096 gboolean finished = FALSE;
2097 gboolean retval = FALSE;
2099 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), FALSE);
2100 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
2102 if (priv->row_reference && !gtk_tree_row_reference_valid (priv->row_reference)) {
2103 gtk_tree_row_reference_free (priv->row_reference);
2104 priv->row_reference = NULL;
2107 /* Return inmediatly if there is no header model */
2108 if (!priv->header_model || !priv->row_reference)
2111 path = gtk_tree_row_reference_get_path (priv->row_reference);
2112 while (!finished && gtk_tree_path_prev (path)) {
2116 gtk_tree_model_get_iter (priv->header_model, &iter, path);
2117 gtk_tree_model_get (priv->header_model, &iter,
2118 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
2122 if (msg_is_visible (header, priv->is_outbox)) {
2123 GtkTreeRowReference *row_reference;
2124 row_reference = gtk_tree_row_reference_new (priv->header_model, path);
2125 /* Read the message & show it */
2126 retval = message_reader (window, priv, header, NULL, NULL, row_reference);
2127 gtk_tree_row_reference_free (row_reference);
2131 g_object_unref (header);
2135 gtk_tree_path_free (path);
2140 view_msg_cb (ModestMailOperation *mail_op,
2147 ModestMsgViewWindow *self = NULL;
2148 ModestMsgViewWindowPrivate *priv = NULL;
2149 GtkTreeRowReference *row_reference = NULL;
2151 /* Unregister the header (it was registered before creating the mail operation) */
2152 modest_window_mgr_unregister_header (modest_runtime_get_window_mgr (), header);
2154 row_reference = (GtkTreeRowReference *) user_data;
2157 gtk_tree_row_reference_free (row_reference);
2158 self = (ModestMsgViewWindow *) modest_mail_operation_get_source (mail_op);
2160 /* Restore window title */
2161 update_window_title (self);
2162 modest_ui_actions_on_close_window (NULL, MODEST_WINDOW (self));
2163 g_object_unref (self);
2168 /* If there was any error */
2169 if (!modest_ui_actions_msg_retrieval_check (mail_op, header, msg)) {
2171 gtk_tree_row_reference_free (row_reference);
2172 self = (ModestMsgViewWindow *) modest_mail_operation_get_source (mail_op);
2174 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
2175 /* First we check if the parent is a folder window */
2176 if (priv->msg_uid && !modest_hildon2_window_mgr_get_folder_window (MODEST_HILDON2_WINDOW_MGR (modest_runtime_get_window_mgr ()))) {
2178 TnyAccount *account = NULL;
2179 GtkWidget *header_window = NULL;
2181 is_merge = g_str_has_prefix (priv->msg_uid, "merge:");
2183 /* Get the account */
2185 account = tny_account_store_find_account (TNY_ACCOUNT_STORE (modest_runtime_get_account_store ()),
2188 if (is_merge || account) {
2189 TnyFolder *folder = NULL;
2191 /* Try to get the message, if it's already downloaded
2192 we don't need to connect */
2194 folder = modest_tny_folder_store_find_folder_from_uri (TNY_FOLDER_STORE (account),
2197 ModestTnyAccountStore *account_store;
2198 ModestTnyLocalFoldersAccount *local_folders_account;
2200 account_store = modest_runtime_get_account_store ();
2201 local_folders_account = MODEST_TNY_LOCAL_FOLDERS_ACCOUNT (
2202 modest_tny_account_store_get_local_folders_account (account_store));
2203 folder = modest_tny_local_folders_account_get_merged_outbox (local_folders_account);
2204 g_object_unref (local_folders_account);
2206 if (account) g_object_unref (account);
2209 header_window = (GtkWidget *)
2210 modest_header_window_new (
2212 modest_window_get_active_account (MODEST_WINDOW (self)),
2213 modest_window_get_active_mailbox (MODEST_WINDOW (self)));
2214 if (!modest_window_mgr_register_window (modest_runtime_get_window_mgr (),
2215 MODEST_WINDOW (header_window),
2217 gtk_widget_destroy (GTK_WIDGET (header_window));
2219 gtk_widget_show_all (GTK_WIDGET (header_window));
2221 g_object_unref (folder);
2227 /* Restore window title */
2228 update_window_title (self);
2229 modest_ui_actions_on_close_window (NULL, MODEST_WINDOW (self));
2230 g_object_unref (self);
2235 /* Get the window */
2236 self = (ModestMsgViewWindow *) modest_mail_operation_get_source (mail_op);
2237 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self));
2238 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
2240 /* Update the row reference */
2241 if (priv->row_reference != NULL) {
2242 gtk_tree_row_reference_free (priv->row_reference);
2243 priv->row_reference = (row_reference && gtk_tree_row_reference_valid (row_reference))?gtk_tree_row_reference_copy (row_reference):NULL;
2244 if (priv->next_row_reference != NULL) {
2245 gtk_tree_row_reference_free (priv->next_row_reference);
2247 if (priv->row_reference) {
2248 priv->next_row_reference = gtk_tree_row_reference_copy (priv->row_reference);
2249 select_next_valid_row (priv->header_model, &(priv->next_row_reference), TRUE, priv->is_outbox);
2251 priv->next_row_reference = NULL;
2255 /* Mark header as read */
2256 if (!(tny_header_get_flags (header) & TNY_HEADER_FLAG_SEEN))
2257 tny_header_set_flag (header, TNY_HEADER_FLAG_SEEN);
2259 /* Set new message */
2260 if (priv->msg_view != NULL && TNY_IS_MSG_VIEW (priv->msg_view)) {
2261 tny_msg_view_set_msg (TNY_MSG_VIEW (priv->msg_view), msg);
2262 modest_msg_view_window_update_priority (self);
2263 update_window_title (MODEST_MSG_VIEW_WINDOW (self));
2264 update_branding (MODEST_MSG_VIEW_WINDOW (self));
2265 modest_msg_view_grab_focus (MODEST_MSG_VIEW (priv->msg_view));
2268 /* Set the new message uid of the window */
2269 if (priv->msg_uid) {
2270 g_free (priv->msg_uid);
2271 priv->msg_uid = modest_tny_folder_get_header_unique_id (header);
2274 /* Notify the observers */
2275 g_signal_emit (G_OBJECT (self), signals[MSG_CHANGED_SIGNAL],
2276 0, priv->header_model, priv->row_reference);
2278 /* Sync the flags if the message is not opened from a header
2279 model, i.e, if it's opened from a notification */
2280 if (!priv->header_model)
2284 g_object_unref (self);
2286 gtk_tree_row_reference_free (row_reference);
2290 modest_msg_view_window_get_folder_type (ModestMsgViewWindow *window)
2292 ModestMsgViewWindowPrivate *priv;
2294 TnyFolderType folder_type;
2296 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
2298 folder_type = TNY_FOLDER_TYPE_UNKNOWN;
2300 msg = tny_msg_view_get_msg (TNY_MSG_VIEW (priv->msg_view));
2304 folder = tny_msg_get_folder (msg);
2306 folder_type = modest_tny_folder_guess_folder_type (folder);
2307 g_object_unref (folder);
2309 g_object_unref (msg);
2317 modest_msg_view_window_update_priority (ModestMsgViewWindow *window)
2319 ModestMsgViewWindowPrivate *priv;
2320 TnyHeader *header = NULL;
2321 TnyHeaderFlags flags = 0;
2323 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
2325 if (priv->header_model && priv->row_reference && gtk_tree_row_reference_valid (priv->row_reference)) {
2327 GtkTreePath *path = NULL;
2329 path = gtk_tree_row_reference_get_path (priv->row_reference);
2330 g_return_if_fail (path != NULL);
2331 gtk_tree_model_get_iter (priv->header_model,
2333 gtk_tree_row_reference_get_path (priv->row_reference));
2335 gtk_tree_model_get (priv->header_model, &iter, TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
2337 gtk_tree_path_free (path);
2340 msg = tny_msg_view_get_msg (TNY_MSG_VIEW (priv->msg_view));
2342 header = tny_msg_get_header (msg);
2343 g_object_unref (msg);
2348 flags = tny_header_get_flags (header);
2349 g_object_unref(G_OBJECT(header));
2352 modest_msg_view_set_priority (MODEST_MSG_VIEW(priv->msg_view), flags);
2357 toolbar_resize (ModestMsgViewWindow *self)
2359 ModestMsgViewWindowPrivate *priv = NULL;
2360 ModestWindowPrivate *parent_priv = NULL;
2362 gint static_button_size;
2363 ModestWindowMgr *mgr;
2365 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self));
2366 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
2367 parent_priv = MODEST_WINDOW_GET_PRIVATE(self);
2369 mgr = modest_runtime_get_window_mgr ();
2370 static_button_size = modest_window_mgr_get_fullscreen_mode (mgr)?120:120;
2372 if (parent_priv->toolbar) {
2373 /* Set expandable and homogeneous tool buttons */
2374 widget = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/ToolbarMessageReply");
2375 gtk_tool_item_set_expand (GTK_TOOL_ITEM (widget), TRUE);
2376 gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (widget), TRUE);
2377 widget = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/ToolbarMessageReplyAll");
2378 gtk_tool_item_set_expand (GTK_TOOL_ITEM (widget), TRUE);
2379 gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (widget), TRUE);
2380 widget = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/ToolbarMessageForward");
2381 gtk_tool_item_set_expand (GTK_TOOL_ITEM (widget), TRUE);
2382 gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (widget), TRUE);
2383 widget = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/ToolbarDeleteMessage");
2384 gtk_tool_item_set_expand (GTK_TOOL_ITEM (widget), TRUE);
2385 gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (widget), TRUE);
2386 widget = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/ToolbarDownloadExternalImages");
2387 gtk_tool_item_set_expand (GTK_TOOL_ITEM (widget), TRUE);
2388 gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (widget), TRUE);
2389 gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (priv->next_toolitem), TRUE);
2390 gtk_tool_item_set_expand (GTK_TOOL_ITEM (priv->next_toolitem), TRUE);
2391 gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (priv->prev_toolitem), TRUE);
2392 gtk_tool_item_set_expand (GTK_TOOL_ITEM (priv->prev_toolitem), TRUE);
2397 modest_msg_view_window_show_toolbar (ModestWindow *self,
2398 gboolean show_toolbar)
2400 ModestMsgViewWindowPrivate *priv = NULL;
2401 ModestWindowPrivate *parent_priv;
2403 parent_priv = MODEST_WINDOW_GET_PRIVATE(self);
2404 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(self);
2406 /* Set optimized view status */
2407 priv->optimized_view = !show_toolbar;
2409 if (!parent_priv->toolbar) {
2410 parent_priv->toolbar = gtk_ui_manager_get_widget (parent_priv->ui_manager,
2412 gtk_toolbar_set_icon_size (GTK_TOOLBAR (parent_priv->toolbar), HILDON_ICON_SIZE_FINGER);
2413 gtk_widget_set_no_show_all (parent_priv->toolbar, TRUE);
2415 priv->next_toolitem = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/ToolbarMessageNext");
2416 priv->prev_toolitem = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/ToolbarMessageBack");
2417 toolbar_resize (MODEST_MSG_VIEW_WINDOW (self));
2420 hildon_window_add_toolbar (HILDON_WINDOW (self),
2421 GTK_TOOLBAR (parent_priv->toolbar));
2426 /* Quick hack: this prevents toolbar icons "dance" when progress bar show status is changed */
2427 /* TODO: resize mode migth be GTK_RESIZE_QUEUE, in order to avoid unneccesary shows */
2428 gtk_container_set_resize_mode (GTK_CONTAINER(parent_priv->toolbar), GTK_RESIZE_IMMEDIATE);
2430 gtk_widget_show (GTK_WIDGET (parent_priv->toolbar));
2431 if (modest_msg_view_window_transfer_mode_enabled (MODEST_MSG_VIEW_WINDOW (self)))
2432 set_progress_hint (MODEST_MSG_VIEW_WINDOW (self), TRUE);
2434 set_progress_hint (MODEST_MSG_VIEW_WINDOW (self), FALSE);
2437 gtk_widget_set_no_show_all (parent_priv->toolbar, TRUE);
2438 gtk_widget_hide (GTK_WIDGET (parent_priv->toolbar));
2443 modest_msg_view_window_clipboard_owner_change (GtkClipboard *clipboard,
2445 ModestMsgViewWindow *window)
2447 if (!GTK_WIDGET_VISIBLE (window))
2450 modest_window_check_dimming_rules_group (MODEST_WINDOW (window), MODEST_DIMMING_RULES_CLIPBOARD);
2454 modest_msg_view_window_transfer_mode_enabled (ModestMsgViewWindow *self)
2456 ModestMsgViewWindowPrivate *priv;
2458 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self), FALSE);
2459 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(self);
2461 return priv->progress_hint;
2465 observers_empty (ModestMsgViewWindow *self)
2468 ModestMsgViewWindowPrivate *priv;
2469 gboolean is_empty = TRUE;
2470 guint pending_ops = 0;
2472 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(self);
2473 tmp = priv->progress_widgets;
2475 /* Check all observers */
2476 while (tmp && is_empty) {
2477 pending_ops = modest_progress_object_num_pending_operations (MODEST_PROGRESS_OBJECT(tmp->data));
2478 is_empty = pending_ops == 0;
2480 tmp = g_slist_next(tmp);
2487 on_account_removed (TnyAccountStore *account_store,
2488 TnyAccount *account,
2491 /* Do nothing if it's a transport account, because we only
2492 show the messages of a store account */
2493 if (tny_account_get_account_type(account) == TNY_ACCOUNT_TYPE_STORE) {
2494 const gchar *parent_acc = NULL;
2495 const gchar *our_acc = NULL;
2497 our_acc = modest_window_get_active_account (MODEST_WINDOW (user_data));
2498 parent_acc = modest_tny_account_get_parent_modest_account_name_for_server_account (account);
2500 /* Close this window if I'm showing a message of the removed account */
2501 if (our_acc && parent_acc && strcmp (parent_acc, our_acc) == 0)
2502 modest_ui_actions_on_close_window (NULL, MODEST_WINDOW (user_data));
2507 on_mail_operation_started (ModestMailOperation *mail_op,
2510 ModestMsgViewWindow *self;
2511 ModestMailOperationTypeOperation op_type;
2513 ModestMsgViewWindowPrivate *priv;
2514 GObject *source = NULL;
2516 self = MODEST_MSG_VIEW_WINDOW (user_data);
2517 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
2518 op_type = modest_mail_operation_get_type_operation (mail_op);
2519 tmp = priv->progress_widgets;
2520 source = modest_mail_operation_get_source(mail_op);
2521 if (G_OBJECT (self) == source) {
2522 if (op_type == MODEST_MAIL_OPERATION_TYPE_RECEIVE ||
2523 op_type == MODEST_MAIL_OPERATION_TYPE_OPEN ||
2524 op_type == MODEST_MAIL_OPERATION_TYPE_DELETE) {
2525 set_progress_hint (self, TRUE);
2527 modest_progress_object_add_operation (
2528 MODEST_PROGRESS_OBJECT (tmp->data),
2530 tmp = g_slist_next (tmp);
2534 g_object_unref (source);
2536 /* Update dimming rules */
2537 check_dimming_rules_after_change (self);
2541 on_mail_operation_finished (ModestMailOperation *mail_op,
2544 ModestMsgViewWindow *self;
2545 ModestMailOperationTypeOperation op_type;
2547 ModestMsgViewWindowPrivate *priv;
2549 self = MODEST_MSG_VIEW_WINDOW (user_data);
2550 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
2551 op_type = modest_mail_operation_get_type_operation (mail_op);
2552 tmp = priv->progress_widgets;
2554 if (op_type == MODEST_MAIL_OPERATION_TYPE_RECEIVE ||
2555 op_type == MODEST_MAIL_OPERATION_TYPE_OPEN ||
2556 op_type == MODEST_MAIL_OPERATION_TYPE_DELETE) {
2558 modest_progress_object_remove_operation (MODEST_PROGRESS_OBJECT (tmp->data),
2560 tmp = g_slist_next (tmp);
2563 /* If no more operations are being observed, NORMAL mode is enabled again */
2564 if (observers_empty (self)) {
2565 set_progress_hint (self, FALSE);
2569 /* Update dimming rules. We have to do this right here
2570 and not in view_msg_cb because at that point the
2571 transfer mode is still enabled so the dimming rule
2572 won't let the user delete the message that has been
2573 readed for example */
2574 check_dimming_rules_after_change (self);
2578 on_queue_changed (ModestMailOperationQueue *queue,
2579 ModestMailOperation *mail_op,
2580 ModestMailOperationQueueNotification type,
2581 ModestMsgViewWindow *self)
2583 ModestMsgViewWindowPrivate *priv;
2585 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
2587 /* If this operations was created by another window, do nothing */
2588 if (!modest_mail_operation_is_mine (mail_op, G_OBJECT(self)))
2591 if (type == MODEST_MAIL_OPERATION_QUEUE_OPERATION_ADDED) {
2592 priv->sighandlers = modest_signal_mgr_connect (priv->sighandlers,
2594 "operation-started",
2595 G_CALLBACK (on_mail_operation_started),
2597 priv->sighandlers = modest_signal_mgr_connect (priv->sighandlers,
2599 "operation-finished",
2600 G_CALLBACK (on_mail_operation_finished),
2602 } else if (type == MODEST_MAIL_OPERATION_QUEUE_OPERATION_REMOVED) {
2603 priv->sighandlers = modest_signal_mgr_disconnect (priv->sighandlers,
2605 "operation-started");
2606 priv->sighandlers = modest_signal_mgr_disconnect (priv->sighandlers,
2608 "operation-finished");
2613 modest_msg_view_window_get_attachments (ModestMsgViewWindow *win)
2615 ModestMsgViewWindowPrivate *priv;
2616 TnyList *selected_attachments = NULL;
2618 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (win), NULL);
2619 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (win);
2621 /* In Hildon 2.2 as there's no selection we assume we have all attachments selected */
2622 selected_attachments = modest_msg_view_get_attachments (MODEST_MSG_VIEW (priv->msg_view));
2624 return selected_attachments;
2628 ModestMsgViewWindow *self;
2630 gchar *attachment_uid;
2631 } DecodeAsyncHelper;
2634 on_decode_to_stream_async_handler (TnyMimePart *mime_part,
2640 DecodeAsyncHelper *helper = (DecodeAsyncHelper *) user_data;
2641 const gchar *content_type;
2643 if (cancelled || err) {
2646 if ((err->domain == TNY_ERROR_DOMAIN) &&
2647 (err->code == TNY_IO_ERROR_WRITE) &&
2648 (errno == ENOSPC)) {
2649 msg = g_strdup_printf (_KR("cerm_device_memory_full"), "");
2651 msg = g_strdup (_("mail_ib_file_operation_failed"));
2653 modest_platform_information_banner (NULL, NULL, msg);
2659 /* It could happen that the window was closed. So we
2660 assume it is a cancelation */
2661 if (!GTK_WIDGET_VISIBLE (helper->self))
2664 /* Remove the progress hint */
2665 set_progress_hint (helper->self, FALSE);
2667 content_type = tny_mime_part_get_content_type (mime_part);
2668 if (g_str_has_prefix (content_type, "message/rfc822")) {
2669 ModestWindowMgr *mgr;
2670 ModestWindow *msg_win = NULL;
2673 const gchar *mailbox;
2674 TnyStream *file_stream;
2677 fd = g_open (helper->file_path, O_RDONLY, 0644);
2679 file_stream = tny_fs_stream_new (fd);
2681 mgr = modest_runtime_get_window_mgr ();
2683 account = g_strdup (modest_window_get_active_account (MODEST_WINDOW (helper->self)));
2684 mailbox = modest_window_get_active_mailbox (MODEST_WINDOW (helper->self));
2687 account = modest_account_mgr_get_default_account (modest_runtime_get_account_mgr ());
2689 msg = tny_camel_msg_new ();
2690 tny_camel_msg_parse (msg, file_stream);
2691 msg_win = modest_msg_view_window_new_for_attachment (TNY_MSG (msg), account, mailbox, helper->attachment_uid);
2692 modest_window_set_zoom (MODEST_WINDOW (msg_win),
2693 modest_window_get_zoom (MODEST_WINDOW (helper->self)));
2694 if (modest_window_mgr_register_window (mgr, msg_win, MODEST_WINDOW (helper->self)))
2695 gtk_widget_show_all (GTK_WIDGET (msg_win));
2697 gtk_widget_destroy (GTK_WIDGET (msg_win));
2698 g_object_unref (msg);
2699 g_object_unref (file_stream);
2701 modest_platform_information_banner (NULL, NULL, _("mail_ib_file_operation_failed"));
2706 /* make the file read-only */
2707 g_chmod(helper->file_path, 0444);
2709 /* Activate the file */
2710 modest_platform_activate_file (helper->file_path, tny_mime_part_get_content_type (mime_part));
2715 g_object_unref (helper->self);
2716 g_free (helper->file_path);
2717 g_free (helper->attachment_uid);
2718 g_slice_free (DecodeAsyncHelper, helper);
2722 modest_msg_view_window_view_attachment (ModestMsgViewWindow *window,
2723 TnyMimePart *mime_part)
2725 ModestMsgViewWindowPrivate *priv;
2726 const gchar *msg_uid;
2727 gchar *attachment_uid = NULL;
2728 gint attachment_index = 0;
2729 TnyList *attachments;
2731 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window));
2732 g_return_if_fail (TNY_IS_MIME_PART (mime_part) || (mime_part == NULL));
2733 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
2735 msg_uid = modest_msg_view_window_get_message_uid (MODEST_MSG_VIEW_WINDOW (window));
2736 attachments = modest_msg_view_get_attachments (MODEST_MSG_VIEW (priv->msg_view));
2737 attachment_index = modest_list_index (attachments, (GObject *) mime_part);
2738 g_object_unref (attachments);
2740 if (msg_uid && attachment_index >= 0) {
2741 attachment_uid = g_strdup_printf ("%s/%d", msg_uid, attachment_index);
2744 if (mime_part == NULL) {
2745 gboolean error = FALSE;
2746 TnyList *selected_attachments = modest_msg_view_get_selected_attachments (MODEST_MSG_VIEW (priv->msg_view));
2747 if (selected_attachments == NULL || tny_list_get_length (selected_attachments) == 0) {
2749 } else if (tny_list_get_length (selected_attachments) > 1) {
2750 hildon_banner_show_information (NULL, NULL, _("mcen_ib_unable_to_display_more"));
2754 iter = tny_list_create_iterator (selected_attachments);
2755 mime_part = (TnyMimePart *) tny_iterator_get_current (iter);
2756 g_object_unref (iter);
2758 if (selected_attachments)
2759 g_object_unref (selected_attachments);
2764 g_object_ref (mime_part);
2767 if (tny_mime_part_is_purged (mime_part))
2770 if (!modest_tny_mime_part_is_msg (mime_part) && tny_mime_part_get_filename (mime_part)) {
2771 gchar *filepath = NULL;
2772 const gchar *att_filename = tny_mime_part_get_filename (mime_part);
2773 gboolean show_error_banner = FALSE;
2774 TnyFsStream *temp_stream = NULL;
2775 temp_stream = modest_utils_create_temp_stream (att_filename, attachment_uid,
2778 if (temp_stream != NULL) {
2779 ModestAccountMgr *mgr;
2780 DecodeAsyncHelper *helper;
2781 gboolean decode_in_provider;
2782 ModestProtocol *protocol;
2783 const gchar *account;
2785 /* Activate progress hint */
2786 set_progress_hint (window, TRUE);
2788 helper = g_slice_new0 (DecodeAsyncHelper);
2789 helper->self = g_object_ref (window);
2790 helper->file_path = g_strdup (filepath);
2791 helper->attachment_uid = g_strdup (attachment_uid);
2793 decode_in_provider = FALSE;
2794 mgr = modest_runtime_get_account_mgr ();
2795 account = modest_window_get_active_account (MODEST_WINDOW (window));
2796 if (modest_account_mgr_account_is_multimailbox (mgr, account, &protocol)) {
2797 if (MODEST_IS_ACCOUNT_PROTOCOL (protocol)) {
2799 uri = g_strconcat ("file://", filepath, NULL);
2800 decode_in_provider =
2801 modest_account_protocol_decode_part_to_stream_async (
2802 MODEST_ACCOUNT_PROTOCOL (protocol),
2805 TNY_STREAM (temp_stream),
2806 on_decode_to_stream_async_handler,
2813 if (!decode_in_provider)
2814 tny_mime_part_decode_to_stream_async (mime_part, TNY_STREAM (temp_stream),
2815 on_decode_to_stream_async_handler,
2818 g_object_unref (temp_stream);
2819 /* NOTE: files in the temporary area will be automatically
2820 * cleaned after some time if they are no longer in use */
2823 const gchar *content_type;
2824 /* the file may already exist but it isn't writable,
2825 * let's try to open it anyway */
2826 content_type = tny_mime_part_get_content_type (mime_part);
2827 modest_platform_activate_file (filepath, content_type);
2829 g_warning ("%s: modest_utils_create_temp_stream failed", __FUNCTION__);
2830 show_error_banner = TRUE;
2835 if (show_error_banner)
2836 modest_platform_information_banner (NULL, NULL, _("mail_ib_file_operation_failed"));
2837 } else if (!modest_tny_mime_part_is_msg (mime_part)) {
2838 ModestWindowMgr *mgr;
2839 ModestWindow *msg_win = NULL;
2840 TnyMsg *current_msg;
2844 current_msg = modest_msg_view_window_get_message (MODEST_MSG_VIEW_WINDOW (window));
2845 mgr = modest_runtime_get_window_mgr ();
2846 header = tny_msg_get_header (TNY_MSG (current_msg));
2847 found = modest_window_mgr_find_registered_message_uid (mgr,
2852 g_debug ("window for this body is already being created");
2855 /* it's not found, so create a new window for it */
2856 modest_window_mgr_register_header (mgr, header, attachment_uid); /* register the uid before building the window */
2857 gchar *account = g_strdup (modest_window_get_active_account (MODEST_WINDOW (window)));
2858 const gchar *mailbox = modest_window_get_active_mailbox (MODEST_WINDOW (window));
2860 account = modest_account_mgr_get_default_account (modest_runtime_get_account_mgr ());
2862 msg_win = modest_msg_view_window_new_with_other_body (TNY_MSG (current_msg), TNY_MIME_PART (mime_part),
2863 account, mailbox, attachment_uid);
2865 modest_window_set_zoom (MODEST_WINDOW (msg_win),
2866 modest_window_get_zoom (MODEST_WINDOW (window)));
2867 if (modest_window_mgr_register_window (mgr, msg_win, MODEST_WINDOW (window)))
2868 gtk_widget_show_all (GTK_WIDGET (msg_win));
2870 gtk_widget_destroy (GTK_WIDGET (msg_win));
2872 g_object_unref (current_msg);
2874 /* message attachment */
2875 TnyHeader *header = NULL;
2876 ModestWindowMgr *mgr;
2877 ModestWindow *msg_win = NULL;
2880 header = tny_msg_get_header (TNY_MSG (mime_part));
2881 mgr = modest_runtime_get_window_mgr ();
2882 found = modest_window_mgr_find_registered_header (mgr, header, &msg_win);
2885 /* if it's found, but there is no msg_win, it's probably in the process of being created;
2886 * thus, we don't do anything */
2887 g_debug ("window for is already being created");
2889 /* it's not found, so create a new window for it */
2890 modest_window_mgr_register_header (mgr, header, attachment_uid); /* register the uid before building the window */
2891 gchar *account = g_strdup (modest_window_get_active_account (MODEST_WINDOW (window)));
2892 const gchar *mailbox = modest_window_get_active_mailbox (MODEST_WINDOW (window));
2894 account = modest_account_mgr_get_default_account (modest_runtime_get_account_mgr ());
2895 msg_win = modest_msg_view_window_new_for_attachment (TNY_MSG (mime_part), account,
2896 mailbox, attachment_uid);
2897 modest_window_set_zoom (MODEST_WINDOW (msg_win),
2898 modest_window_get_zoom (MODEST_WINDOW (window)));
2899 if (modest_window_mgr_register_window (mgr, msg_win, MODEST_WINDOW (window)))
2900 gtk_widget_show_all (GTK_WIDGET (msg_win));
2902 gtk_widget_destroy (GTK_WIDGET (msg_win));
2908 g_free (attachment_uid);
2910 g_object_unref (mime_part);
2922 GnomeVFSResult result;
2924 ModestMsgViewWindow *window;
2927 static void save_mime_part_info_free (SaveMimePartInfo *info, gboolean with_struct);
2928 static gboolean idle_save_mime_part_show_result (SaveMimePartInfo *info);
2929 static gpointer save_mime_part_to_file (SaveMimePartInfo *info);
2930 static void save_mime_parts_to_file_with_checks (GtkWindow *parent, SaveMimePartInfo *info);
2933 save_mime_part_info_free (SaveMimePartInfo *info, gboolean with_struct)
2937 for (node = info->pairs; node != NULL; node = g_list_next (node)) {
2938 SaveMimePartPair *pair = (SaveMimePartPair *) node->data;
2939 g_free (pair->filename);
2940 g_object_unref (pair->part);
2941 g_slice_free (SaveMimePartPair, pair);
2943 g_list_free (info->pairs);
2946 g_object_unref (info->window);
2947 info->window = NULL;
2949 g_slice_free (SaveMimePartInfo, info);
2954 idle_save_mime_part_show_result (SaveMimePartInfo *info)
2956 /* This is a GDK lock because we are an idle callback and
2957 * hildon_banner_show_information is or does Gtk+ code */
2959 gdk_threads_enter (); /* CHECKED */
2960 if (info->result == GNOME_VFS_OK) {
2961 hildon_banner_show_information (NULL, NULL, _CS("sfil_ib_saved"));
2962 } else if (info->result == GNOME_VFS_ERROR_NO_SPACE) {
2965 /* Check if the uri belongs to the external mmc */
2966 if (g_str_has_prefix (info->uri, g_getenv (MODEST_MMC1_VOLUMEPATH_ENV)))
2967 msg = g_strdup_printf (_KR("cerm_device_memory_full"), "");
2969 msg = g_strdup (_KR("cerm_memory_card_full"));
2970 modest_platform_information_banner (NULL, NULL, msg);
2973 hildon_banner_show_information (NULL, NULL, _("mail_ib_file_operation_failed"));
2975 save_mime_part_info_free (info, FALSE);
2976 gdk_threads_leave (); /* CHECKED */
2982 save_mime_part_to_file (SaveMimePartInfo *info)
2984 GnomeVFSHandle *handle;
2986 SaveMimePartPair *pair = (SaveMimePartPair *) info->pairs->data;
2988 info->result = gnome_vfs_create (&handle, pair->filename, GNOME_VFS_OPEN_WRITE, FALSE, 0644);
2989 if (info->result == GNOME_VFS_OK) {
2990 GError *error = NULL;
2991 gboolean decode_in_provider;
2993 ModestAccountMgr *mgr;
2994 const gchar *account;
2995 ModestProtocol *protocol = NULL;
2997 stream = tny_vfs_stream_new (handle);
2999 decode_in_provider = FALSE;
3000 mgr = modest_runtime_get_account_mgr ();
3001 account = modest_window_get_active_account (MODEST_WINDOW (info->window));
3002 if (modest_account_mgr_account_is_multimailbox (mgr, account, &protocol)) {
3003 if (MODEST_IS_ACCOUNT_PROTOCOL (protocol)) {
3004 decode_in_provider =
3005 modest_account_protocol_decode_part_to_stream (
3006 MODEST_ACCOUNT_PROTOCOL (protocol),
3014 if (!decode_in_provider)
3015 written = tny_mime_part_decode_to_stream (pair->part, stream, &error);
3018 g_warning ("modest: could not save attachment %s: %d (%s)\n", pair->filename, error?error->code:-1, error?error->message:"Unknown error");
3020 if ((error->domain == TNY_ERROR_DOMAIN) &&
3021 (error->code == TNY_IO_ERROR_WRITE) &&
3022 (errno == ENOSPC)) {
3023 info->result = GNOME_VFS_ERROR_NO_SPACE;
3025 info->result = GNOME_VFS_ERROR_IO;
3028 g_object_unref (G_OBJECT (stream));
3030 g_warning ("Could not create save attachment %s: %s\n",
3031 pair->filename, gnome_vfs_result_to_string (info->result));
3034 /* Go on saving remaining files */
3035 info->pairs = g_list_remove_link (info->pairs, info->pairs);
3036 if (info->pairs != NULL) {
3037 save_mime_part_to_file (info);
3039 g_idle_add ((GSourceFunc) idle_save_mime_part_show_result, info);
3046 save_mime_parts_to_file_with_checks (GtkWindow *parent,
3047 SaveMimePartInfo *info)
3049 gboolean is_ok = TRUE;
3050 gint replaced_files = 0;
3051 const GList *files = info->pairs;
3052 const GList *iter, *to_replace = NULL;
3054 for (iter = files; (iter != NULL) && (replaced_files < 2); iter = g_list_next(iter)) {
3055 SaveMimePartPair *pair = iter->data;
3056 gchar *unescaped = g_uri_unescape_string (pair->filename, NULL);
3058 if (modest_utils_file_exists (unescaped)) {
3060 if (replaced_files == 1)
3065 if (replaced_files) {
3068 if (replaced_files == 1) {
3069 SaveMimePartPair *pair = to_replace->data;
3070 const gchar *basename = strrchr (pair->filename, G_DIR_SEPARATOR) + 1;
3071 gchar *escaped_basename, *message;
3073 escaped_basename = g_uri_unescape_string (basename, NULL);
3074 message = g_strdup_printf ("%s\n%s",
3075 _FM("docm_nc_replace_file"),
3076 (escaped_basename) ? escaped_basename : "");
3077 response = modest_platform_run_confirmation_dialog (parent, message);
3079 g_free (escaped_basename);
3081 response = modest_platform_run_confirmation_dialog (parent,
3082 _FM("docm_nc_replace_multiple"));
3084 if (response != GTK_RESPONSE_OK)
3089 save_mime_part_info_free (info, TRUE);
3091 g_thread_create ((GThreadFunc)save_mime_part_to_file, info, FALSE, NULL);
3096 typedef struct _SaveAttachmentsInfo {
3097 TnyList *attachments_list;
3098 ModestMsgViewWindow *window;
3099 } SaveAttachmentsInfo;
3102 save_attachments_response (GtkDialog *dialog,
3106 TnyList *mime_parts;
3108 GList *files_to_save = NULL;
3109 gchar *current_folder;
3110 SaveAttachmentsInfo *sa_info = (SaveAttachmentsInfo *) user_data;
3112 mime_parts = TNY_LIST (sa_info->attachments_list);
3114 if (arg1 != GTK_RESPONSE_OK)
3117 chooser_uri = gtk_file_chooser_get_uri (GTK_FILE_CHOOSER (dialog));
3118 current_folder = gtk_file_chooser_get_current_folder_uri (GTK_FILE_CHOOSER (dialog));
3119 if (current_folder && *current_folder != '\0') {
3121 modest_conf_set_string (modest_runtime_get_conf (), MODEST_CONF_LATEST_SAVE_ATTACHMENT_PATH,
3122 current_folder,&err);
3124 g_debug ("Error storing latest used folder: %s", err->message);
3128 g_free (current_folder);
3130 if (!modest_utils_folder_writable (chooser_uri)) {
3131 const gchar *err_msg;
3133 #ifdef MODEST_PLATFORM_MAEMO
3134 if (modest_maemo_utils_in_usb_mode ()) {
3135 err_msg = dgettext ("hildon-status-bar-usb", "usbh_ib_mmc_usb_connected");
3137 err_msg = _FM("sfil_ib_readonly_location");
3140 err_msg = _FM("sfil_ib_readonly_location");
3142 hildon_banner_show_information (NULL, NULL, err_msg);
3146 iter = tny_list_create_iterator (mime_parts);
3147 while (!tny_iterator_is_done (iter)) {
3148 TnyMimePart *mime_part = (TnyMimePart *) tny_iterator_get_current (iter);
3150 if ((modest_tny_mime_part_is_attachment_for_modest (mime_part)) &&
3151 !tny_mime_part_is_purged (mime_part) &&
3152 (tny_mime_part_get_filename (mime_part) != NULL)) {
3153 SaveMimePartPair *pair;
3155 pair = g_slice_new0 (SaveMimePartPair);
3157 if (tny_list_get_length (mime_parts) > 1) {
3159 gnome_vfs_escape_slashes (tny_mime_part_get_filename (mime_part));
3160 pair->filename = g_build_filename (chooser_uri, escaped, NULL);
3163 pair->filename = g_strdup (chooser_uri);
3165 pair->part = mime_part;
3166 files_to_save = g_list_prepend (files_to_save, pair);
3168 tny_iterator_next (iter);
3170 g_object_unref (iter);
3173 if (files_to_save != NULL) {
3174 SaveMimePartInfo *info = g_slice_new0 (SaveMimePartInfo);
3175 info->pairs = files_to_save;
3176 info->result = TRUE;
3177 info->uri = g_strdup (chooser_uri);
3178 info->window = g_object_ref (sa_info->window);
3179 save_mime_parts_to_file_with_checks ((GtkWindow *) dialog, info);
3181 g_free (chooser_uri);
3184 /* Free and close the dialog */
3185 g_object_unref (mime_parts);
3186 g_object_unref (sa_info->window);
3187 g_slice_free (SaveAttachmentsInfo, sa_info);
3188 gtk_widget_destroy (GTK_WIDGET (dialog));
3192 modest_msg_view_window_save_attachments (ModestMsgViewWindow *window,
3193 TnyList *mime_parts)
3195 ModestMsgViewWindowPrivate *priv;
3196 GtkWidget *save_dialog = NULL;
3197 gchar *conf_folder = NULL;
3198 gchar *filename = NULL;
3199 gchar *save_multiple_str = NULL;
3200 const gchar *root_folder = "file:///";
3202 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window));
3203 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
3205 if (mime_parts == NULL) {
3206 /* In Hildon 2.2 save and delete operate over all the attachments as there's no
3207 * selection available */
3208 mime_parts = modest_msg_view_get_attachments (MODEST_MSG_VIEW (priv->msg_view));
3209 if (mime_parts && !modest_maemo_utils_select_attachments (GTK_WINDOW (window), mime_parts, FALSE)) {
3210 g_object_unref (mime_parts);
3213 if (mime_parts == NULL || tny_list_get_length (mime_parts) == 0) {
3215 g_object_unref (mime_parts);
3221 g_object_ref (mime_parts);
3224 /* prepare dialog */
3225 if (tny_list_get_length (mime_parts) == 1) {
3227 /* only one attachment selected */
3228 iter = tny_list_create_iterator (mime_parts);
3229 TnyMimePart *mime_part = (TnyMimePart *) tny_iterator_get_current (iter);
3230 g_object_unref (iter);
3231 if (!modest_tny_mime_part_is_msg (mime_part) &&
3232 modest_tny_mime_part_is_attachment_for_modest (mime_part) &&
3233 !tny_mime_part_is_purged (mime_part)) {
3234 filename = g_strdup (tny_mime_part_get_filename (mime_part));
3236 /* TODO: show any error? */
3237 g_warning ("%s: Tried to save a non-file attachment", __FUNCTION__);
3238 g_object_unref (mime_parts);
3241 g_object_unref (mime_part);
3243 gint num = tny_list_get_length (mime_parts);
3244 save_multiple_str = g_strdup_printf (dngettext("hildon-fm",
3245 "sfil_va_number_of_objects_attachment",
3246 "sfil_va_number_of_objects_attachments",
3250 save_dialog = hildon_file_chooser_dialog_new (GTK_WINDOW (window),
3251 GTK_FILE_CHOOSER_ACTION_SAVE);
3253 /* Get last used folder */
3254 conf_folder = modest_conf_get_string (modest_runtime_get_conf (),
3255 MODEST_CONF_LATEST_SAVE_ATTACHMENT_PATH, NULL);
3257 /* File chooser stops working if we select "file:///" as current folder */
3258 if (conf_folder && g_ascii_strcasecmp (root_folder, conf_folder) != 0) {
3259 g_free (conf_folder);
3263 if (conf_folder && conf_folder[0] != '\0') {
3264 gtk_file_chooser_set_current_folder_uri (GTK_FILE_CHOOSER (save_dialog), conf_folder);
3267 /* Set the default folder to documents folder */
3268 docs_folder = (gchar *) g_strdup(g_get_user_special_dir (G_USER_DIRECTORY_DOCUMENTS));
3271 docs_folder = g_build_filename (g_getenv (MYDOCS_ENV), DOCS_FOLDER, NULL);
3273 gtk_file_chooser_set_current_folder (GTK_FILE_CHOOSER (save_dialog), docs_folder);
3274 g_free (docs_folder);
3276 g_free (conf_folder);
3280 gtk_file_chooser_set_current_name (GTK_FILE_CHOOSER (save_dialog),
3285 /* if multiple, set multiple string */
3286 if (save_multiple_str) {
3287 g_object_set (G_OBJECT (save_dialog), "save-multiple", save_multiple_str, NULL);
3288 gtk_window_set_title (GTK_WINDOW (save_dialog), _FM("sfil_ti_save_objects_files"));
3289 g_free (save_multiple_str);
3292 /* We must run this asynchronously, because the hildon dialog
3293 performs a gtk_dialog_run by itself which leads to gdk
3295 SaveAttachmentsInfo *sa_info;
3296 sa_info = g_slice_new (SaveAttachmentsInfo);
3297 sa_info->attachments_list = mime_parts;
3298 sa_info->window = g_object_ref (window);
3299 g_signal_connect (save_dialog, "response",
3300 G_CALLBACK (save_attachments_response), sa_info);
3302 gtk_widget_show_all (save_dialog);
3306 show_remove_attachment_information (gpointer userdata)
3308 ModestMsgViewWindow *window = (ModestMsgViewWindow *) userdata;
3309 ModestMsgViewWindowPrivate *priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
3311 /* We're outside the main lock */
3312 gdk_threads_enter ();
3314 if (priv->remove_attachment_banner != NULL) {
3315 gtk_widget_destroy (priv->remove_attachment_banner);
3316 g_object_unref (priv->remove_attachment_banner);
3319 priv->remove_attachment_banner = g_object_ref (
3320 hildon_banner_show_animation (NULL, NULL, _("mcen_me_inbox_remove_attachments")));
3322 gdk_threads_leave ();
3328 modest_msg_view_window_remove_attachments (ModestMsgViewWindow *window, gboolean get_all)
3330 ModestMsgViewWindowPrivate *priv;
3331 TnyList *mime_parts = NULL, *tmp;
3332 gchar *confirmation_message;
3338 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window));
3339 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
3341 /* In hildon 2.2 we ignore the get_all flag as we always get all attachments. This is
3342 * because we don't have selection
3344 mime_parts = modest_msg_view_get_attachments (MODEST_MSG_VIEW (priv->msg_view));
3346 /* Remove already purged messages from mime parts list. We use
3347 a copy of the list to remove items in the original one */
3348 tmp = tny_list_copy (mime_parts);
3349 iter = tny_list_create_iterator (tmp);
3350 while (!tny_iterator_is_done (iter)) {
3351 TnyMimePart *part = TNY_MIME_PART (tny_iterator_get_current (iter));
3352 if (tny_mime_part_is_purged (part))
3353 tny_list_remove (mime_parts, (GObject *) part);
3355 g_object_unref (part);
3356 tny_iterator_next (iter);
3358 g_object_unref (tmp);
3359 g_object_unref (iter);
3361 if (!modest_maemo_utils_select_attachments (GTK_WINDOW (window), mime_parts, TRUE) ||
3362 tny_list_get_length (mime_parts) == 0) {
3363 g_object_unref (mime_parts);
3367 n_attachments = tny_list_get_length (mime_parts);
3368 if (n_attachments == 1) {
3372 iter = tny_list_create_iterator (mime_parts);
3373 part = (TnyMimePart *) tny_iterator_get_current (iter);
3374 g_object_unref (iter);
3375 if (modest_tny_mime_part_is_msg (part)) {
3377 header = tny_msg_get_header (TNY_MSG (part));
3378 filename = tny_header_dup_subject (header);
3379 g_object_unref (header);
3380 if (filename == NULL)
3381 filename = g_strdup (_("mail_va_no_subject"));
3383 filename = g_strdup (tny_mime_part_get_filename (TNY_MIME_PART (part)));
3385 confirmation_message = g_strdup_printf (_("mcen_nc_purge_file_text"), filename);
3387 g_object_unref (part);
3389 confirmation_message = g_strdup_printf (ngettext("mcen_nc_purge_file_text",
3390 "mcen_nc_purge_files_text",
3391 n_attachments), n_attachments);
3393 response = modest_platform_run_confirmation_dialog (GTK_WINDOW (window),
3394 confirmation_message);
3395 g_free (confirmation_message);
3397 if (response != GTK_RESPONSE_OK) {
3398 g_object_unref (mime_parts);
3402 priv->purge_timeout = g_timeout_add (2000, show_remove_attachment_information, window);
3404 iter = tny_list_create_iterator (mime_parts);
3405 while (!tny_iterator_is_done (iter)) {
3408 part = (TnyMimePart *) tny_iterator_get_current (iter);
3409 tny_mime_part_set_purged (TNY_MIME_PART (part));
3410 g_object_unref (part);
3411 tny_iterator_next (iter);
3413 g_object_unref (iter);
3415 msg = tny_msg_view_get_msg (TNY_MSG_VIEW (priv->msg_view));
3416 tny_msg_view_clear (TNY_MSG_VIEW (priv->msg_view));
3417 tny_msg_rewrite_cache (msg);
3418 tny_msg_view_set_msg (TNY_MSG_VIEW (priv->msg_view), msg);
3419 g_object_unref (msg);
3420 update_branding (MODEST_MSG_VIEW_WINDOW (window));
3422 g_object_unref (mime_parts);
3424 if (priv->purge_timeout > 0) {
3425 g_source_remove (priv->purge_timeout);
3426 priv->purge_timeout = 0;
3429 if (priv->remove_attachment_banner) {
3430 gtk_widget_destroy (priv->remove_attachment_banner);
3431 g_object_unref (priv->remove_attachment_banner);
3432 priv->remove_attachment_banner = NULL;
3438 update_window_title (ModestMsgViewWindow *window)
3440 ModestMsgViewWindowPrivate *priv;
3442 TnyHeader *header = NULL;
3443 gchar *subject = NULL;
3445 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
3447 /* Note that if the window is closed while we're retrieving
3448 the message, this widget could de deleted */
3449 if (!priv->msg_view)
3452 msg = tny_msg_view_get_msg (TNY_MSG_VIEW (priv->msg_view));
3454 if (priv->other_body) {
3457 description = modest_tny_mime_part_get_header_value (priv->other_body, "Content-Description");
3459 g_strstrip (description);
3460 subject = description;
3462 } else if (msg != NULL) {
3463 header = tny_msg_get_header (msg);
3464 subject = tny_header_dup_subject (header);
3465 g_object_unref (header);
3466 g_object_unref (msg);
3469 if ((subject == NULL)||(subject[0] == '\0')) {
3471 subject = g_strdup (_("mail_va_no_subject"));
3474 gtk_window_set_title (GTK_WINDOW (window), subject);
3479 on_move_focus (GtkWidget *widget,
3480 GtkDirectionType direction,
3483 g_signal_stop_emission_by_name (G_OBJECT (widget), "move-focus");
3487 fetch_image_open_stream (TnyStreamCache *self, gint64 *expected_size, gchar *uri)
3489 GnomeVFSResult result;
3490 GnomeVFSHandle *handle = NULL;
3491 GnomeVFSFileInfo *info = NULL;
3494 result = gnome_vfs_open (&handle, uri, GNOME_VFS_OPEN_READ);
3495 if (result != GNOME_VFS_OK) {
3500 info = gnome_vfs_file_info_new ();
3501 result = gnome_vfs_get_file_info_from_handle (handle, info, GNOME_VFS_FILE_INFO_DEFAULT);
3502 if (result != GNOME_VFS_OK || ! (info->valid_fields & GNOME_VFS_FILE_INFO_FIELDS_SIZE)) {
3503 /* We put a "safe" default size for going to cache */
3504 *expected_size = (300*1024);
3506 *expected_size = info->size;
3508 gnome_vfs_file_info_unref (info);
3510 stream = tny_vfs_stream_new (handle);
3519 TnyStream *output_stream;
3520 GtkWidget *msg_view;
3525 on_fetch_image_idle_refresh_view (gpointer userdata)
3528 FetchImageData *fidata = (FetchImageData *) userdata;
3530 gdk_threads_enter ();
3531 if (GTK_WIDGET_DRAWABLE (fidata->msg_view)) {
3532 ModestMsgViewWindowPrivate *priv;
3534 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (fidata->window);
3535 priv->fetching_images--;
3536 gtk_widget_queue_draw (fidata->msg_view);
3537 update_progress_hint (MODEST_MSG_VIEW_WINDOW (fidata->window));
3539 gdk_threads_leave ();
3541 g_object_unref (fidata->msg_view);
3542 g_object_unref (fidata->window);
3543 g_slice_free (FetchImageData, fidata);
3548 on_fetch_image_thread (gpointer userdata)
3550 FetchImageData *fidata = (FetchImageData *) userdata;
3551 TnyStreamCache *cache;
3552 TnyStream *cache_stream;
3554 cache = modest_runtime_get_images_cache ();
3556 tny_stream_cache_get_stream (cache,
3558 (TnyStreamCacheOpenStreamFetcher) fetch_image_open_stream,
3559 (gpointer) fidata->uri);
3560 g_free (fidata->cache_id);
3561 g_free (fidata->uri);
3563 if (cache_stream != NULL) {
3566 while (G_LIKELY (!tny_stream_is_eos (cache_stream))) {
3569 nb_read = tny_stream_read (cache_stream, buffer, sizeof (buffer));
3570 if (G_UNLIKELY (nb_read < 0)) {
3572 } else if (G_LIKELY (nb_read > 0)) {
3573 gssize nb_written = 0;
3575 while (G_UNLIKELY (nb_written < nb_read)) {
3578 len = tny_stream_write (fidata->output_stream, buffer + nb_written,
3579 nb_read - nb_written);
3580 if (G_UNLIKELY (len < 0))
3586 tny_stream_close (cache_stream);
3587 g_object_unref (cache_stream);
3590 tny_stream_close (fidata->output_stream);
3591 g_object_unref (fidata->output_stream);
3593 g_idle_add (on_fetch_image_idle_refresh_view, fidata);
3599 on_fetch_image (ModestMsgView *msgview,
3602 ModestMsgViewWindow *window)
3604 const gchar *current_account;
3605 ModestMsgViewWindowPrivate *priv;
3606 FetchImageData *fidata;
3608 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
3610 current_account = modest_window_get_active_account (MODEST_WINDOW (window));
3612 fidata = g_slice_new0 (FetchImageData);
3613 fidata->msg_view = g_object_ref (msgview);
3614 fidata->window = g_object_ref (window);
3615 fidata->uri = g_strdup (uri);
3616 fidata->cache_id = modest_images_cache_get_id (current_account, uri);
3617 fidata->output_stream = g_object_ref (stream);
3619 priv->fetching_images++;
3620 if (g_thread_create (on_fetch_image_thread, fidata, FALSE, NULL) == NULL) {
3621 g_object_unref (fidata->output_stream);
3622 g_free (fidata->cache_id);
3623 g_free (fidata->uri);
3624 g_object_unref (fidata->msg_view);
3625 g_slice_free (FetchImageData, fidata);
3626 tny_stream_close (stream);
3627 priv->fetching_images--;
3628 update_progress_hint (window);
3631 update_progress_hint (window);
3637 setup_menu (ModestMsgViewWindow *self)
3639 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW(self));
3641 /* Settings menu buttons */
3642 modest_hildon2_window_add_to_menu (MODEST_HILDON2_WINDOW (self), _("mcen_me_viewer_find"), NULL,
3643 APP_MENU_CALLBACK (modest_msg_view_window_show_find_toolbar),
3644 MODEST_DIMMING_CALLBACK (modest_ui_dimming_rules_on_find_in_msg));
3646 modest_hildon2_window_add_to_menu (MODEST_HILDON2_WINDOW (self),
3647 dngettext(GETTEXT_PACKAGE,
3648 "mcen_me_move_message",
3649 "mcen_me_move_messages",
3652 APP_MENU_CALLBACK (modest_ui_actions_on_move_to),
3653 MODEST_DIMMING_CALLBACK (modest_ui_dimming_rules_on_move_to));
3655 modest_hildon2_window_add_to_menu (MODEST_HILDON2_WINDOW (self), _("mcen_me_inbox_mark_as_read"), NULL,
3656 APP_MENU_CALLBACK (modest_ui_actions_on_mark_as_read),
3657 MODEST_DIMMING_CALLBACK (modest_ui_dimming_rules_on_mark_as_read_msg_in_view));
3659 modest_hildon2_window_add_to_menu (MODEST_HILDON2_WINDOW (self), _("mcen_me_inbox_mark_as_unread"), NULL,
3660 APP_MENU_CALLBACK (modest_ui_actions_on_mark_as_unread),
3661 MODEST_DIMMING_CALLBACK (modest_ui_dimming_rules_on_mark_as_unread_msg_in_view));
3663 modest_hildon2_window_add_to_menu (MODEST_HILDON2_WINDOW (self), _("mcen_me_viewer_save_attachments"), NULL,
3664 APP_MENU_CALLBACK (modest_ui_actions_save_attachments),
3665 MODEST_DIMMING_CALLBACK (modest_ui_dimming_rules_on_save_attachments));
3666 modest_hildon2_window_add_to_menu (MODEST_HILDON2_WINDOW (self), _("mcen_me_inbox_remove_attachments"), NULL,
3667 APP_MENU_CALLBACK (modest_ui_actions_remove_attachments),
3668 MODEST_DIMMING_CALLBACK (modest_ui_dimming_rules_on_remove_attachments));
3670 modest_hildon2_window_add_to_menu (MODEST_HILDON2_WINDOW (self), _("mcen_me_new_message"), "<Control>n",
3671 APP_MENU_CALLBACK (modest_ui_actions_on_new_msg),
3672 MODEST_DIMMING_CALLBACK (modest_ui_dimming_rules_on_new_msg));
3673 modest_hildon2_window_add_to_menu (MODEST_HILDON2_WINDOW (self), _("mcen_me_viewer_addtocontacts"), NULL,
3674 APP_MENU_CALLBACK (modest_ui_actions_add_to_contacts),
3675 MODEST_DIMMING_CALLBACK (modest_ui_dimming_rules_on_add_to_contacts));
3677 modest_hildon2_window_add_to_menu (MODEST_HILDON2_WINDOW (self), _("mcen_ti_message_properties"), NULL,
3678 APP_MENU_CALLBACK (modest_ui_actions_on_details),
3679 MODEST_DIMMING_CALLBACK (modest_ui_dimming_rules_on_details));
3683 modest_msg_view_window_add_to_contacts (ModestMsgViewWindow *self)
3685 ModestMsgViewWindowPrivate *priv;
3686 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
3687 GSList *recipients = NULL;
3690 msg = tny_msg_view_get_msg (TNY_MSG_VIEW (priv->msg_view));
3694 header = modest_msg_view_window_get_header (self);
3697 recipients = modest_tny_msg_header_get_all_recipients_list (header);
3698 g_object_unref (header);
3700 recipients = modest_tny_msg_get_all_recipients_list (msg);
3701 g_object_unref (msg);
3705 /* Offer the user to add recipients to the address book */
3706 modest_address_book_add_address_list_with_selector (recipients, (GtkWindow *) self);
3707 g_slist_foreach (recipients, (GFunc) g_free, NULL); g_slist_free (recipients);
3712 _modest_msg_view_window_map_event (GtkWidget *widget,
3716 ModestMsgViewWindow *self = (ModestMsgViewWindow *) userdata;
3718 update_progress_hint (self);
3724 modest_msg_view_window_fetch_images (ModestMsgViewWindow *self)
3726 ModestMsgViewWindowPrivate *priv;
3727 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
3729 modest_msg_view_request_fetch_images (MODEST_MSG_VIEW (priv->msg_view));
3733 modest_msg_view_window_has_blocked_external_images (ModestMsgViewWindow *self)
3735 ModestMsgViewWindowPrivate *priv;
3736 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
3738 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self), FALSE);
3740 return modest_msg_view_has_blocked_external_images (MODEST_MSG_VIEW (priv->msg_view));
3744 modest_msg_view_window_reload (ModestMsgViewWindow *self)
3746 ModestMsgViewWindowPrivate *priv;
3747 const gchar *msg_uid;
3748 TnyHeader *header = NULL;
3749 TnyFolder *folder = NULL;
3751 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self));
3753 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
3755 header = modest_msg_view_window_get_header (MODEST_MSG_VIEW_WINDOW (self));
3759 folder = tny_header_get_folder (header);
3760 g_object_unref (header);
3765 msg_uid = modest_msg_view_window_get_message_uid (self);
3767 GtkTreeRowReference *row_reference;
3769 if (priv->row_reference && gtk_tree_row_reference_valid (priv->row_reference)) {
3770 row_reference = priv->row_reference;
3772 row_reference = NULL;
3774 if (!message_reader (self, priv, NULL, msg_uid, folder, row_reference))
3775 g_warning ("Shouldn't happen, trying to reload a message failed");
3778 g_object_unref (folder);
3782 update_branding (ModestMsgViewWindow *self)
3784 const gchar *account;
3785 const gchar *mailbox;
3786 ModestAccountMgr *mgr;
3787 ModestProtocol *protocol = NULL;
3788 gchar *service_name = NULL;
3789 const GdkPixbuf *service_icon = NULL;
3790 ModestMsgViewWindowPrivate *priv;
3792 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
3794 account = modest_window_get_active_account (MODEST_WINDOW (self));
3795 mailbox = modest_window_get_active_mailbox (MODEST_WINDOW (self));
3797 mgr = modest_runtime_get_account_mgr ();
3799 if (modest_account_mgr_account_is_multimailbox (mgr, account, &protocol)) {
3800 if (MODEST_IS_ACCOUNT_PROTOCOL (protocol)) {
3801 service_name = modest_account_protocol_get_service_name (MODEST_ACCOUNT_PROTOCOL (protocol),
3803 service_icon = modest_account_protocol_get_service_icon (MODEST_ACCOUNT_PROTOCOL (protocol),
3804 account, mailbox, MODEST_ICON_SIZE_SMALL);
3808 modest_msg_view_set_branding (MODEST_MSG_VIEW (priv->msg_view), service_name, service_icon);
3809 g_free (service_name);
3813 sync_flags (ModestMsgViewWindow *self)
3815 TnyHeader *header = NULL;
3817 header = modest_msg_view_window_get_header (self);
3819 TnyMsg *msg = modest_msg_view_window_get_message (self);
3821 header = tny_msg_get_header (msg);
3822 g_object_unref (msg);
3827 TnyFolder *folder = tny_header_get_folder (header);
3830 ModestMailOperation *mail_op;
3832 /* Sync folder, we need this to save the seen flag */
3833 mail_op = modest_mail_operation_new (NULL);
3834 modest_mail_operation_queue_add (modest_runtime_get_mail_operation_queue (),
3836 modest_mail_operation_sync_folder (mail_op, folder, FALSE, NULL, NULL);
3837 g_object_unref (mail_op);
3838 g_object_unref (folder);
3840 g_object_unref (header);