1 /* Copyright (c) 2006, Nokia Corporation
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions are
8 * * Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 * * Redistributions in binary form must reproduce the above copyright
11 * notice, this list of conditions and the following disclaimer in the
12 * documentation and/or other materials provided with the distribution.
13 * * Neither the name of the Nokia Corporation nor the names of its
14 * contributors may be used to endorse or promote products derived from
15 * this software without specific prior written permission.
17 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
18 * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
19 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
20 * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
21 * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
22 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
23 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
24 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
25 * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
26 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
27 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29 #include <glib/gi18n.h>
31 #include <tny-account-store.h>
32 #include <tny-simple-list.h>
34 #include <tny-mime-part.h>
35 #include <tny-vfs-stream.h>
36 #include <tny-error.h>
37 #include "modest-marshal.h"
38 #include "modest-platform.h"
39 #include <modest-utils.h>
40 #include <modest-maemo-utils.h>
41 #include <modest-tny-msg.h>
42 #include <modest-msg-view-window.h>
43 #include <modest-main-window-ui.h>
44 #include "modest-msg-view-window-ui-dimming.h"
45 #include <modest-widget-memory.h>
46 #include <modest-progress-object.h>
47 #include <modest-runtime.h>
48 #include <modest-window-priv.h>
49 #include <modest-tny-folder.h>
50 #include <modest-text-utils.h>
51 #include <modest-account-mgr-helpers.h>
52 #include <hildon/hildon-pannable-area.h>
53 #include <hildon/hildon-picker-dialog.h>
54 #include <hildon/hildon-app-menu.h>
55 #include "modest-defs.h"
56 #include "modest-hildon-includes.h"
57 #include "modest-ui-dimming-manager.h"
58 #include <gdk/gdkkeysyms.h>
59 #include <modest-tny-account.h>
60 #include <modest-mime-part-view.h>
61 #include <modest-isearch-view.h>
62 #include <modest-tny-mime-part.h>
63 #include <modest-address-book.h>
66 #include <glib/gstdio.h>
67 #include <modest-debug.h>
68 #include <modest-header-window.h>
69 #include <modest-account-protocol.h>
70 #include <tny-camel-msg.h>
72 #define MYDOCS_ENV "MYDOCSDIR"
73 #define DOCS_FOLDER ".documents"
75 typedef struct _ModestMsgViewWindowPrivate ModestMsgViewWindowPrivate;
76 struct _ModestMsgViewWindowPrivate {
79 GtkWidget *main_scroll;
80 GtkWidget *find_toolbar;
83 /* Progress observers */
84 GSList *progress_widgets;
87 GtkWidget *prev_toolitem;
88 GtkWidget *next_toolitem;
89 gboolean progress_hint;
92 /* Optimized view enabled */
93 gboolean optimized_view;
95 /* Whether this was created via the *_new_for_search_result() function. */
96 gboolean is_search_result;
98 /* Whether the message is in outbox */
101 /* A reference to the @model of the header view
102 * to allow selecting previous/next messages,
103 * if the message is currently selected in the header view.
105 const gchar *header_folder_id;
106 GtkTreeModel *header_model;
107 GtkTreeRowReference *row_reference;
108 GtkTreeRowReference *next_row_reference;
110 gulong clipboard_change_handler;
111 gulong queue_change_handler;
112 gulong account_removed_handler;
113 gulong row_changed_handler;
114 gulong row_deleted_handler;
115 gulong row_inserted_handler;
116 gulong rows_reordered_handler;
119 GtkWidget *remove_attachment_banner;
122 TnyMimePart *other_body;
127 static void modest_msg_view_window_class_init (ModestMsgViewWindowClass *klass);
128 static void modest_msg_view_window_init (ModestMsgViewWindow *obj);
129 static void modest_header_view_observer_init (ModestHeaderViewObserverIface *iface_class);
130 static void modest_msg_view_window_finalize (GObject *obj);
131 static void modest_msg_view_window_show_find_toolbar (GtkWidget *obj, gpointer data);
132 static void modest_msg_view_window_find_toolbar_close (GtkWidget *widget,
133 ModestMsgViewWindow *obj);
134 static void modest_msg_view_window_find_toolbar_search (GtkWidget *widget,
135 ModestMsgViewWindow *obj);
136 static void modest_msg_view_window_toggle_find_toolbar (GtkWidget *obj,
138 static void modest_msg_view_window_disconnect_signals (ModestWindow *self);
140 static gdouble modest_msg_view_window_get_zoom (ModestWindow *window);
141 static void modest_msg_view_window_set_zoom (ModestWindow *window,
143 static gboolean modest_msg_view_window_zoom_minus (ModestWindow *window);
144 static gboolean modest_msg_view_window_zoom_plus (ModestWindow *window);
145 static gboolean modest_msg_view_window_key_event (GtkWidget *window,
148 static void modest_msg_view_window_update_priority (ModestMsgViewWindow *window);
150 static void modest_msg_view_window_show_toolbar (ModestWindow *window,
151 gboolean show_toolbar);
153 static void modest_msg_view_window_clipboard_owner_change (GtkClipboard *clipboard,
155 ModestMsgViewWindow *window);
157 static void modest_msg_view_window_on_row_changed (GtkTreeModel *header_model,
160 ModestMsgViewWindow *window);
162 static void modest_msg_view_window_on_row_deleted (GtkTreeModel *header_model,
164 ModestMsgViewWindow *window);
166 static void modest_msg_view_window_on_row_inserted (GtkTreeModel *header_model,
167 GtkTreePath *tree_path,
168 GtkTreeIter *tree_iter,
169 ModestMsgViewWindow *window);
171 static void modest_msg_view_window_on_row_reordered (GtkTreeModel *header_model,
175 ModestMsgViewWindow *window);
177 static void modest_msg_view_window_update_model_replaced (ModestHeaderViewObserver *window,
179 const gchar *tny_folder_id);
181 static void on_queue_changed (ModestMailOperationQueue *queue,
182 ModestMailOperation *mail_op,
183 ModestMailOperationQueueNotification type,
184 ModestMsgViewWindow *self);
186 static void on_account_removed (TnyAccountStore *account_store,
190 static void on_move_focus (GtkWidget *widget,
191 GtkDirectionType direction,
194 static void view_msg_cb (ModestMailOperation *mail_op,
201 static void set_progress_hint (ModestMsgViewWindow *self,
204 static void update_window_title (ModestMsgViewWindow *window);
206 static void init_window (ModestMsgViewWindow *obj);
208 static gboolean msg_is_visible (TnyHeader *header, gboolean check_outbox);
210 static void check_dimming_rules_after_change (ModestMsgViewWindow *window);
212 static gboolean on_fetch_image (ModestMsgView *msgview,
215 ModestMsgViewWindow *window);
217 static gboolean modest_msg_view_window_scroll_child (ModestMsgViewWindow *self,
218 GtkScrollType scroll_type,
221 static gboolean message_reader (ModestMsgViewWindow *window,
222 ModestMsgViewWindowPrivate *priv,
224 const gchar *msg_uid,
226 GtkTreeRowReference *row_reference);
228 static void setup_menu (ModestMsgViewWindow *self);
229 static gboolean _modest_msg_view_window_map_event (GtkWidget *widget,
232 static void update_branding (ModestMsgViewWindow *self);
233 static void sync_flags (ModestMsgViewWindow *self);
235 /* list my signals */
242 static const GtkToggleActionEntry msg_view_toggle_action_entries [] = {
243 { "FindInMessage", MODEST_TOOLBAR_ICON_FIND, N_("qgn_toolb_gene_find"), "<CTRL>F", NULL, G_CALLBACK (modest_msg_view_window_toggle_find_toolbar), FALSE },
246 #define MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE((o), \
247 MODEST_TYPE_MSG_VIEW_WINDOW, \
248 ModestMsgViewWindowPrivate))
250 static GtkWindowClass *parent_class = NULL;
252 /* uncomment the following if you have defined any signals */
253 static guint signals[LAST_SIGNAL] = {0};
256 modest_msg_view_window_get_type (void)
258 static GType my_type = 0;
260 static const GTypeInfo my_info = {
261 sizeof(ModestMsgViewWindowClass),
262 NULL, /* base init */
263 NULL, /* base finalize */
264 (GClassInitFunc) modest_msg_view_window_class_init,
265 NULL, /* class finalize */
266 NULL, /* class data */
267 sizeof(ModestMsgViewWindow),
269 (GInstanceInitFunc) modest_msg_view_window_init,
272 my_type = g_type_register_static (MODEST_TYPE_HILDON2_WINDOW,
273 "ModestMsgViewWindow",
276 static const GInterfaceInfo modest_header_view_observer_info =
278 (GInterfaceInitFunc) modest_header_view_observer_init,
279 NULL, /* interface_finalize */
280 NULL /* interface_data */
283 g_type_add_interface_static (my_type,
284 MODEST_TYPE_HEADER_VIEW_OBSERVER,
285 &modest_header_view_observer_info);
291 save_state (ModestWindow *self)
293 modest_widget_memory_save (modest_runtime_get_conf (),
295 MODEST_CONF_MSG_VIEW_WINDOW_KEY);
299 modest_msg_view_window_scroll_child (ModestMsgViewWindow *self,
300 GtkScrollType scroll_type,
304 ModestMsgViewWindowPrivate *priv;
307 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(self);
309 switch (scroll_type) {
310 case GTK_SCROLL_STEP_UP:
313 case GTK_SCROLL_STEP_DOWN:
316 case GTK_SCROLL_PAGE_UP:
319 case GTK_SCROLL_PAGE_DOWN:
322 case GTK_SCROLL_START:
333 modest_maemo_utils_scroll_pannable((HildonPannableArea *) priv->main_scroll, 0, step);
335 return (gboolean) step;
339 add_scroll_binding (GtkBindingSet *binding_set,
341 GtkScrollType scroll)
343 guint keypad_keyval = keyval - GDK_Left + GDK_KP_Left;
345 gtk_binding_entry_add_signal (binding_set, keyval, 0,
347 GTK_TYPE_SCROLL_TYPE, scroll,
348 G_TYPE_BOOLEAN, FALSE);
349 gtk_binding_entry_add_signal (binding_set, keypad_keyval, 0,
351 GTK_TYPE_SCROLL_TYPE, scroll,
352 G_TYPE_BOOLEAN, FALSE);
356 modest_msg_view_window_class_init (ModestMsgViewWindowClass *klass)
358 GObjectClass *gobject_class;
359 HildonWindowClass *hildon_window_class;
360 ModestWindowClass *modest_window_class;
361 GtkBindingSet *binding_set;
363 gobject_class = (GObjectClass*) klass;
364 hildon_window_class = (HildonWindowClass *) klass;
365 modest_window_class = (ModestWindowClass *) klass;
367 parent_class = g_type_class_peek_parent (klass);
368 gobject_class->finalize = modest_msg_view_window_finalize;
370 modest_window_class->set_zoom_func = modest_msg_view_window_set_zoom;
371 modest_window_class->get_zoom_func = modest_msg_view_window_get_zoom;
372 modest_window_class->zoom_plus_func = modest_msg_view_window_zoom_plus;
373 modest_window_class->zoom_minus_func = modest_msg_view_window_zoom_minus;
374 modest_window_class->show_toolbar_func = modest_msg_view_window_show_toolbar;
375 modest_window_class->disconnect_signals_func = modest_msg_view_window_disconnect_signals;
377 modest_window_class->save_state_func = save_state;
379 klass->scroll_child = modest_msg_view_window_scroll_child;
381 signals[MSG_CHANGED_SIGNAL] =
382 g_signal_new ("msg-changed",
383 G_TYPE_FROM_CLASS (gobject_class),
385 G_STRUCT_OFFSET (ModestMsgViewWindowClass, msg_changed),
387 modest_marshal_VOID__POINTER_POINTER,
388 G_TYPE_NONE, 2, G_TYPE_POINTER, G_TYPE_POINTER);
390 signals[SCROLL_CHILD_SIGNAL] =
391 g_signal_new ("scroll-child",
392 G_TYPE_FROM_CLASS (gobject_class),
393 G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
394 G_STRUCT_OFFSET (ModestMsgViewWindowClass, scroll_child),
396 modest_marshal_BOOLEAN__ENUM_BOOLEAN,
397 G_TYPE_BOOLEAN, 2, GTK_TYPE_SCROLL_TYPE, G_TYPE_BOOLEAN);
399 binding_set = gtk_binding_set_by_class (klass);
400 add_scroll_binding (binding_set, GDK_Up, GTK_SCROLL_STEP_UP);
401 add_scroll_binding (binding_set, GDK_Down, GTK_SCROLL_STEP_DOWN);
402 add_scroll_binding (binding_set, GDK_Page_Up, GTK_SCROLL_PAGE_UP);
403 add_scroll_binding (binding_set, GDK_Page_Down, GTK_SCROLL_PAGE_DOWN);
404 add_scroll_binding (binding_set, GDK_Home, GTK_SCROLL_START);
405 add_scroll_binding (binding_set, GDK_End, GTK_SCROLL_END);
407 g_type_class_add_private (gobject_class, sizeof(ModestMsgViewWindowPrivate));
411 static void modest_header_view_observer_init(
412 ModestHeaderViewObserverIface *iface_class)
414 iface_class->update_func = modest_msg_view_window_update_model_replaced;
418 modest_msg_view_window_init (ModestMsgViewWindow *obj)
420 ModestMsgViewWindowPrivate *priv;
421 ModestWindowPrivate *parent_priv = NULL;
422 GtkActionGroup *action_group = NULL;
423 GError *error = NULL;
425 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(obj);
426 parent_priv = MODEST_WINDOW_GET_PRIVATE(obj);
427 parent_priv->ui_manager = gtk_ui_manager_new();
429 action_group = gtk_action_group_new ("ModestMsgViewWindowActions");
430 gtk_action_group_set_translation_domain (action_group, GETTEXT_PACKAGE);
432 /* Add common actions */
433 gtk_action_group_add_actions (action_group,
434 modest_action_entries,
435 G_N_ELEMENTS (modest_action_entries),
437 gtk_action_group_add_toggle_actions (action_group,
438 msg_view_toggle_action_entries,
439 G_N_ELEMENTS (msg_view_toggle_action_entries),
442 gtk_ui_manager_insert_action_group (parent_priv->ui_manager, action_group, 0);
443 g_object_unref (action_group);
445 /* Load the UI definition */
446 gtk_ui_manager_add_ui_from_file (parent_priv->ui_manager, MODEST_UIDIR "modest-msg-view-window-ui.xml",
449 g_printerr ("modest: could not merge modest-msg-view-window-ui.xml: %s\n", error->message);
450 g_error_free (error);
455 /* Add accelerators */
456 gtk_window_add_accel_group (GTK_WINDOW (obj),
457 gtk_ui_manager_get_accel_group (parent_priv->ui_manager));
459 priv->is_search_result = FALSE;
460 priv->is_outbox = FALSE;
462 priv->msg_view = NULL;
463 priv->header_model = NULL;
464 priv->header_folder_id = NULL;
465 priv->clipboard_change_handler = 0;
466 priv->queue_change_handler = 0;
467 priv->account_removed_handler = 0;
468 priv->row_changed_handler = 0;
469 priv->row_deleted_handler = 0;
470 priv->row_inserted_handler = 0;
471 priv->rows_reordered_handler = 0;
472 priv->progress_hint = FALSE;
473 priv->fetching_images = 0;
475 priv->optimized_view = FALSE;
476 priv->purge_timeout = 0;
477 priv->remove_attachment_banner = NULL;
478 priv->msg_uid = NULL;
479 priv->other_body = NULL;
481 priv->sighandlers = NULL;
484 init_window (MODEST_MSG_VIEW_WINDOW(obj));
486 hildon_program_add_window (hildon_program_get_instance(),
492 update_progress_hint (ModestMsgViewWindow *self)
494 ModestMsgViewWindowPrivate *priv;
495 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(self);
497 if (GTK_WIDGET_VISIBLE (self)) {
498 hildon_gtk_window_set_progress_indicator (GTK_WINDOW (self),
499 (priv->progress_hint || (priv->fetching_images > 0))?1:0);
504 set_progress_hint (ModestMsgViewWindow *self,
507 ModestWindowPrivate *parent_priv;
508 ModestMsgViewWindowPrivate *priv;
510 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self));
512 parent_priv = MODEST_WINDOW_GET_PRIVATE(self);
513 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(self);
515 /* Sets current progress hint */
516 priv->progress_hint = enabled;
518 update_progress_hint (self);
524 init_window (ModestMsgViewWindow *obj)
526 GtkWidget *main_vbox;
527 ModestMsgViewWindowPrivate *priv;
529 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(obj);
531 priv->msg_view = GTK_WIDGET (tny_platform_factory_new_msg_view (modest_tny_platform_factory_get_instance ()));
532 modest_msg_view_set_shadow_type (MODEST_MSG_VIEW (priv->msg_view), GTK_SHADOW_NONE);
533 main_vbox = gtk_vbox_new (FALSE, 6);
534 priv->main_scroll = hildon_pannable_area_new ();
535 g_object_set (G_OBJECT (priv->main_scroll),
536 "mov-mode", HILDON_MOVEMENT_MODE_BOTH,
539 gtk_container_add (GTK_CONTAINER (priv->main_scroll), priv->msg_view);
540 gtk_box_pack_start (GTK_BOX(main_vbox), priv->main_scroll, TRUE, TRUE, 0);
541 gtk_container_add (GTK_CONTAINER(obj), main_vbox);
543 /* NULL-ize fields if the window is destroyed */
544 g_signal_connect (priv->msg_view, "destroy", G_CALLBACK (gtk_widget_destroyed), &(priv->msg_view));
546 gtk_widget_show_all (GTK_WIDGET(main_vbox));
550 modest_msg_view_window_disconnect_signals (ModestWindow *self)
552 ModestMsgViewWindowPrivate *priv;
553 GtkWidget *header_view = NULL;
554 GtkWindow *parent_window = NULL;
556 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
558 if (gtk_clipboard_get (GDK_SELECTION_PRIMARY) &&
559 g_signal_handler_is_connected (gtk_clipboard_get (GDK_SELECTION_PRIMARY),
560 priv->clipboard_change_handler))
561 g_signal_handler_disconnect (gtk_clipboard_get (GDK_SELECTION_PRIMARY),
562 priv->clipboard_change_handler);
564 if (g_signal_handler_is_connected (G_OBJECT (modest_runtime_get_mail_operation_queue ()),
565 priv->queue_change_handler))
566 g_signal_handler_disconnect (G_OBJECT (modest_runtime_get_mail_operation_queue ()),
567 priv->queue_change_handler);
569 if (g_signal_handler_is_connected (G_OBJECT (modest_runtime_get_account_store ()),
570 priv->account_removed_handler))
571 g_signal_handler_disconnect (G_OBJECT (modest_runtime_get_account_store ()),
572 priv->account_removed_handler);
574 if (priv->header_model) {
575 if (g_signal_handler_is_connected(G_OBJECT (priv->header_model),
576 priv->row_changed_handler))
577 g_signal_handler_disconnect(G_OBJECT (priv->header_model),
578 priv->row_changed_handler);
580 if (g_signal_handler_is_connected(G_OBJECT (priv->header_model),
581 priv->row_deleted_handler))
582 g_signal_handler_disconnect(G_OBJECT (priv->header_model),
583 priv->row_deleted_handler);
585 if (g_signal_handler_is_connected(G_OBJECT (priv->header_model),
586 priv->row_inserted_handler))
587 g_signal_handler_disconnect(G_OBJECT (priv->header_model),
588 priv->row_inserted_handler);
590 if (g_signal_handler_is_connected(G_OBJECT (priv->header_model),
591 priv->rows_reordered_handler))
592 g_signal_handler_disconnect(G_OBJECT (priv->header_model),
593 priv->rows_reordered_handler);
596 modest_signal_mgr_disconnect_all_and_destroy (priv->sighandlers);
597 priv->sighandlers = NULL;
599 parent_window = gtk_window_get_transient_for (GTK_WINDOW (self));
600 if (parent_window && MODEST_IS_HEADER_WINDOW (parent_window)) {
601 header_view = GTK_WIDGET (modest_header_window_get_header_view (MODEST_HEADER_WINDOW (parent_window)));
603 modest_header_view_remove_observer(MODEST_HEADER_VIEW (header_view),
604 MODEST_HEADER_VIEW_OBSERVER(self));
610 modest_msg_view_window_finalize (GObject *obj)
612 ModestMsgViewWindowPrivate *priv;
614 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (obj);
616 /* Sanity check: shouldn't be needed, the window mgr should
617 call this function before */
618 modest_msg_view_window_disconnect_signals (MODEST_WINDOW (obj));
620 if (priv->other_body != NULL) {
621 g_object_unref (priv->other_body);
622 priv->other_body = NULL;
625 if (priv->header_model != NULL) {
626 g_object_unref (priv->header_model);
627 priv->header_model = NULL;
630 if (priv->remove_attachment_banner) {
631 gtk_widget_destroy (priv->remove_attachment_banner);
632 g_object_unref (priv->remove_attachment_banner);
633 priv->remove_attachment_banner = NULL;
636 if (priv->purge_timeout > 0) {
637 g_source_remove (priv->purge_timeout);
638 priv->purge_timeout = 0;
641 if (priv->row_reference) {
642 gtk_tree_row_reference_free (priv->row_reference);
643 priv->row_reference = NULL;
646 if (priv->next_row_reference) {
647 gtk_tree_row_reference_free (priv->next_row_reference);
648 priv->next_row_reference = NULL;
652 g_free (priv->msg_uid);
653 priv->msg_uid = NULL;
656 G_OBJECT_CLASS(parent_class)->finalize (obj);
660 select_next_valid_row (GtkTreeModel *model,
661 GtkTreeRowReference **row_reference,
665 GtkTreeIter tmp_iter;
667 GtkTreePath *next = NULL;
668 gboolean retval = FALSE, finished;
670 g_return_val_if_fail (gtk_tree_row_reference_valid (*row_reference), FALSE);
672 path = gtk_tree_row_reference_get_path (*row_reference);
673 gtk_tree_model_get_iter (model, &tmp_iter, path);
674 gtk_tree_row_reference_free (*row_reference);
675 *row_reference = NULL;
679 TnyHeader *header = NULL;
681 if (gtk_tree_model_iter_next (model, &tmp_iter)) {
682 gtk_tree_model_get (model, &tmp_iter,
683 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
687 if (msg_is_visible (header, is_outbox)) {
688 next = gtk_tree_model_get_path (model, &tmp_iter);
689 *row_reference = gtk_tree_row_reference_new (model, next);
690 gtk_tree_path_free (next);
694 g_object_unref (header);
697 } else if (cycle && gtk_tree_model_get_iter_first (model, &tmp_iter)) {
698 next = gtk_tree_model_get_path (model, &tmp_iter);
700 /* Ensure that we are not selecting the same */
701 if (gtk_tree_path_compare (path, next) != 0) {
702 gtk_tree_model_get (model, &tmp_iter,
703 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
706 if (msg_is_visible (header, is_outbox)) {
707 *row_reference = gtk_tree_row_reference_new (model, next);
711 g_object_unref (header);
715 /* If we ended up in the same message
716 then there is no valid next
720 gtk_tree_path_free (next);
722 /* If there are no more messages and we don't
723 want to start again in the first one then
724 there is no valid next message */
730 gtk_tree_path_free (path);
735 /* TODO: This should be in _init(), with the parameters as properties. */
737 modest_msg_view_window_construct (ModestMsgViewWindow *self,
738 const gchar *modest_account_name,
739 const gchar *mailbox,
740 const gchar *msg_uid)
743 ModestMsgViewWindowPrivate *priv = NULL;
744 ModestWindowPrivate *parent_priv = NULL;
745 ModestDimmingRulesGroup *toolbar_rules_group = NULL;
746 ModestDimmingRulesGroup *clipboard_rules_group = NULL;
748 obj = G_OBJECT (self);
749 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(obj);
750 parent_priv = MODEST_WINDOW_GET_PRIVATE(obj);
752 priv->msg_uid = g_strdup (msg_uid);
755 parent_priv->menubar = NULL;
757 toolbar_rules_group = modest_dimming_rules_group_new (MODEST_DIMMING_RULES_TOOLBAR, TRUE);
758 clipboard_rules_group = modest_dimming_rules_group_new (MODEST_DIMMING_RULES_CLIPBOARD, FALSE);
761 /* Add common dimming rules */
762 modest_dimming_rules_group_add_rules (toolbar_rules_group,
763 modest_msg_view_toolbar_dimming_entries,
764 G_N_ELEMENTS (modest_msg_view_toolbar_dimming_entries),
765 MODEST_WINDOW (self));
766 modest_dimming_rules_group_add_rules (clipboard_rules_group,
767 modest_msg_view_clipboard_dimming_entries,
768 G_N_ELEMENTS (modest_msg_view_clipboard_dimming_entries),
769 MODEST_WINDOW (self));
771 /* Insert dimming rules group for this window */
772 modest_ui_dimming_manager_insert_rules_group (parent_priv->ui_dimming_manager, toolbar_rules_group);
773 modest_ui_dimming_manager_insert_rules_group (parent_priv->ui_dimming_manager, clipboard_rules_group);
774 g_object_unref (toolbar_rules_group);
775 g_object_unref (clipboard_rules_group);
777 /* g_signal_connect (G_OBJECT(obj), "delete-event", G_CALLBACK(on_delete_event), obj); */
779 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);
780 g_signal_connect (G_OBJECT(priv->msg_view), "activate_link",
781 G_CALLBACK (modest_ui_actions_on_msg_link_clicked), obj);
782 g_signal_connect (G_OBJECT(priv->msg_view), "link_hover",
783 G_CALLBACK (modest_ui_actions_on_msg_link_hover), obj);
784 g_signal_connect (G_OBJECT(priv->msg_view), "attachment_clicked",
785 G_CALLBACK (modest_ui_actions_on_msg_attachment_clicked), obj);
786 g_signal_connect (G_OBJECT(priv->msg_view), "recpt_activated",
787 G_CALLBACK (modest_ui_actions_on_msg_recpt_activated), obj);
788 g_signal_connect (G_OBJECT(priv->msg_view), "show_details",
789 G_CALLBACK (modest_ui_actions_on_details), obj);
790 g_signal_connect (G_OBJECT(priv->msg_view), "link_contextual",
791 G_CALLBACK (modest_ui_actions_on_msg_link_contextual), obj);
792 g_signal_connect (G_OBJECT(priv->msg_view), "limit_error",
793 G_CALLBACK (modest_ui_actions_on_limit_error), obj);
794 g_signal_connect (G_OBJECT (priv->msg_view), "fetch_image",
795 G_CALLBACK (on_fetch_image), obj);
797 g_signal_connect (G_OBJECT (obj), "key-release-event",
798 G_CALLBACK (modest_msg_view_window_key_event),
801 g_signal_connect (G_OBJECT (obj), "key-press-event",
802 G_CALLBACK (modest_msg_view_window_key_event),
805 g_signal_connect (G_OBJECT (obj), "move-focus",
806 G_CALLBACK (on_move_focus), obj);
808 g_signal_connect (G_OBJECT (obj), "map-event",
809 G_CALLBACK (_modest_msg_view_window_map_event),
812 /* Mail Operation Queue */
813 priv->queue_change_handler = g_signal_connect (G_OBJECT (modest_runtime_get_mail_operation_queue ()),
815 G_CALLBACK (on_queue_changed),
818 /* Account manager */
819 priv->account_removed_handler = g_signal_connect (G_OBJECT (modest_runtime_get_account_store ()),
821 G_CALLBACK(on_account_removed),
824 modest_window_set_active_account (MODEST_WINDOW(obj), modest_account_name);
825 modest_window_set_active_mailbox (MODEST_WINDOW(obj), mailbox);
827 /* First add out toolbar ... */
828 modest_msg_view_window_show_toolbar (MODEST_WINDOW (obj), TRUE);
830 /* ... and later the find toolbar. This way find toolbar will
831 be shown over the other */
832 priv->find_toolbar = hildon_find_toolbar_new (NULL);
833 hildon_window_add_toolbar (HILDON_WINDOW (obj), GTK_TOOLBAR (priv->find_toolbar));
834 gtk_widget_set_no_show_all (priv->find_toolbar, TRUE);
835 g_signal_connect (G_OBJECT (priv->find_toolbar), "close",
836 G_CALLBACK (modest_msg_view_window_find_toolbar_close), obj);
837 g_signal_connect (G_OBJECT (priv->find_toolbar), "search",
838 G_CALLBACK (modest_msg_view_window_find_toolbar_search), obj);
839 priv->last_search = NULL;
841 /* Init the clipboard actions dim status */
842 modest_msg_view_grab_focus(MODEST_MSG_VIEW (priv->msg_view));
844 update_window_title (MODEST_MSG_VIEW_WINDOW (obj));
849 /* FIXME: parameter checks */
851 modest_msg_view_window_new_with_header_model (TnyMsg *msg,
852 const gchar *modest_account_name,
853 const gchar *mailbox,
854 const gchar *msg_uid,
856 GtkTreeRowReference *row_reference)
858 ModestMsgViewWindow *window = NULL;
859 ModestMsgViewWindowPrivate *priv = NULL;
860 TnyFolder *header_folder = NULL;
861 ModestHeaderView *header_view = NULL;
862 ModestWindowMgr *mgr = NULL;
865 modest_tny_mime_part_to_string (TNY_MIME_PART (msg), 0);
868 mgr = modest_runtime_get_window_mgr ();
869 window = MODEST_MSG_VIEW_WINDOW (modest_window_mgr_get_msg_view_window (mgr));
870 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), NULL);
872 modest_msg_view_window_construct (window, modest_account_name, mailbox, msg_uid);
874 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
876 /* Remember the message list's TreeModel so we can detect changes
877 * and change the list selection when necessary: */
878 header_folder = modest_header_view_get_folder (header_view);
880 priv->is_outbox = (modest_tny_folder_guess_folder_type (header_folder) ==
881 TNY_FOLDER_TYPE_OUTBOX);
882 priv->header_folder_id = tny_folder_get_id (header_folder);
883 g_object_unref(header_folder);
886 /* Setup row references and connect signals */
887 priv->header_model = g_object_ref (model);
889 if (row_reference && gtk_tree_row_reference_valid (row_reference)) {
890 priv->row_reference = gtk_tree_row_reference_copy (row_reference);
891 priv->next_row_reference = gtk_tree_row_reference_copy (row_reference);
892 select_next_valid_row (model, &(priv->next_row_reference), TRUE, priv->is_outbox);
894 priv->row_reference = NULL;
895 priv->next_row_reference = NULL;
898 /* Connect signals */
899 priv->row_changed_handler =
900 g_signal_connect (GTK_TREE_MODEL(model), "row-changed",
901 G_CALLBACK(modest_msg_view_window_on_row_changed),
903 priv->row_deleted_handler =
904 g_signal_connect (GTK_TREE_MODEL(model), "row-deleted",
905 G_CALLBACK(modest_msg_view_window_on_row_deleted),
907 priv->row_inserted_handler =
908 g_signal_connect (GTK_TREE_MODEL(model), "row-inserted",
909 G_CALLBACK(modest_msg_view_window_on_row_inserted),
911 priv->rows_reordered_handler =
912 g_signal_connect(GTK_TREE_MODEL(model), "rows-reordered",
913 G_CALLBACK(modest_msg_view_window_on_row_reordered),
916 if (header_view != NULL){
917 modest_header_view_add_observer(header_view,
918 MODEST_HEADER_VIEW_OBSERVER(window));
921 tny_msg_view_set_msg (TNY_MSG_VIEW (priv->msg_view), msg);
922 update_window_title (MODEST_MSG_VIEW_WINDOW (window));
923 update_branding (MODEST_MSG_VIEW_WINDOW (window));
925 /* gtk_widget_show_all (GTK_WIDGET (window)); */
926 modest_msg_view_window_update_priority (window);
927 /* Check dimming rules */
928 modest_ui_actions_check_toolbar_dimming_rules (MODEST_WINDOW (window));
929 modest_ui_actions_check_menu_dimming_rules (MODEST_WINDOW (window));
930 modest_window_check_dimming_rules_group (MODEST_WINDOW (window), MODEST_DIMMING_RULES_CLIPBOARD);
932 return MODEST_WINDOW(window);
936 modest_msg_view_window_new_from_uid (const gchar *modest_account_name,
937 const gchar *mailbox,
938 const gchar *msg_uid)
940 ModestMsgViewWindow *window = NULL;
941 ModestMsgViewWindowPrivate *priv = NULL;
942 ModestWindowMgr *mgr = NULL;
944 TnyAccount *account = NULL;
946 mgr = modest_runtime_get_window_mgr ();
947 window = MODEST_MSG_VIEW_WINDOW (modest_window_mgr_get_msg_view_window (mgr));
948 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), NULL);
950 modest_msg_view_window_construct (window, modest_account_name, mailbox, msg_uid);
952 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
954 is_merge = g_str_has_prefix (msg_uid, "merge:");
956 /* Get the account */
958 account = tny_account_store_find_account (TNY_ACCOUNT_STORE (modest_runtime_get_account_store ()),
961 if (is_merge || account) {
962 TnyFolder *folder = NULL;
964 /* Try to get the message, if it's already downloaded
965 we don't need to connect */
967 folder = modest_tny_folder_store_find_folder_from_uri (TNY_FOLDER_STORE (account), msg_uid);
969 ModestTnyAccountStore *account_store;
970 ModestTnyLocalFoldersAccount *local_folders_account;
972 account_store = modest_runtime_get_account_store ();
973 local_folders_account = MODEST_TNY_LOCAL_FOLDERS_ACCOUNT (
974 modest_tny_account_store_get_local_folders_account (account_store));
975 folder = modest_tny_local_folders_account_get_merged_outbox (local_folders_account);
976 g_object_unref (local_folders_account);
980 gboolean device_online;
982 device = modest_runtime_get_device();
983 device_online = tny_device_is_online (device);
985 message_reader (window, priv, NULL, msg_uid, folder, NULL);
987 TnyMsg *msg = tny_folder_find_msg (folder, msg_uid, NULL);
989 tny_msg_view_set_msg (TNY_MSG_VIEW (priv->msg_view), msg);
990 update_window_title (MODEST_MSG_VIEW_WINDOW (window));
991 update_branding (MODEST_MSG_VIEW_WINDOW (window));
992 g_object_unref (msg);
993 /* Sync flags to server */
994 sync_flags (MODEST_MSG_VIEW_WINDOW (window));
996 message_reader (window, priv, NULL, msg_uid, folder, NULL);
999 g_object_unref (folder);
1004 /* Check dimming rules */
1005 modest_ui_actions_check_toolbar_dimming_rules (MODEST_WINDOW (window));
1006 modest_ui_actions_check_menu_dimming_rules (MODEST_WINDOW (window));
1007 modest_window_check_dimming_rules_group (MODEST_WINDOW (window), MODEST_DIMMING_RULES_CLIPBOARD);
1009 return MODEST_WINDOW(window);
1013 modest_msg_view_window_new_from_header_view (ModestHeaderView *header_view,
1014 const gchar *modest_account_name,
1015 const gchar *mailbox,
1016 const gchar *msg_uid,
1017 GtkTreeRowReference *row_reference)
1019 ModestMsgViewWindow *window = NULL;
1020 ModestMsgViewWindowPrivate *priv = NULL;
1021 TnyFolder *header_folder = NULL;
1022 ModestWindowMgr *mgr = NULL;
1026 mgr = modest_runtime_get_window_mgr ();
1027 window = MODEST_MSG_VIEW_WINDOW (modest_window_mgr_get_msg_view_window (mgr));
1028 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), NULL);
1030 modest_msg_view_window_construct (window, modest_account_name, mailbox, msg_uid);
1032 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1034 /* Remember the message list's TreeModel so we can detect changes
1035 * and change the list selection when necessary: */
1037 if (header_view != NULL){
1038 header_folder = modest_header_view_get_folder(header_view);
1039 /* This could happen if the header folder was
1040 unseleted before opening this msg window (for
1041 example if the user selects an account in the
1042 folder view of the main window */
1043 if (header_folder) {
1044 priv->is_outbox = (modest_tny_folder_guess_folder_type (header_folder) ==
1045 TNY_FOLDER_TYPE_OUTBOX);
1046 priv->header_folder_id = tny_folder_get_id(header_folder);
1047 g_object_unref(header_folder);
1051 /* Setup row references and connect signals */
1052 priv->header_model = gtk_tree_view_get_model (GTK_TREE_VIEW (header_view));
1053 g_object_ref (priv->header_model);
1055 if (row_reference && gtk_tree_row_reference_valid (row_reference)) {
1056 priv->row_reference = gtk_tree_row_reference_copy (row_reference);
1057 priv->next_row_reference = gtk_tree_row_reference_copy (row_reference);
1058 select_next_valid_row (priv->header_model, &(priv->next_row_reference), TRUE, priv->is_outbox);
1060 priv->row_reference = NULL;
1061 priv->next_row_reference = NULL;
1064 /* Connect signals */
1065 priv->row_changed_handler =
1066 g_signal_connect (GTK_TREE_MODEL(priv->header_model), "row-changed",
1067 G_CALLBACK(modest_msg_view_window_on_row_changed),
1069 priv->row_deleted_handler =
1070 g_signal_connect (GTK_TREE_MODEL(priv->header_model), "row-deleted",
1071 G_CALLBACK(modest_msg_view_window_on_row_deleted),
1073 priv->row_inserted_handler =
1074 g_signal_connect (GTK_TREE_MODEL(priv->header_model), "row-inserted",
1075 G_CALLBACK(modest_msg_view_window_on_row_inserted),
1077 priv->rows_reordered_handler =
1078 g_signal_connect(GTK_TREE_MODEL(priv->header_model), "rows-reordered",
1079 G_CALLBACK(modest_msg_view_window_on_row_reordered),
1082 if (header_view != NULL){
1083 modest_header_view_add_observer(header_view,
1084 MODEST_HEADER_VIEW_OBSERVER(window));
1087 tny_msg_view_set_msg (TNY_MSG_VIEW (priv->msg_view), NULL);
1088 update_branding (MODEST_MSG_VIEW_WINDOW (window));
1090 if (priv->row_reference) {
1091 path = gtk_tree_row_reference_get_path (priv->row_reference);
1092 if (gtk_tree_model_get_iter (priv->header_model, &iter, path)) {
1094 gtk_tree_model_get (priv->header_model, &iter,
1095 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
1097 message_reader (window, priv, header, NULL, NULL, priv->row_reference);
1098 g_object_unref (header);
1100 gtk_tree_path_free (path);
1102 /* Check dimming rules */
1103 modest_ui_actions_check_toolbar_dimming_rules (MODEST_WINDOW (window));
1104 modest_ui_actions_check_menu_dimming_rules (MODEST_WINDOW (window));
1105 modest_window_check_dimming_rules_group (MODEST_WINDOW (window), MODEST_DIMMING_RULES_CLIPBOARD);
1107 return MODEST_WINDOW(window);
1111 modest_msg_view_window_new_for_search_result (TnyMsg *msg,
1112 const gchar *modest_account_name,
1113 const gchar *mailbox,
1114 const gchar *msg_uid)
1116 ModestMsgViewWindow *window = NULL;
1117 ModestMsgViewWindowPrivate *priv = NULL;
1118 ModestWindowMgr *mgr = NULL;
1120 mgr = modest_runtime_get_window_mgr ();
1121 window = MODEST_MSG_VIEW_WINDOW (modest_window_mgr_get_msg_view_window (mgr));
1122 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), NULL);
1123 modest_msg_view_window_construct (window, modest_account_name, mailbox, msg_uid);
1125 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1127 /* Remember that this is a search result,
1128 * so we can disable some UI appropriately: */
1129 priv->is_search_result = TRUE;
1131 tny_msg_view_set_msg (TNY_MSG_VIEW (priv->msg_view), msg);
1132 update_branding (MODEST_MSG_VIEW_WINDOW (window));
1134 update_window_title (window);
1135 /* gtk_widget_show_all (GTK_WIDGET (window));*/
1136 modest_msg_view_window_update_priority (window);
1138 /* Check dimming rules */
1139 modest_ui_actions_check_toolbar_dimming_rules (MODEST_WINDOW (window));
1140 modest_ui_actions_check_menu_dimming_rules (MODEST_WINDOW (window));
1141 modest_window_check_dimming_rules_group (MODEST_WINDOW (window), MODEST_DIMMING_RULES_CLIPBOARD);
1143 return MODEST_WINDOW(window);
1147 modest_msg_view_window_is_other_body (ModestMsgViewWindow *self)
1149 ModestMsgViewWindowPrivate *priv = NULL;
1151 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self), FALSE);
1152 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
1154 return (priv->other_body != NULL);
1158 modest_msg_view_window_new_with_other_body (TnyMsg *msg,
1159 TnyMimePart *other_body,
1160 const gchar *modest_account_name,
1161 const gchar *mailbox,
1162 const gchar *msg_uid)
1164 GObject *obj = NULL;
1165 ModestMsgViewWindowPrivate *priv;
1166 ModestWindowMgr *mgr = NULL;
1168 g_return_val_if_fail (msg, NULL);
1169 mgr = modest_runtime_get_window_mgr ();
1170 obj = G_OBJECT (modest_window_mgr_get_msg_view_window (mgr));
1171 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (obj);
1172 modest_msg_view_window_construct (MODEST_MSG_VIEW_WINDOW (obj),
1173 modest_account_name, mailbox, msg_uid);
1176 priv->other_body = g_object_ref (other_body);
1177 modest_msg_view_set_msg_with_other_body (MODEST_MSG_VIEW (priv->msg_view), msg, other_body);
1179 tny_msg_view_set_msg (TNY_MSG_VIEW (priv->msg_view), msg);
1181 update_window_title (MODEST_MSG_VIEW_WINDOW (obj));
1182 update_branding (MODEST_MSG_VIEW_WINDOW (obj));
1184 /* gtk_widget_show_all (GTK_WIDGET (obj)); */
1186 /* Check dimming rules */
1187 modest_ui_actions_check_toolbar_dimming_rules (MODEST_WINDOW (obj));
1188 modest_ui_actions_check_menu_dimming_rules (MODEST_WINDOW (obj));
1189 modest_window_check_dimming_rules_group (MODEST_WINDOW (obj), MODEST_DIMMING_RULES_CLIPBOARD);
1191 return MODEST_WINDOW(obj);
1195 modest_msg_view_window_new_for_attachment (TnyMsg *msg,
1196 const gchar *modest_account_name,
1197 const gchar *mailbox,
1198 const gchar *msg_uid)
1200 return modest_msg_view_window_new_with_other_body (msg, NULL, modest_account_name, mailbox, msg_uid);
1204 modest_msg_view_window_on_row_changed (GtkTreeModel *header_model,
1207 ModestMsgViewWindow *window)
1209 check_dimming_rules_after_change (window);
1213 modest_msg_view_window_on_row_deleted(GtkTreeModel *header_model,
1215 ModestMsgViewWindow *window)
1217 check_dimming_rules_after_change (window);
1219 /* The window could have dissapeared */
1222 check_dimming_rules_after_change (ModestMsgViewWindow *window)
1224 modest_ui_actions_check_menu_dimming_rules (MODEST_WINDOW (window));
1225 modest_ui_actions_check_toolbar_dimming_rules (MODEST_WINDOW (window));
1229 /* On insertions we check if the folder still has the message we are
1230 * showing or do not. If do not, we do nothing. Which means we are still
1231 * not attached to any header folder and thus next/prev buttons are
1232 * still dimmed. Once the message that is shown by msg-view is found, the
1233 * new model of header-view will be attached and the references will be set.
1234 * On each further insertions dimming rules will be checked. However
1235 * this requires extra CPU time at least works.
1236 * (An message might be deleted from TnyFolder and thus will not be
1237 * inserted into the model again for example if it is removed by the
1238 * imap server and the header view is refreshed.)
1241 modest_msg_view_window_on_row_inserted (GtkTreeModel *model,
1242 GtkTreePath *tree_path,
1243 GtkTreeIter *tree_iter,
1244 ModestMsgViewWindow *window)
1246 ModestMsgViewWindowPrivate *priv = NULL;
1247 TnyHeader *header = NULL;
1249 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window));
1250 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1252 g_assert (model == priv->header_model);
1254 /* Check if the newly inserted message is the same we are actually
1255 * showing. IF not, we should remain detached from the header model
1256 * and thus prev and next toolbar buttons should remain dimmed. */
1257 gtk_tree_model_get (model, tree_iter,
1258 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
1261 if (TNY_IS_HEADER (header)) {
1264 uid = modest_tny_folder_get_header_unique_id (header);
1265 if (!g_str_equal(priv->msg_uid, uid)) {
1266 check_dimming_rules_after_change (window);
1268 g_object_unref (G_OBJECT(header));
1272 g_object_unref(G_OBJECT(header));
1275 if (priv->row_reference) {
1276 gtk_tree_row_reference_free (priv->row_reference);
1279 /* Setup row_reference for the actual msg. */
1280 priv->row_reference = gtk_tree_row_reference_new (priv->header_model, tree_path);
1281 if (priv->row_reference == NULL) {
1282 g_warning("%s: No reference for msg header item.", __FUNCTION__);
1286 /* Now set up next_row_reference. */
1287 if (priv->next_row_reference) {
1288 gtk_tree_row_reference_free (priv->next_row_reference);
1291 priv->next_row_reference = gtk_tree_row_reference_copy (priv->row_reference);
1292 select_next_valid_row (priv->header_model,
1293 &(priv->next_row_reference), FALSE, priv->is_outbox);
1295 /* Connect the remaining callbacks to become able to detect
1296 * changes in header-view. */
1297 priv->row_changed_handler =
1298 g_signal_connect (priv->header_model, "row-changed",
1299 G_CALLBACK (modest_msg_view_window_on_row_changed),
1301 priv->row_deleted_handler =
1302 g_signal_connect (priv->header_model, "row-deleted",
1303 G_CALLBACK (modest_msg_view_window_on_row_deleted),
1305 priv->rows_reordered_handler =
1306 g_signal_connect (priv->header_model, "rows-reordered",
1307 G_CALLBACK (modest_msg_view_window_on_row_reordered),
1310 check_dimming_rules_after_change (window);
1314 modest_msg_view_window_on_row_reordered (GtkTreeModel *header_model,
1318 ModestMsgViewWindow *window)
1320 ModestMsgViewWindowPrivate *priv = NULL;
1321 gboolean already_changed = FALSE;
1323 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(window);
1325 /* If the current row was reordered select the proper next
1326 valid row. The same if the next row reference changes */
1327 if (!priv->row_reference ||
1328 !gtk_tree_row_reference_valid (priv->row_reference))
1331 if (priv->next_row_reference &&
1332 gtk_tree_row_reference_valid (priv->next_row_reference)) {
1333 GtkTreePath *cur, *next;
1334 /* Check that the order is still the correct one */
1335 cur = gtk_tree_row_reference_get_path (priv->row_reference);
1336 next = gtk_tree_row_reference_get_path (priv->next_row_reference);
1337 gtk_tree_path_next (cur);
1338 if (gtk_tree_path_compare (cur, next) != 0) {
1339 gtk_tree_row_reference_free (priv->next_row_reference);
1340 priv->next_row_reference = gtk_tree_row_reference_copy (priv->row_reference);
1341 select_next_valid_row (header_model, &(priv->next_row_reference), FALSE, priv->is_outbox);
1342 already_changed = TRUE;
1344 gtk_tree_path_free (cur);
1345 gtk_tree_path_free (next);
1347 if (priv->next_row_reference)
1348 gtk_tree_row_reference_free (priv->next_row_reference);
1349 /* Update next row reference */
1350 priv->next_row_reference = gtk_tree_row_reference_copy (priv->row_reference);
1351 select_next_valid_row (header_model, &(priv->next_row_reference), FALSE, priv->is_outbox);
1352 already_changed = TRUE;
1355 check_dimming_rules_after_change (window);
1358 /* The modest_msg_view_window_update_model_replaced implements update
1359 * function for ModestHeaderViewObserver. Checks whether the TnyFolder
1360 * actually belongs to the header-view is the same as the TnyFolder of
1361 * the message of msg-view or not. If they are different, there is
1362 * nothing to do. If they are the same, then the model has replaced and
1363 * the reference in msg-view shall be replaced from the old model to
1364 * the new model. In this case the view will be detached from it's
1365 * header folder. From this point the next/prev buttons are dimmed.
1368 modest_msg_view_window_update_model_replaced (ModestHeaderViewObserver *observer,
1369 GtkTreeModel *model,
1370 const gchar *tny_folder_id)
1372 ModestMsgViewWindowPrivate *priv = NULL;
1373 ModestMsgViewWindow *window = NULL;
1375 g_assert(MODEST_IS_HEADER_VIEW_OBSERVER(observer));
1376 g_assert(MODEST_IS_MSG_VIEW_WINDOW(observer));
1378 window = MODEST_MSG_VIEW_WINDOW(observer);
1379 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(window);
1381 /* If there is an other folder in the header-view then we do
1382 * not care about it's model (msg list). Else if the
1383 * header-view shows the folder the msg shown by us is in, we
1384 * shall replace our model reference and make some check. */
1385 if(model == NULL || tny_folder_id == NULL ||
1386 (priv->header_folder_id && !g_str_equal(tny_folder_id, priv->header_folder_id)))
1389 /* Model is changed(replaced), so we should forget the old
1390 * one. Because there might be other references and there
1391 * might be some change on the model even if we unreferenced
1392 * it, we need to disconnect our signals here. */
1393 if (priv->header_model) {
1394 if (g_signal_handler_is_connected(G_OBJECT (priv->header_model),
1395 priv->row_changed_handler))
1396 g_signal_handler_disconnect(G_OBJECT (priv->header_model),
1397 priv->row_changed_handler);
1398 if (g_signal_handler_is_connected(G_OBJECT (priv->header_model),
1399 priv->row_deleted_handler))
1400 g_signal_handler_disconnect(G_OBJECT (priv->header_model),
1401 priv->row_deleted_handler);
1402 if (g_signal_handler_is_connected(G_OBJECT (priv->header_model),
1403 priv->row_inserted_handler))
1404 g_signal_handler_disconnect(G_OBJECT (priv->header_model),
1405 priv->row_inserted_handler);
1406 if (g_signal_handler_is_connected(G_OBJECT (priv->header_model),
1407 priv->rows_reordered_handler))
1408 g_signal_handler_disconnect(G_OBJECT (priv->header_model),
1409 priv->rows_reordered_handler);
1412 if (priv->row_reference)
1413 gtk_tree_row_reference_free (priv->row_reference);
1414 if (priv->next_row_reference)
1415 gtk_tree_row_reference_free (priv->next_row_reference);
1416 g_object_unref(priv->header_model);
1419 priv->row_changed_handler = 0;
1420 priv->row_deleted_handler = 0;
1421 priv->row_inserted_handler = 0;
1422 priv->rows_reordered_handler = 0;
1423 priv->next_row_reference = NULL;
1424 priv->row_reference = NULL;
1425 priv->header_model = NULL;
1428 priv->header_model = g_object_ref (model);
1430 /* Also we must connect to the new model for row insertions.
1431 * Only for insertions now. We will need other ones only after
1432 * the msg is show by msg-view is added to the new model. */
1433 priv->row_inserted_handler =
1434 g_signal_connect (priv->header_model, "row-inserted",
1435 G_CALLBACK(modest_msg_view_window_on_row_inserted),
1438 modest_ui_actions_check_menu_dimming_rules(MODEST_WINDOW(window));
1439 modest_ui_actions_check_toolbar_dimming_rules(MODEST_WINDOW(window));
1443 modest_msg_view_window_toolbar_on_transfer_mode (ModestMsgViewWindow *self)
1445 ModestMsgViewWindowPrivate *priv= NULL;
1447 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self), FALSE);
1448 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
1450 return priv->progress_hint;
1454 modest_msg_view_window_get_header (ModestMsgViewWindow *self)
1456 ModestMsgViewWindowPrivate *priv= NULL;
1458 TnyHeader *header = NULL;
1459 GtkTreePath *path = NULL;
1462 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self), NULL);
1463 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
1465 /* If the message was not obtained from a treemodel,
1466 * for instance if it was opened directly by the search UI:
1468 if (priv->header_model == NULL ||
1469 priv->row_reference == NULL ||
1470 !gtk_tree_row_reference_valid (priv->row_reference)) {
1471 msg = modest_msg_view_window_get_message (self);
1473 header = tny_msg_get_header (msg);
1474 g_object_unref (msg);
1479 /* Get iter of the currently selected message in the header view: */
1480 path = gtk_tree_row_reference_get_path (priv->row_reference);
1481 g_return_val_if_fail (path != NULL, NULL);
1482 gtk_tree_model_get_iter (priv->header_model,
1486 /* Get current message header */
1487 gtk_tree_model_get (priv->header_model, &iter,
1488 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
1491 gtk_tree_path_free (path);
1496 modest_msg_view_window_get_message (ModestMsgViewWindow *self)
1498 ModestMsgViewWindowPrivate *priv;
1500 g_return_val_if_fail (self, NULL);
1502 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(self);
1504 return tny_msg_view_get_msg (TNY_MSG_VIEW (priv->msg_view));
1508 modest_msg_view_window_get_message_uid (ModestMsgViewWindow *self)
1510 ModestMsgViewWindowPrivate *priv;
1512 g_return_val_if_fail (self, NULL);
1514 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
1516 return (const gchar*) priv->msg_uid;
1519 /* Used for the Ctrl+F accelerator */
1521 modest_msg_view_window_toggle_find_toolbar (GtkWidget *obj,
1524 ModestMsgViewWindow *window = MODEST_MSG_VIEW_WINDOW (data);
1525 ModestMsgViewWindowPrivate *priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1527 if (GTK_WIDGET_VISIBLE (priv->find_toolbar)) {
1528 modest_msg_view_window_find_toolbar_close (obj, data);
1530 modest_msg_view_window_show_find_toolbar (obj, data);
1534 /* Handler for menu option */
1536 modest_msg_view_window_show_find_toolbar (GtkWidget *obj,
1539 ModestMsgViewWindow *window = MODEST_MSG_VIEW_WINDOW (data);
1540 ModestMsgViewWindowPrivate *priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1542 gtk_widget_show (priv->find_toolbar);
1543 hildon_find_toolbar_highlight_entry (HILDON_FIND_TOOLBAR (priv->find_toolbar), TRUE);
1546 /* Handler for click on the "X" close button in find toolbar */
1548 modest_msg_view_window_find_toolbar_close (GtkWidget *widget,
1549 ModestMsgViewWindow *obj)
1551 ModestMsgViewWindowPrivate *priv;
1553 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (obj);
1556 gtk_widget_hide (priv->find_toolbar);
1557 modest_msg_view_grab_focus (MODEST_MSG_VIEW (priv->msg_view));
1561 modest_msg_view_window_find_toolbar_search (GtkWidget *widget,
1562 ModestMsgViewWindow *obj)
1564 gchar *current_search;
1565 ModestMsgViewWindowPrivate *priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (obj);
1567 if (modest_mime_part_view_is_empty (MODEST_MIME_PART_VIEW (priv->msg_view))) {
1568 hildon_banner_show_information (NULL, NULL, _("mail_ib_nothing_to_find"));
1572 g_object_get (G_OBJECT (widget), "prefix", ¤t_search, NULL);
1574 if ((current_search == NULL) || (strcmp (current_search, "") == 0)) {
1575 g_free (current_search);
1576 hildon_banner_show_information (NULL, NULL, _CS("ecdg_ib_find_rep_enter_text"));
1580 if ((priv->last_search == NULL) || (strcmp (priv->last_search, current_search) != 0)) {
1582 g_free (priv->last_search);
1583 priv->last_search = g_strdup (current_search);
1584 result = modest_isearch_view_search (MODEST_ISEARCH_VIEW (priv->msg_view),
1587 hildon_banner_show_information (NULL, NULL,
1588 _HL("ckct_ib_find_no_matches"));
1589 g_free (priv->last_search);
1590 priv->last_search = NULL;
1592 hildon_find_toolbar_highlight_entry (HILDON_FIND_TOOLBAR (priv->find_toolbar), TRUE);
1595 if (!modest_isearch_view_search_next (MODEST_ISEARCH_VIEW (priv->msg_view))) {
1596 hildon_banner_show_information (NULL, NULL,
1597 _HL("ckct_ib_find_search_complete"));
1598 g_free (priv->last_search);
1599 priv->last_search = NULL;
1601 hildon_find_toolbar_highlight_entry (HILDON_FIND_TOOLBAR (priv->find_toolbar), TRUE);
1605 g_free (current_search);
1610 modest_msg_view_window_set_zoom (ModestWindow *window,
1613 ModestMsgViewWindowPrivate *priv;
1614 ModestWindowPrivate *parent_priv;
1616 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window));
1618 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1619 parent_priv = MODEST_WINDOW_GET_PRIVATE (window);
1620 modest_zoomable_set_zoom (MODEST_ZOOMABLE (priv->msg_view), zoom);
1625 modest_msg_view_window_get_zoom (ModestWindow *window)
1627 ModestMsgViewWindowPrivate *priv;
1629 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), 1.0);
1631 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1632 return modest_zoomable_get_zoom (MODEST_ZOOMABLE (priv->msg_view));
1636 modest_msg_view_window_zoom_plus (ModestWindow *window)
1639 ModestMsgViewWindowPrivate *priv;
1643 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), 1.0);
1644 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1646 zoom_level = modest_zoomable_get_zoom (MODEST_ZOOMABLE (priv->msg_view));
1648 if (zoom_level >= 2.0) {
1649 hildon_banner_show_information (NULL, NULL,
1650 _CS("ckct_ib_max_zoom_level_reached"));
1652 } else if (zoom_level >= 1.5) {
1654 } else if (zoom_level >= 1.2) {
1656 } else if (zoom_level >= 1.0) {
1658 } else if (zoom_level >= 0.8) {
1660 } else if (zoom_level >= 0.5) {
1666 /* set zoom level */
1667 int_zoom = (gint) rint (zoom_level*100.0+0.1);
1668 banner_text = g_strdup_printf (_HL("wdgt_ib_zoom"), int_zoom);
1669 modest_platform_information_banner (GTK_WIDGET (window), NULL, banner_text);
1670 g_free (banner_text);
1671 modest_zoomable_set_zoom (MODEST_ZOOMABLE (priv->msg_view), zoom_level);
1677 modest_msg_view_window_zoom_minus (ModestWindow *window)
1680 ModestMsgViewWindowPrivate *priv;
1684 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), 1.0);
1685 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1687 zoom_level = modest_zoomable_get_zoom (MODEST_ZOOMABLE (priv->msg_view));
1689 if (zoom_level <= 0.5) {
1690 hildon_banner_show_information (NULL, NULL,
1691 _CS("ckct_ib_min_zoom_level_reached"));
1693 } else if (zoom_level <= 0.8) {
1695 } else if (zoom_level <= 1.0) {
1697 } else if (zoom_level <= 1.2) {
1699 } else if (zoom_level <= 1.5) {
1701 } else if (zoom_level <= 2.0) {
1707 /* set zoom level */
1708 int_zoom = (gint) rint (zoom_level*100.0+0.1);
1709 banner_text = g_strdup_printf (_HL("wdgt_ib_zoom"), int_zoom);
1710 modest_platform_information_banner (GTK_WIDGET (window), NULL, banner_text);
1711 g_free (banner_text);
1712 modest_zoomable_set_zoom (MODEST_ZOOMABLE (priv->msg_view), zoom_level);
1718 modest_msg_view_window_key_event (GtkWidget *window,
1724 focus = gtk_window_get_focus (GTK_WINDOW (window));
1726 /* for the find toolbar case */
1727 if (focus && GTK_IS_ENTRY (focus)) {
1728 if (event->keyval == GDK_BackSpace) {
1730 copy = gdk_event_copy ((GdkEvent *) event);
1731 gtk_widget_event (focus, copy);
1732 gdk_event_free (copy);
1742 modest_msg_view_window_last_message_selected (ModestMsgViewWindow *window)
1745 ModestMsgViewWindowPrivate *priv;
1746 GtkTreeIter tmp_iter;
1747 gboolean is_last_selected;
1749 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), TRUE);
1750 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1752 /*if no model (so no rows at all), then virtually we are the last*/
1753 if (!priv->header_model || !priv->row_reference)
1756 if (!gtk_tree_row_reference_valid (priv->row_reference))
1759 path = gtk_tree_row_reference_get_path (priv->row_reference);
1763 is_last_selected = TRUE;
1764 while (is_last_selected) {
1766 gtk_tree_path_next (path);
1767 if (!gtk_tree_model_get_iter (priv->header_model, &tmp_iter, path))
1769 gtk_tree_model_get (priv->header_model, &tmp_iter,
1770 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
1773 if (msg_is_visible (header, priv->is_outbox))
1774 is_last_selected = FALSE;
1775 g_object_unref(G_OBJECT(header));
1778 gtk_tree_path_free (path);
1779 return is_last_selected;
1783 modest_msg_view_window_has_headers_model (ModestMsgViewWindow *window)
1785 ModestMsgViewWindowPrivate *priv;
1787 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), TRUE);
1788 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1790 return priv->header_model != NULL;
1794 modest_msg_view_window_is_search_result (ModestMsgViewWindow *window)
1796 ModestMsgViewWindowPrivate *priv;
1798 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), TRUE);
1799 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1801 return priv->is_search_result;
1805 msg_is_visible (TnyHeader *header, gboolean check_outbox)
1807 if ((tny_header_get_flags(header) & TNY_HEADER_FLAG_DELETED))
1809 if (!check_outbox) {
1812 ModestTnySendQueueStatus status;
1813 status = modest_tny_all_send_queues_get_msg_status (header);
1814 return ((status != MODEST_TNY_SEND_QUEUE_FAILED) &&
1815 (status != MODEST_TNY_SEND_QUEUE_SENDING));
1820 modest_msg_view_window_first_message_selected (ModestMsgViewWindow *window)
1823 ModestMsgViewWindowPrivate *priv;
1824 gboolean is_first_selected;
1825 GtkTreeIter tmp_iter;
1827 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), TRUE);
1828 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1830 /*if no model (so no rows at all), then virtually we are the first*/
1831 if (!priv->header_model || !priv->row_reference)
1834 if (!gtk_tree_row_reference_valid (priv->row_reference))
1837 path = gtk_tree_row_reference_get_path (priv->row_reference);
1841 is_first_selected = TRUE;
1842 while (is_first_selected) {
1844 if(!gtk_tree_path_prev (path))
1846 /* Here the 'if' is needless for logic, but let make sure
1847 * iter is valid for gtk_tree_model_get. */
1848 if (!gtk_tree_model_get_iter (priv->header_model, &tmp_iter, path))
1850 gtk_tree_model_get (priv->header_model, &tmp_iter,
1851 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
1854 if (msg_is_visible (header, priv->is_outbox))
1855 is_first_selected = FALSE;
1856 g_object_unref(G_OBJECT(header));
1859 gtk_tree_path_free (path);
1860 return is_first_selected;
1867 GtkTreeRowReference *row_reference;
1871 message_reader_performer (gboolean canceled,
1873 GtkWindow *parent_window,
1874 TnyAccount *account,
1877 ModestMailOperation *mail_op = NULL;
1878 MsgReaderInfo *info;
1880 info = (MsgReaderInfo *) user_data;
1881 if (canceled || err) {
1882 update_window_title (MODEST_MSG_VIEW_WINDOW (parent_window));
1886 /* Register the header - it'll be unregistered in the callback */
1888 modest_window_mgr_register_header (modest_runtime_get_window_mgr (), info->header, NULL);
1890 /* New mail operation */
1891 mail_op = modest_mail_operation_new_with_error_handling (G_OBJECT(parent_window),
1892 modest_ui_actions_disk_operations_error_handler,
1895 modest_mail_operation_queue_add (modest_runtime_get_mail_operation_queue (), mail_op);
1897 modest_mail_operation_get_msg (mail_op, info->header, TRUE, view_msg_cb, info->row_reference);
1899 modest_mail_operation_find_msg (mail_op, info->folder, info->msg_uid, TRUE, view_msg_cb, NULL);
1900 g_object_unref (mail_op);
1902 /* Update dimming rules */
1903 modest_ui_actions_check_toolbar_dimming_rules (MODEST_WINDOW (parent_window));
1904 modest_ui_actions_check_menu_dimming_rules (MODEST_WINDOW (parent_window));
1907 /* Frees. The row_reference will be freed by the view_msg_cb callback */
1908 g_free (info->msg_uid);
1910 g_object_unref (info->folder);
1912 g_object_unref (info->header);
1913 g_slice_free (MsgReaderInfo, info);
1918 * Reads the message whose summary item is @header. It takes care of
1919 * several things, among others:
1921 * If the message was not previously downloaded then ask the user
1922 * before downloading. If there is no connection launch the connection
1923 * dialog. Update toolbar dimming rules.
1925 * Returns: TRUE if the mail operation was started, otherwise if the
1926 * user do not want to download the message, or if the user do not
1927 * want to connect, then the operation is not issued
1930 message_reader (ModestMsgViewWindow *window,
1931 ModestMsgViewWindowPrivate *priv,
1933 const gchar *msg_uid,
1935 GtkTreeRowReference *row_reference)
1937 ModestWindowMgr *mgr;
1938 TnyAccount *account = NULL;
1939 MsgReaderInfo *info;
1941 /* We set the header from model while we're loading */
1942 tny_header_view_set_header (TNY_HEADER_VIEW (priv->msg_view), header);
1943 gtk_window_set_title (GTK_WINDOW (window), _CS("ckdg_pb_updating"));
1949 g_object_ref (folder);
1951 mgr = modest_runtime_get_window_mgr ();
1952 /* Msg download completed */
1953 if (!header || !(tny_header_get_flags (header) & TNY_HEADER_FLAG_CACHED)) {
1955 /* Ask the user if he wants to download the message if
1957 if (!tny_device_is_online (modest_runtime_get_device())) {
1958 GtkResponseType response;
1960 response = modest_platform_run_confirmation_dialog (GTK_WINDOW (window),
1961 _("mcen_nc_get_msg"));
1962 if (response == GTK_RESPONSE_CANCEL) {
1963 update_window_title (window);
1968 folder = tny_header_get_folder (header);
1970 info = g_slice_new (MsgReaderInfo);
1971 info->msg_uid = g_strdup (msg_uid);
1973 info->header = g_object_ref (header);
1975 info->header = NULL;
1977 info->folder = g_object_ref (folder);
1979 info->folder = NULL;
1980 if (row_reference) {
1981 info->row_reference = gtk_tree_row_reference_copy (row_reference);
1983 info->row_reference = NULL;
1986 /* Offer the connection dialog if necessary */
1987 modest_platform_connect_if_remote_and_perform ((GtkWindow *) window,
1989 TNY_FOLDER_STORE (folder),
1990 message_reader_performer,
1993 g_object_unref (folder);
1999 folder = tny_header_get_folder (header);
2002 account = tny_folder_get_account (folder);
2004 info = g_slice_new (MsgReaderInfo);
2005 info->msg_uid = g_strdup (msg_uid);
2007 info->folder = g_object_ref (folder);
2009 info->folder = NULL;
2011 info->header = g_object_ref (header);
2013 info->header = NULL;
2015 info->row_reference = gtk_tree_row_reference_copy (row_reference);
2017 info->row_reference = NULL;
2019 message_reader_performer (FALSE, NULL, (GtkWindow *) window, account, info);
2021 g_object_unref (account);
2023 g_object_unref (folder);
2029 modest_msg_view_window_select_next_message (ModestMsgViewWindow *window)
2031 ModestMsgViewWindowPrivate *priv;
2032 GtkTreePath *path= NULL;
2033 GtkTreeIter tmp_iter;
2035 gboolean retval = TRUE;
2036 GtkTreeRowReference *row_reference = NULL;
2038 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), FALSE);
2039 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
2041 if (!priv->row_reference)
2044 /* Update the next row reference if it's not valid. This could
2045 happen if for example the header which it was pointing to,
2046 was deleted. The best place to do it is in the row-deleted
2047 handler but the tinymail model do not work like the glib
2048 tree models and reports the deletion when the row is still
2050 if (!gtk_tree_row_reference_valid (priv->next_row_reference)) {
2051 if (priv->next_row_reference) {
2052 gtk_tree_row_reference_free (priv->next_row_reference);
2054 if (gtk_tree_row_reference_valid (priv->row_reference)) {
2055 priv->next_row_reference = gtk_tree_row_reference_copy (priv->row_reference);
2056 select_next_valid_row (priv->header_model, &(priv->next_row_reference), FALSE, priv->is_outbox);
2058 priv->next_row_reference = NULL;
2061 if (priv->next_row_reference)
2062 path = gtk_tree_row_reference_get_path (priv->next_row_reference);
2066 row_reference = gtk_tree_row_reference_copy (priv->next_row_reference);
2068 gtk_tree_model_get_iter (priv->header_model,
2071 gtk_tree_path_free (path);
2073 gtk_tree_model_get (priv->header_model, &tmp_iter,
2074 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
2077 /* Read the message & show it */
2078 if (!message_reader (window, priv, header, NULL, NULL, row_reference)) {
2081 gtk_tree_row_reference_free (row_reference);
2084 g_object_unref (header);
2090 modest_msg_view_window_select_previous_message (ModestMsgViewWindow *window)
2092 ModestMsgViewWindowPrivate *priv = NULL;
2094 gboolean finished = FALSE;
2095 gboolean retval = FALSE;
2097 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), FALSE);
2098 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
2100 if (priv->row_reference && !gtk_tree_row_reference_valid (priv->row_reference)) {
2101 gtk_tree_row_reference_free (priv->row_reference);
2102 priv->row_reference = NULL;
2105 /* Return inmediatly if there is no header model */
2106 if (!priv->header_model || !priv->row_reference)
2109 path = gtk_tree_row_reference_get_path (priv->row_reference);
2110 while (!finished && gtk_tree_path_prev (path)) {
2114 gtk_tree_model_get_iter (priv->header_model, &iter, path);
2115 gtk_tree_model_get (priv->header_model, &iter,
2116 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
2120 if (msg_is_visible (header, priv->is_outbox)) {
2121 GtkTreeRowReference *row_reference;
2122 row_reference = gtk_tree_row_reference_new (priv->header_model, path);
2123 /* Read the message & show it */
2124 retval = message_reader (window, priv, header, NULL, NULL, row_reference);
2125 gtk_tree_row_reference_free (row_reference);
2129 g_object_unref (header);
2133 gtk_tree_path_free (path);
2138 view_msg_cb (ModestMailOperation *mail_op,
2145 ModestMsgViewWindow *self = NULL;
2146 ModestMsgViewWindowPrivate *priv = NULL;
2147 GtkTreeRowReference *row_reference = NULL;
2149 /* Unregister the header (it was registered before creating the mail operation) */
2150 modest_window_mgr_unregister_header (modest_runtime_get_window_mgr (), header);
2152 row_reference = (GtkTreeRowReference *) user_data;
2155 gtk_tree_row_reference_free (row_reference);
2156 self = (ModestMsgViewWindow *) modest_mail_operation_get_source (mail_op);
2158 /* Restore window title */
2159 update_window_title (self);
2160 modest_ui_actions_on_close_window (NULL, MODEST_WINDOW (self));
2161 g_object_unref (self);
2166 /* If there was any error */
2167 if (!modest_ui_actions_msg_retrieval_check (mail_op, header, msg)) {
2169 gtk_tree_row_reference_free (row_reference);
2170 self = (ModestMsgViewWindow *) modest_mail_operation_get_source (mail_op);
2172 /* Restore window title */
2173 update_window_title (self);
2174 modest_ui_actions_on_close_window (NULL, MODEST_WINDOW (self));
2175 g_object_unref (self);
2180 /* Get the window */
2181 self = (ModestMsgViewWindow *) modest_mail_operation_get_source (mail_op);
2182 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self));
2183 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
2185 /* Update the row reference */
2186 if (priv->row_reference != NULL) {
2187 gtk_tree_row_reference_free (priv->row_reference);
2188 priv->row_reference = (row_reference && gtk_tree_row_reference_valid (row_reference))?gtk_tree_row_reference_copy (row_reference):NULL;
2189 if (priv->next_row_reference != NULL) {
2190 gtk_tree_row_reference_free (priv->next_row_reference);
2192 if (priv->row_reference) {
2193 priv->next_row_reference = gtk_tree_row_reference_copy (priv->row_reference);
2194 select_next_valid_row (priv->header_model, &(priv->next_row_reference), TRUE, priv->is_outbox);
2196 priv->next_row_reference = NULL;
2200 /* Mark header as read */
2201 if (!(tny_header_get_flags (header) & TNY_HEADER_FLAG_SEEN))
2202 tny_header_set_flag (header, TNY_HEADER_FLAG_SEEN);
2204 /* Set new message */
2205 if (priv->msg_view != NULL && TNY_IS_MSG_VIEW (priv->msg_view)) {
2206 tny_msg_view_set_msg (TNY_MSG_VIEW (priv->msg_view), msg);
2207 modest_msg_view_window_update_priority (self);
2208 update_window_title (MODEST_MSG_VIEW_WINDOW (self));
2209 update_branding (MODEST_MSG_VIEW_WINDOW (self));
2210 modest_msg_view_grab_focus (MODEST_MSG_VIEW (priv->msg_view));
2213 /* Set the new message uid of the window */
2214 if (priv->msg_uid) {
2215 g_free (priv->msg_uid);
2216 priv->msg_uid = modest_tny_folder_get_header_unique_id (header);
2219 /* Notify the observers */
2220 g_signal_emit (G_OBJECT (self), signals[MSG_CHANGED_SIGNAL],
2221 0, priv->header_model, priv->row_reference);
2223 /* Sync the flags if the message is not opened from a header
2224 model, i.e, if it's opened from a notification */
2225 if (!priv->header_model)
2229 g_object_unref (self);
2231 gtk_tree_row_reference_free (row_reference);
2235 modest_msg_view_window_get_folder_type (ModestMsgViewWindow *window)
2237 ModestMsgViewWindowPrivate *priv;
2239 TnyFolderType folder_type;
2241 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
2243 folder_type = TNY_FOLDER_TYPE_UNKNOWN;
2245 msg = tny_msg_view_get_msg (TNY_MSG_VIEW (priv->msg_view));
2249 folder = tny_msg_get_folder (msg);
2251 folder_type = modest_tny_folder_guess_folder_type (folder);
2252 g_object_unref (folder);
2254 g_object_unref (msg);
2262 modest_msg_view_window_update_priority (ModestMsgViewWindow *window)
2264 ModestMsgViewWindowPrivate *priv;
2265 TnyHeader *header = NULL;
2266 TnyHeaderFlags flags = 0;
2268 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
2270 if (priv->header_model && priv->row_reference && gtk_tree_row_reference_valid (priv->row_reference)) {
2272 GtkTreePath *path = NULL;
2274 path = gtk_tree_row_reference_get_path (priv->row_reference);
2275 g_return_if_fail (path != NULL);
2276 gtk_tree_model_get_iter (priv->header_model,
2278 gtk_tree_row_reference_get_path (priv->row_reference));
2280 gtk_tree_model_get (priv->header_model, &iter, TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
2282 gtk_tree_path_free (path);
2285 msg = tny_msg_view_get_msg (TNY_MSG_VIEW (priv->msg_view));
2287 header = tny_msg_get_header (msg);
2288 g_object_unref (msg);
2293 flags = tny_header_get_flags (header);
2294 g_object_unref(G_OBJECT(header));
2297 modest_msg_view_set_priority (MODEST_MSG_VIEW(priv->msg_view), flags);
2302 toolbar_resize (ModestMsgViewWindow *self)
2304 ModestMsgViewWindowPrivate *priv = NULL;
2305 ModestWindowPrivate *parent_priv = NULL;
2307 gint static_button_size;
2308 ModestWindowMgr *mgr;
2310 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self));
2311 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
2312 parent_priv = MODEST_WINDOW_GET_PRIVATE(self);
2314 mgr = modest_runtime_get_window_mgr ();
2315 static_button_size = modest_window_mgr_get_fullscreen_mode (mgr)?120:120;
2317 if (parent_priv->toolbar) {
2318 /* Set expandable and homogeneous tool buttons */
2319 widget = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/ToolbarMessageReply");
2320 gtk_tool_item_set_expand (GTK_TOOL_ITEM (widget), TRUE);
2321 gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (widget), TRUE);
2322 widget = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/ToolbarMessageReplyAll");
2323 gtk_tool_item_set_expand (GTK_TOOL_ITEM (widget), TRUE);
2324 gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (widget), TRUE);
2325 widget = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/ToolbarMessageForward");
2326 gtk_tool_item_set_expand (GTK_TOOL_ITEM (widget), TRUE);
2327 gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (widget), TRUE);
2328 widget = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/ToolbarDeleteMessage");
2329 gtk_tool_item_set_expand (GTK_TOOL_ITEM (widget), TRUE);
2330 gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (widget), TRUE);
2331 widget = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/ToolbarDownloadExternalImages");
2332 gtk_tool_item_set_expand (GTK_TOOL_ITEM (widget), TRUE);
2333 gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (widget), TRUE);
2334 gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (priv->next_toolitem), TRUE);
2335 gtk_tool_item_set_expand (GTK_TOOL_ITEM (priv->next_toolitem), TRUE);
2336 gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (priv->prev_toolitem), TRUE);
2337 gtk_tool_item_set_expand (GTK_TOOL_ITEM (priv->prev_toolitem), TRUE);
2342 modest_msg_view_window_show_toolbar (ModestWindow *self,
2343 gboolean show_toolbar)
2345 ModestMsgViewWindowPrivate *priv = NULL;
2346 ModestWindowPrivate *parent_priv;
2348 parent_priv = MODEST_WINDOW_GET_PRIVATE(self);
2349 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(self);
2351 /* Set optimized view status */
2352 priv->optimized_view = !show_toolbar;
2354 if (!parent_priv->toolbar) {
2355 parent_priv->toolbar = gtk_ui_manager_get_widget (parent_priv->ui_manager,
2357 gtk_toolbar_set_icon_size (GTK_TOOLBAR (parent_priv->toolbar), HILDON_ICON_SIZE_FINGER);
2358 gtk_widget_set_no_show_all (parent_priv->toolbar, TRUE);
2360 priv->next_toolitem = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/ToolbarMessageNext");
2361 priv->prev_toolitem = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/ToolbarMessageBack");
2362 toolbar_resize (MODEST_MSG_VIEW_WINDOW (self));
2365 hildon_window_add_toolbar (HILDON_WINDOW (self),
2366 GTK_TOOLBAR (parent_priv->toolbar));
2371 /* Quick hack: this prevents toolbar icons "dance" when progress bar show status is changed */
2372 /* TODO: resize mode migth be GTK_RESIZE_QUEUE, in order to avoid unneccesary shows */
2373 gtk_container_set_resize_mode (GTK_CONTAINER(parent_priv->toolbar), GTK_RESIZE_IMMEDIATE);
2375 gtk_widget_show (GTK_WIDGET (parent_priv->toolbar));
2376 if (modest_msg_view_window_transfer_mode_enabled (MODEST_MSG_VIEW_WINDOW (self)))
2377 set_progress_hint (MODEST_MSG_VIEW_WINDOW (self), TRUE);
2379 set_progress_hint (MODEST_MSG_VIEW_WINDOW (self), FALSE);
2382 gtk_widget_set_no_show_all (parent_priv->toolbar, TRUE);
2383 gtk_widget_hide (GTK_WIDGET (parent_priv->toolbar));
2388 modest_msg_view_window_clipboard_owner_change (GtkClipboard *clipboard,
2390 ModestMsgViewWindow *window)
2392 if (!GTK_WIDGET_VISIBLE (window))
2395 modest_window_check_dimming_rules_group (MODEST_WINDOW (window), MODEST_DIMMING_RULES_CLIPBOARD);
2399 modest_msg_view_window_transfer_mode_enabled (ModestMsgViewWindow *self)
2401 ModestMsgViewWindowPrivate *priv;
2403 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self), FALSE);
2404 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(self);
2406 return priv->progress_hint;
2410 observers_empty (ModestMsgViewWindow *self)
2413 ModestMsgViewWindowPrivate *priv;
2414 gboolean is_empty = TRUE;
2415 guint pending_ops = 0;
2417 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(self);
2418 tmp = priv->progress_widgets;
2420 /* Check all observers */
2421 while (tmp && is_empty) {
2422 pending_ops = modest_progress_object_num_pending_operations (MODEST_PROGRESS_OBJECT(tmp->data));
2423 is_empty = pending_ops == 0;
2425 tmp = g_slist_next(tmp);
2432 on_account_removed (TnyAccountStore *account_store,
2433 TnyAccount *account,
2436 /* Do nothing if it's a transport account, because we only
2437 show the messages of a store account */
2438 if (tny_account_get_account_type(account) == TNY_ACCOUNT_TYPE_STORE) {
2439 const gchar *parent_acc = NULL;
2440 const gchar *our_acc = NULL;
2442 our_acc = modest_window_get_active_account (MODEST_WINDOW (user_data));
2443 parent_acc = modest_tny_account_get_parent_modest_account_name_for_server_account (account);
2445 /* Close this window if I'm showing a message of the removed account */
2446 if (our_acc && parent_acc && strcmp (parent_acc, our_acc) == 0)
2447 modest_ui_actions_on_close_window (NULL, MODEST_WINDOW (user_data));
2452 on_mail_operation_started (ModestMailOperation *mail_op,
2455 ModestMsgViewWindow *self;
2456 ModestMailOperationTypeOperation op_type;
2458 ModestMsgViewWindowPrivate *priv;
2459 GObject *source = NULL;
2461 self = MODEST_MSG_VIEW_WINDOW (user_data);
2462 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
2463 op_type = modest_mail_operation_get_type_operation (mail_op);
2464 tmp = priv->progress_widgets;
2465 source = modest_mail_operation_get_source(mail_op);
2466 if (G_OBJECT (self) == source) {
2467 if (op_type == MODEST_MAIL_OPERATION_TYPE_RECEIVE ||
2468 op_type == MODEST_MAIL_OPERATION_TYPE_OPEN ||
2469 op_type == MODEST_MAIL_OPERATION_TYPE_DELETE) {
2470 set_progress_hint (self, TRUE);
2472 modest_progress_object_add_operation (
2473 MODEST_PROGRESS_OBJECT (tmp->data),
2475 tmp = g_slist_next (tmp);
2479 g_object_unref (source);
2481 /* Update dimming rules */
2482 check_dimming_rules_after_change (self);
2486 on_mail_operation_finished (ModestMailOperation *mail_op,
2489 ModestMsgViewWindow *self;
2490 ModestMailOperationTypeOperation op_type;
2492 ModestMsgViewWindowPrivate *priv;
2494 self = MODEST_MSG_VIEW_WINDOW (user_data);
2495 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
2496 op_type = modest_mail_operation_get_type_operation (mail_op);
2497 tmp = priv->progress_widgets;
2499 if (op_type == MODEST_MAIL_OPERATION_TYPE_RECEIVE ||
2500 op_type == MODEST_MAIL_OPERATION_TYPE_OPEN ||
2501 op_type == MODEST_MAIL_OPERATION_TYPE_DELETE) {
2503 modest_progress_object_remove_operation (MODEST_PROGRESS_OBJECT (tmp->data),
2505 tmp = g_slist_next (tmp);
2508 /* If no more operations are being observed, NORMAL mode is enabled again */
2509 if (observers_empty (self)) {
2510 set_progress_hint (self, FALSE);
2514 /* Update dimming rules. We have to do this right here
2515 and not in view_msg_cb because at that point the
2516 transfer mode is still enabled so the dimming rule
2517 won't let the user delete the message that has been
2518 readed for example */
2519 check_dimming_rules_after_change (self);
2523 on_queue_changed (ModestMailOperationQueue *queue,
2524 ModestMailOperation *mail_op,
2525 ModestMailOperationQueueNotification type,
2526 ModestMsgViewWindow *self)
2528 ModestMsgViewWindowPrivate *priv;
2530 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
2532 /* If this operations was created by another window, do nothing */
2533 if (!modest_mail_operation_is_mine (mail_op, G_OBJECT(self)))
2536 if (type == MODEST_MAIL_OPERATION_QUEUE_OPERATION_ADDED) {
2537 priv->sighandlers = modest_signal_mgr_connect (priv->sighandlers,
2539 "operation-started",
2540 G_CALLBACK (on_mail_operation_started),
2542 priv->sighandlers = modest_signal_mgr_connect (priv->sighandlers,
2544 "operation-finished",
2545 G_CALLBACK (on_mail_operation_finished),
2547 } else if (type == MODEST_MAIL_OPERATION_QUEUE_OPERATION_REMOVED) {
2548 priv->sighandlers = modest_signal_mgr_disconnect (priv->sighandlers,
2550 "operation-started");
2551 priv->sighandlers = modest_signal_mgr_disconnect (priv->sighandlers,
2553 "operation-finished");
2558 modest_msg_view_window_get_attachments (ModestMsgViewWindow *win)
2560 ModestMsgViewWindowPrivate *priv;
2561 TnyList *selected_attachments = NULL;
2563 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (win), NULL);
2564 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (win);
2566 /* In Hildon 2.2 as there's no selection we assume we have all attachments selected */
2567 selected_attachments = modest_msg_view_get_attachments (MODEST_MSG_VIEW (priv->msg_view));
2569 return selected_attachments;
2573 ModestMsgViewWindow *self;
2575 gchar *attachment_uid;
2576 } DecodeAsyncHelper;
2579 on_decode_to_stream_async_handler (TnyMimePart *mime_part,
2585 DecodeAsyncHelper *helper = (DecodeAsyncHelper *) user_data;
2586 const gchar *content_type;
2588 if (cancelled || err) {
2591 if ((err->domain == TNY_ERROR_DOMAIN) &&
2592 (err->code == TNY_IO_ERROR_WRITE) &&
2593 (errno == ENOSPC)) {
2594 msg = g_strdup_printf (_KR("cerm_device_memory_full"), "");
2596 msg = g_strdup (_("mail_ib_file_operation_failed"));
2598 modest_platform_information_banner (NULL, NULL, msg);
2604 /* It could happen that the window was closed. So we
2605 assume it is a cancelation */
2606 if (!GTK_WIDGET_VISIBLE (helper->self))
2609 /* Remove the progress hint */
2610 set_progress_hint (helper->self, FALSE);
2612 content_type = tny_mime_part_get_content_type (mime_part);
2613 if (g_str_has_prefix (content_type, "message/rfc822")) {
2614 ModestWindowMgr *mgr;
2615 ModestWindow *msg_win = NULL;
2618 const gchar *mailbox;
2619 TnyStream *file_stream;
2622 fd = g_open (helper->file_path, O_RDONLY, 0644);
2624 file_stream = tny_fs_stream_new (fd);
2626 mgr = modest_runtime_get_window_mgr ();
2628 account = g_strdup (modest_window_get_active_account (MODEST_WINDOW (helper->self)));
2629 mailbox = modest_window_get_active_mailbox (MODEST_WINDOW (helper->self));
2632 account = modest_account_mgr_get_default_account (modest_runtime_get_account_mgr ());
2634 msg = tny_camel_msg_new ();
2635 tny_camel_msg_parse (msg, file_stream);
2636 msg_win = modest_msg_view_window_new_for_attachment (TNY_MSG (msg), account, mailbox, helper->attachment_uid);
2637 modest_window_set_zoom (MODEST_WINDOW (msg_win),
2638 modest_window_get_zoom (MODEST_WINDOW (helper->self)));
2639 if (modest_window_mgr_register_window (mgr, msg_win, MODEST_WINDOW (helper->self)))
2640 gtk_widget_show_all (GTK_WIDGET (msg_win));
2642 gtk_widget_destroy (GTK_WIDGET (msg_win));
2643 g_object_unref (msg);
2644 g_object_unref (file_stream);
2646 modest_platform_information_banner (NULL, NULL, _("mail_ib_file_operation_failed"));
2651 /* make the file read-only */
2652 g_chmod(helper->file_path, 0444);
2654 /* Activate the file */
2655 modest_platform_activate_file (helper->file_path, tny_mime_part_get_content_type (mime_part));
2660 g_object_unref (helper->self);
2661 g_free (helper->file_path);
2662 g_free (helper->attachment_uid);
2663 g_slice_free (DecodeAsyncHelper, helper);
2667 modest_msg_view_window_view_attachment (ModestMsgViewWindow *window,
2668 TnyMimePart *mime_part)
2670 ModestMsgViewWindowPrivate *priv;
2671 const gchar *msg_uid;
2672 gchar *attachment_uid = NULL;
2673 gint attachment_index = 0;
2674 TnyList *attachments;
2676 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window));
2677 g_return_if_fail (TNY_IS_MIME_PART (mime_part) || (mime_part == NULL));
2678 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
2680 msg_uid = modest_msg_view_window_get_message_uid (MODEST_MSG_VIEW_WINDOW (window));
2681 attachments = modest_msg_view_get_attachments (MODEST_MSG_VIEW (priv->msg_view));
2682 attachment_index = modest_list_index (attachments, (GObject *) mime_part);
2683 g_object_unref (attachments);
2685 if (msg_uid && attachment_index >= 0) {
2686 attachment_uid = g_strdup_printf ("%s/%d", msg_uid, attachment_index);
2689 if (mime_part == NULL) {
2690 gboolean error = FALSE;
2691 TnyList *selected_attachments = modest_msg_view_get_selected_attachments (MODEST_MSG_VIEW (priv->msg_view));
2692 if (selected_attachments == NULL || tny_list_get_length (selected_attachments) == 0) {
2694 } else if (tny_list_get_length (selected_attachments) > 1) {
2695 hildon_banner_show_information (NULL, NULL, _("mcen_ib_unable_to_display_more"));
2699 iter = tny_list_create_iterator (selected_attachments);
2700 mime_part = (TnyMimePart *) tny_iterator_get_current (iter);
2701 g_object_unref (iter);
2703 if (selected_attachments)
2704 g_object_unref (selected_attachments);
2709 g_object_ref (mime_part);
2712 if (tny_mime_part_is_purged (mime_part))
2715 if (!modest_tny_mime_part_is_msg (mime_part) && tny_mime_part_get_filename (mime_part)) {
2716 gchar *filepath = NULL;
2717 const gchar *att_filename = tny_mime_part_get_filename (mime_part);
2718 gboolean show_error_banner = FALSE;
2719 TnyFsStream *temp_stream = NULL;
2720 temp_stream = modest_utils_create_temp_stream (att_filename, attachment_uid,
2723 if (temp_stream != NULL) {
2724 ModestAccountMgr *mgr;
2725 DecodeAsyncHelper *helper;
2726 gboolean decode_in_provider;
2727 ModestProtocol *protocol;
2728 const gchar *account;
2730 /* Activate progress hint */
2731 set_progress_hint (window, TRUE);
2733 helper = g_slice_new0 (DecodeAsyncHelper);
2734 helper->self = g_object_ref (window);
2735 helper->file_path = g_strdup (filepath);
2736 helper->attachment_uid = g_strdup (attachment_uid);
2738 decode_in_provider = FALSE;
2739 mgr = modest_runtime_get_account_mgr ();
2740 account = modest_window_get_active_account (MODEST_WINDOW (window));
2741 if (modest_account_mgr_account_is_multimailbox (mgr, account, &protocol)) {
2742 if (MODEST_IS_ACCOUNT_PROTOCOL (protocol)) {
2744 uri = g_strconcat ("file://", filepath, NULL);
2745 decode_in_provider =
2746 modest_account_protocol_decode_part_to_stream_async (
2747 MODEST_ACCOUNT_PROTOCOL (protocol),
2750 TNY_STREAM (temp_stream),
2751 on_decode_to_stream_async_handler,
2758 if (!decode_in_provider)
2759 tny_mime_part_decode_to_stream_async (mime_part, TNY_STREAM (temp_stream),
2760 on_decode_to_stream_async_handler,
2763 g_object_unref (temp_stream);
2764 /* NOTE: files in the temporary area will be automatically
2765 * cleaned after some time if they are no longer in use */
2768 const gchar *content_type;
2769 /* the file may already exist but it isn't writable,
2770 * let's try to open it anyway */
2771 content_type = tny_mime_part_get_content_type (mime_part);
2772 modest_platform_activate_file (filepath, content_type);
2774 g_warning ("%s: modest_utils_create_temp_stream failed", __FUNCTION__);
2775 show_error_banner = TRUE;
2780 if (show_error_banner)
2781 modest_platform_information_banner (NULL, NULL, _("mail_ib_file_operation_failed"));
2782 } else if (!modest_tny_mime_part_is_msg (mime_part)) {
2783 ModestWindowMgr *mgr;
2784 ModestWindow *msg_win = NULL;
2785 TnyMsg *current_msg;
2789 current_msg = modest_msg_view_window_get_message (MODEST_MSG_VIEW_WINDOW (window));
2790 mgr = modest_runtime_get_window_mgr ();
2791 header = tny_msg_get_header (TNY_MSG (current_msg));
2792 found = modest_window_mgr_find_registered_message_uid (mgr,
2797 g_debug ("window for this body is already being created");
2800 /* it's not found, so create a new window for it */
2801 modest_window_mgr_register_header (mgr, header, attachment_uid); /* register the uid before building the window */
2802 gchar *account = g_strdup (modest_window_get_active_account (MODEST_WINDOW (window)));
2803 const gchar *mailbox = modest_window_get_active_mailbox (MODEST_WINDOW (window));
2805 account = modest_account_mgr_get_default_account (modest_runtime_get_account_mgr ());
2807 msg_win = modest_msg_view_window_new_with_other_body (TNY_MSG (current_msg), TNY_MIME_PART (mime_part),
2808 account, mailbox, attachment_uid);
2810 modest_window_set_zoom (MODEST_WINDOW (msg_win),
2811 modest_window_get_zoom (MODEST_WINDOW (window)));
2812 if (modest_window_mgr_register_window (mgr, msg_win, MODEST_WINDOW (window)))
2813 gtk_widget_show_all (GTK_WIDGET (msg_win));
2815 gtk_widget_destroy (GTK_WIDGET (msg_win));
2817 g_object_unref (current_msg);
2819 /* message attachment */
2820 TnyHeader *header = NULL;
2821 ModestWindowMgr *mgr;
2822 ModestWindow *msg_win = NULL;
2825 header = tny_msg_get_header (TNY_MSG (mime_part));
2826 mgr = modest_runtime_get_window_mgr ();
2827 found = modest_window_mgr_find_registered_header (mgr, header, &msg_win);
2830 /* if it's found, but there is no msg_win, it's probably in the process of being created;
2831 * thus, we don't do anything */
2832 g_debug ("window for is already being created");
2834 /* it's not found, so create a new window for it */
2835 modest_window_mgr_register_header (mgr, header, attachment_uid); /* register the uid before building the window */
2836 gchar *account = g_strdup (modest_window_get_active_account (MODEST_WINDOW (window)));
2837 const gchar *mailbox = modest_window_get_active_mailbox (MODEST_WINDOW (window));
2839 account = modest_account_mgr_get_default_account (modest_runtime_get_account_mgr ());
2840 msg_win = modest_msg_view_window_new_for_attachment (TNY_MSG (mime_part), account,
2841 mailbox, attachment_uid);
2842 modest_window_set_zoom (MODEST_WINDOW (msg_win),
2843 modest_window_get_zoom (MODEST_WINDOW (window)));
2844 if (modest_window_mgr_register_window (mgr, msg_win, MODEST_WINDOW (window)))
2845 gtk_widget_show_all (GTK_WIDGET (msg_win));
2847 gtk_widget_destroy (GTK_WIDGET (msg_win));
2853 g_free (attachment_uid);
2855 g_object_unref (mime_part);
2867 GnomeVFSResult result;
2869 ModestMsgViewWindow *window;
2872 static void save_mime_part_info_free (SaveMimePartInfo *info, gboolean with_struct);
2873 static gboolean idle_save_mime_part_show_result (SaveMimePartInfo *info);
2874 static gpointer save_mime_part_to_file (SaveMimePartInfo *info);
2875 static void save_mime_parts_to_file_with_checks (GtkWindow *parent, SaveMimePartInfo *info);
2878 save_mime_part_info_free (SaveMimePartInfo *info, gboolean with_struct)
2882 for (node = info->pairs; node != NULL; node = g_list_next (node)) {
2883 SaveMimePartPair *pair = (SaveMimePartPair *) node->data;
2884 g_free (pair->filename);
2885 g_object_unref (pair->part);
2886 g_slice_free (SaveMimePartPair, pair);
2888 g_list_free (info->pairs);
2891 g_object_unref (info->window);
2892 info->window = NULL;
2894 g_slice_free (SaveMimePartInfo, info);
2899 idle_save_mime_part_show_result (SaveMimePartInfo *info)
2901 /* This is a GDK lock because we are an idle callback and
2902 * hildon_banner_show_information is or does Gtk+ code */
2904 gdk_threads_enter (); /* CHECKED */
2905 if (info->result == GNOME_VFS_OK) {
2906 hildon_banner_show_information (NULL, NULL, _CS("sfil_ib_saved"));
2907 } else if (info->result == GNOME_VFS_ERROR_NO_SPACE) {
2910 /* Check if the uri belongs to the external mmc */
2911 if (g_str_has_prefix (info->uri, g_getenv (MODEST_MMC1_VOLUMEPATH_ENV)))
2912 msg = g_strdup_printf (_KR("cerm_device_memory_full"), "");
2914 msg = g_strdup (_KR("cerm_memory_card_full"));
2915 modest_platform_information_banner (NULL, NULL, msg);
2918 hildon_banner_show_information (NULL, NULL, _("mail_ib_file_operation_failed"));
2920 save_mime_part_info_free (info, FALSE);
2921 gdk_threads_leave (); /* CHECKED */
2927 save_mime_part_to_file (SaveMimePartInfo *info)
2929 GnomeVFSHandle *handle;
2931 SaveMimePartPair *pair = (SaveMimePartPair *) info->pairs->data;
2933 info->result = gnome_vfs_create (&handle, pair->filename, GNOME_VFS_OPEN_WRITE, FALSE, 0644);
2934 if (info->result == GNOME_VFS_OK) {
2935 GError *error = NULL;
2936 gboolean decode_in_provider;
2938 ModestAccountMgr *mgr;
2939 const gchar *account;
2940 ModestProtocol *protocol = NULL;
2942 stream = tny_vfs_stream_new (handle);
2944 decode_in_provider = FALSE;
2945 mgr = modest_runtime_get_account_mgr ();
2946 account = modest_window_get_active_account (MODEST_WINDOW (info->window));
2947 if (modest_account_mgr_account_is_multimailbox (mgr, account, &protocol)) {
2948 if (MODEST_IS_ACCOUNT_PROTOCOL (protocol)) {
2949 decode_in_provider =
2950 modest_account_protocol_decode_part_to_stream (
2951 MODEST_ACCOUNT_PROTOCOL (protocol),
2959 if (!decode_in_provider)
2960 written = tny_mime_part_decode_to_stream (pair->part, stream, &error);
2963 g_warning ("modest: could not save attachment %s: %d (%s)\n", pair->filename, error?error->code:-1, error?error->message:"Unknown error");
2965 if ((error->domain == TNY_ERROR_DOMAIN) &&
2966 (error->code == TNY_IO_ERROR_WRITE) &&
2967 (errno == ENOSPC)) {
2968 info->result = GNOME_VFS_ERROR_NO_SPACE;
2970 info->result = GNOME_VFS_ERROR_IO;
2973 g_object_unref (G_OBJECT (stream));
2975 g_warning ("Could not create save attachment %s: %s\n",
2976 pair->filename, gnome_vfs_result_to_string (info->result));
2979 /* Go on saving remaining files */
2980 info->pairs = g_list_remove_link (info->pairs, info->pairs);
2981 if (info->pairs != NULL) {
2982 save_mime_part_to_file (info);
2984 g_idle_add ((GSourceFunc) idle_save_mime_part_show_result, info);
2991 save_mime_parts_to_file_with_checks (GtkWindow *parent,
2992 SaveMimePartInfo *info)
2994 gboolean is_ok = TRUE;
2995 gint replaced_files = 0;
2996 const GList *files = info->pairs;
2997 const GList *iter, *to_replace = NULL;
2999 for (iter = files; (iter != NULL) && (replaced_files < 2); iter = g_list_next(iter)) {
3000 SaveMimePartPair *pair = iter->data;
3001 gchar *unescaped = g_uri_unescape_string (pair->filename, NULL);
3003 if (modest_utils_file_exists (unescaped)) {
3005 if (replaced_files == 1)
3010 if (replaced_files) {
3013 if (replaced_files == 1) {
3014 SaveMimePartPair *pair = to_replace->data;
3015 const gchar *basename = strrchr (pair->filename, G_DIR_SEPARATOR) + 1;
3016 gchar *escaped_basename, *message;
3018 escaped_basename = g_uri_unescape_string (basename, NULL);
3019 message = g_strdup_printf ("%s\n%s",
3020 _FM("docm_nc_replace_file"),
3021 (escaped_basename) ? escaped_basename : "");
3022 response = modest_platform_run_confirmation_dialog (parent, message);
3024 g_free (escaped_basename);
3026 response = modest_platform_run_confirmation_dialog (parent,
3027 _FM("docm_nc_replace_multiple"));
3029 if (response != GTK_RESPONSE_OK)
3034 save_mime_part_info_free (info, TRUE);
3036 g_thread_create ((GThreadFunc)save_mime_part_to_file, info, FALSE, NULL);
3041 typedef struct _SaveAttachmentsInfo {
3042 TnyList *attachments_list;
3043 ModestMsgViewWindow *window;
3044 } SaveAttachmentsInfo;
3047 save_attachments_response (GtkDialog *dialog,
3051 TnyList *mime_parts;
3053 GList *files_to_save = NULL;
3054 gchar *current_folder;
3055 SaveAttachmentsInfo *sa_info = (SaveAttachmentsInfo *) user_data;
3057 mime_parts = TNY_LIST (sa_info->attachments_list);
3059 if (arg1 != GTK_RESPONSE_OK)
3062 chooser_uri = gtk_file_chooser_get_uri (GTK_FILE_CHOOSER (dialog));
3063 current_folder = gtk_file_chooser_get_current_folder_uri (GTK_FILE_CHOOSER (dialog));
3064 if (current_folder && *current_folder != '\0') {
3066 modest_conf_set_string (modest_runtime_get_conf (), MODEST_CONF_LATEST_SAVE_ATTACHMENT_PATH,
3067 current_folder,&err);
3069 g_debug ("Error storing latest used folder: %s", err->message);
3073 g_free (current_folder);
3075 if (!modest_utils_folder_writable (chooser_uri)) {
3076 const gchar *err_msg;
3078 #ifdef MODEST_PLATFORM_MAEMO
3079 if (modest_maemo_utils_in_usb_mode ()) {
3080 err_msg = dgettext ("hildon-status-bar-usb", "usbh_ib_mmc_usb_connected");
3082 err_msg = _FM("sfil_ib_readonly_location");
3085 err_msg = _FM("sfil_ib_readonly_location");
3087 hildon_banner_show_information (NULL, NULL, err_msg);
3091 iter = tny_list_create_iterator (mime_parts);
3092 while (!tny_iterator_is_done (iter)) {
3093 TnyMimePart *mime_part = (TnyMimePart *) tny_iterator_get_current (iter);
3095 if ((modest_tny_mime_part_is_attachment_for_modest (mime_part)) &&
3096 !tny_mime_part_is_purged (mime_part) &&
3097 (tny_mime_part_get_filename (mime_part) != NULL)) {
3098 SaveMimePartPair *pair;
3100 pair = g_slice_new0 (SaveMimePartPair);
3102 if (tny_list_get_length (mime_parts) > 1) {
3104 gnome_vfs_escape_slashes (tny_mime_part_get_filename (mime_part));
3105 pair->filename = g_build_filename (chooser_uri, escaped, NULL);
3108 pair->filename = g_strdup (chooser_uri);
3110 pair->part = mime_part;
3111 files_to_save = g_list_prepend (files_to_save, pair);
3113 tny_iterator_next (iter);
3115 g_object_unref (iter);
3118 if (files_to_save != NULL) {
3119 SaveMimePartInfo *info = g_slice_new0 (SaveMimePartInfo);
3120 info->pairs = files_to_save;
3121 info->result = TRUE;
3122 info->uri = g_strdup (chooser_uri);
3123 info->window = g_object_ref (sa_info->window);
3124 save_mime_parts_to_file_with_checks ((GtkWindow *) dialog, info);
3126 g_free (chooser_uri);
3129 /* Free and close the dialog */
3130 g_object_unref (mime_parts);
3131 g_object_unref (sa_info->window);
3132 g_slice_free (SaveAttachmentsInfo, sa_info);
3133 gtk_widget_destroy (GTK_WIDGET (dialog));
3137 modest_msg_view_window_save_attachments (ModestMsgViewWindow *window,
3138 TnyList *mime_parts)
3140 ModestMsgViewWindowPrivate *priv;
3141 GtkWidget *save_dialog = NULL;
3142 gchar *conf_folder = NULL;
3143 gchar *filename = NULL;
3144 gchar *save_multiple_str = NULL;
3145 const gchar *root_folder = "file:///";
3147 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window));
3148 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
3150 if (mime_parts == NULL) {
3151 /* In Hildon 2.2 save and delete operate over all the attachments as there's no
3152 * selection available */
3153 mime_parts = modest_msg_view_get_attachments (MODEST_MSG_VIEW (priv->msg_view));
3154 if (mime_parts && !modest_maemo_utils_select_attachments (GTK_WINDOW (window), mime_parts, FALSE)) {
3155 g_object_unref (mime_parts);
3158 if (mime_parts == NULL || tny_list_get_length (mime_parts) == 0) {
3160 g_object_unref (mime_parts);
3166 g_object_ref (mime_parts);
3169 /* prepare dialog */
3170 if (tny_list_get_length (mime_parts) == 1) {
3172 /* only one attachment selected */
3173 iter = tny_list_create_iterator (mime_parts);
3174 TnyMimePart *mime_part = (TnyMimePart *) tny_iterator_get_current (iter);
3175 g_object_unref (iter);
3176 if (!modest_tny_mime_part_is_msg (mime_part) &&
3177 modest_tny_mime_part_is_attachment_for_modest (mime_part) &&
3178 !tny_mime_part_is_purged (mime_part)) {
3179 filename = g_strdup (tny_mime_part_get_filename (mime_part));
3181 /* TODO: show any error? */
3182 g_warning ("%s: Tried to save a non-file attachment", __FUNCTION__);
3183 g_object_unref (mime_parts);
3186 g_object_unref (mime_part);
3188 gint num = tny_list_get_length (mime_parts);
3189 save_multiple_str = g_strdup_printf (dngettext("hildon-fm",
3190 "sfil_va_number_of_objects_attachment",
3191 "sfil_va_number_of_objects_attachments",
3195 save_dialog = hildon_file_chooser_dialog_new (GTK_WINDOW (window),
3196 GTK_FILE_CHOOSER_ACTION_SAVE);
3198 /* Get last used folder */
3199 conf_folder = modest_conf_get_string (modest_runtime_get_conf (),
3200 MODEST_CONF_LATEST_SAVE_ATTACHMENT_PATH, NULL);
3202 /* File chooser stops working if we select "file:///" as current folder */
3203 if (conf_folder && g_ascii_strcasecmp (root_folder, conf_folder) != 0) {
3204 g_free (conf_folder);
3208 if (conf_folder && conf_folder[0] != '\0') {
3209 gtk_file_chooser_set_current_folder_uri (GTK_FILE_CHOOSER (save_dialog), conf_folder);
3212 /* Set the default folder to documents folder */
3213 docs_folder = (gchar *) g_strdup(g_get_user_special_dir (G_USER_DIRECTORY_DOCUMENTS));
3216 docs_folder = g_build_filename (g_getenv (MYDOCS_ENV), DOCS_FOLDER, NULL);
3218 gtk_file_chooser_set_current_folder (GTK_FILE_CHOOSER (save_dialog), docs_folder);
3219 g_free (docs_folder);
3221 g_free (conf_folder);
3225 gtk_file_chooser_set_current_name (GTK_FILE_CHOOSER (save_dialog),
3230 /* if multiple, set multiple string */
3231 if (save_multiple_str) {
3232 g_object_set (G_OBJECT (save_dialog), "save-multiple", save_multiple_str, NULL);
3233 gtk_window_set_title (GTK_WINDOW (save_dialog), _FM("sfil_ti_save_objects_files"));
3234 g_free (save_multiple_str);
3237 /* We must run this asynchronously, because the hildon dialog
3238 performs a gtk_dialog_run by itself which leads to gdk
3240 SaveAttachmentsInfo *sa_info;
3241 sa_info = g_slice_new (SaveAttachmentsInfo);
3242 sa_info->attachments_list = mime_parts;
3243 sa_info->window = g_object_ref (window);
3244 g_signal_connect (save_dialog, "response",
3245 G_CALLBACK (save_attachments_response), sa_info);
3247 gtk_widget_show_all (save_dialog);
3251 show_remove_attachment_information (gpointer userdata)
3253 ModestMsgViewWindow *window = (ModestMsgViewWindow *) userdata;
3254 ModestMsgViewWindowPrivate *priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
3256 /* We're outside the main lock */
3257 gdk_threads_enter ();
3259 if (priv->remove_attachment_banner != NULL) {
3260 gtk_widget_destroy (priv->remove_attachment_banner);
3261 g_object_unref (priv->remove_attachment_banner);
3264 priv->remove_attachment_banner = g_object_ref (
3265 hildon_banner_show_animation (NULL, NULL, _("mcen_me_inbox_remove_attachments")));
3267 gdk_threads_leave ();
3273 modest_msg_view_window_remove_attachments (ModestMsgViewWindow *window, gboolean get_all)
3275 ModestMsgViewWindowPrivate *priv;
3276 TnyList *mime_parts = NULL, *tmp;
3277 gchar *confirmation_message;
3283 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window));
3284 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
3286 /* In hildon 2.2 we ignore the get_all flag as we always get all attachments. This is
3287 * because we don't have selection
3289 mime_parts = modest_msg_view_get_attachments (MODEST_MSG_VIEW (priv->msg_view));
3291 /* Remove already purged messages from mime parts list. We use
3292 a copy of the list to remove items in the original one */
3293 tmp = tny_list_copy (mime_parts);
3294 iter = tny_list_create_iterator (tmp);
3295 while (!tny_iterator_is_done (iter)) {
3296 TnyMimePart *part = TNY_MIME_PART (tny_iterator_get_current (iter));
3297 if (tny_mime_part_is_purged (part))
3298 tny_list_remove (mime_parts, (GObject *) part);
3300 g_object_unref (part);
3301 tny_iterator_next (iter);
3303 g_object_unref (tmp);
3304 g_object_unref (iter);
3306 if (!modest_maemo_utils_select_attachments (GTK_WINDOW (window), mime_parts, TRUE) ||
3307 tny_list_get_length (mime_parts) == 0) {
3308 g_object_unref (mime_parts);
3312 n_attachments = tny_list_get_length (mime_parts);
3313 if (n_attachments == 1) {
3317 iter = tny_list_create_iterator (mime_parts);
3318 part = (TnyMimePart *) tny_iterator_get_current (iter);
3319 g_object_unref (iter);
3320 if (modest_tny_mime_part_is_msg (part)) {
3322 header = tny_msg_get_header (TNY_MSG (part));
3323 filename = tny_header_dup_subject (header);
3324 g_object_unref (header);
3325 if (filename == NULL)
3326 filename = g_strdup (_("mail_va_no_subject"));
3328 filename = g_strdup (tny_mime_part_get_filename (TNY_MIME_PART (part)));
3330 confirmation_message = g_strdup_printf (_("mcen_nc_purge_file_text"), filename);
3332 g_object_unref (part);
3334 confirmation_message = g_strdup_printf (ngettext("mcen_nc_purge_file_text",
3335 "mcen_nc_purge_files_text",
3336 n_attachments), n_attachments);
3338 response = modest_platform_run_confirmation_dialog (GTK_WINDOW (window),
3339 confirmation_message);
3340 g_free (confirmation_message);
3342 if (response != GTK_RESPONSE_OK) {
3343 g_object_unref (mime_parts);
3347 priv->purge_timeout = g_timeout_add (2000, show_remove_attachment_information, window);
3349 iter = tny_list_create_iterator (mime_parts);
3350 while (!tny_iterator_is_done (iter)) {
3353 part = (TnyMimePart *) tny_iterator_get_current (iter);
3354 tny_mime_part_set_purged (TNY_MIME_PART (part));
3355 g_object_unref (part);
3356 tny_iterator_next (iter);
3358 g_object_unref (iter);
3360 msg = tny_msg_view_get_msg (TNY_MSG_VIEW (priv->msg_view));
3361 tny_msg_view_clear (TNY_MSG_VIEW (priv->msg_view));
3362 tny_msg_rewrite_cache (msg);
3363 tny_msg_view_set_msg (TNY_MSG_VIEW (priv->msg_view), msg);
3364 g_object_unref (msg);
3365 update_branding (MODEST_MSG_VIEW_WINDOW (window));
3367 g_object_unref (mime_parts);
3369 if (priv->purge_timeout > 0) {
3370 g_source_remove (priv->purge_timeout);
3371 priv->purge_timeout = 0;
3374 if (priv->remove_attachment_banner) {
3375 gtk_widget_destroy (priv->remove_attachment_banner);
3376 g_object_unref (priv->remove_attachment_banner);
3377 priv->remove_attachment_banner = NULL;
3383 update_window_title (ModestMsgViewWindow *window)
3385 ModestMsgViewWindowPrivate *priv;
3387 TnyHeader *header = NULL;
3388 gchar *subject = NULL;
3390 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
3392 /* Note that if the window is closed while we're retrieving
3393 the message, this widget could de deleted */
3394 if (!priv->msg_view)
3397 msg = tny_msg_view_get_msg (TNY_MSG_VIEW (priv->msg_view));
3399 if (priv->other_body) {
3402 description = modest_tny_mime_part_get_header_value (priv->other_body, "Content-Description");
3404 g_strstrip (description);
3405 subject = description;
3407 } else if (msg != NULL) {
3408 header = tny_msg_get_header (msg);
3409 subject = tny_header_dup_subject (header);
3410 g_object_unref (header);
3411 g_object_unref (msg);
3414 if ((subject == NULL)||(subject[0] == '\0')) {
3416 subject = g_strdup (_("mail_va_no_subject"));
3419 gtk_window_set_title (GTK_WINDOW (window), subject);
3424 on_move_focus (GtkWidget *widget,
3425 GtkDirectionType direction,
3428 g_signal_stop_emission_by_name (G_OBJECT (widget), "move-focus");
3432 fetch_image_open_stream (TnyStreamCache *self, gint64 *expected_size, gchar *uri)
3434 GnomeVFSResult result;
3435 GnomeVFSHandle *handle = NULL;
3436 GnomeVFSFileInfo *info = NULL;
3439 result = gnome_vfs_open (&handle, uri, GNOME_VFS_OPEN_READ);
3440 if (result != GNOME_VFS_OK) {
3445 info = gnome_vfs_file_info_new ();
3446 result = gnome_vfs_get_file_info_from_handle (handle, info, GNOME_VFS_FILE_INFO_DEFAULT);
3447 if (result != GNOME_VFS_OK || ! (info->valid_fields & GNOME_VFS_FILE_INFO_FIELDS_SIZE)) {
3448 /* We put a "safe" default size for going to cache */
3449 *expected_size = (300*1024);
3451 *expected_size = info->size;
3453 gnome_vfs_file_info_unref (info);
3455 stream = tny_vfs_stream_new (handle);
3464 TnyStream *output_stream;
3465 GtkWidget *msg_view;
3470 on_fetch_image_idle_refresh_view (gpointer userdata)
3473 FetchImageData *fidata = (FetchImageData *) userdata;
3475 gdk_threads_enter ();
3476 if (GTK_WIDGET_DRAWABLE (fidata->msg_view)) {
3477 ModestMsgViewWindowPrivate *priv;
3479 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (fidata->window);
3480 priv->fetching_images--;
3481 gtk_widget_queue_draw (fidata->msg_view);
3482 update_progress_hint (MODEST_MSG_VIEW_WINDOW (fidata->window));
3484 gdk_threads_leave ();
3486 g_object_unref (fidata->msg_view);
3487 g_object_unref (fidata->window);
3488 g_slice_free (FetchImageData, fidata);
3493 on_fetch_image_thread (gpointer userdata)
3495 FetchImageData *fidata = (FetchImageData *) userdata;
3496 TnyStreamCache *cache;
3497 TnyStream *cache_stream;
3499 cache = modest_runtime_get_images_cache ();
3501 tny_stream_cache_get_stream (cache,
3503 (TnyStreamCacheOpenStreamFetcher) fetch_image_open_stream,
3504 (gpointer) fidata->uri);
3505 g_free (fidata->cache_id);
3506 g_free (fidata->uri);
3508 if (cache_stream != NULL) {
3511 while (G_LIKELY (!tny_stream_is_eos (cache_stream))) {
3514 nb_read = tny_stream_read (cache_stream, buffer, sizeof (buffer));
3515 if (G_UNLIKELY (nb_read < 0)) {
3517 } else if (G_LIKELY (nb_read > 0)) {
3518 gssize nb_written = 0;
3520 while (G_UNLIKELY (nb_written < nb_read)) {
3523 len = tny_stream_write (fidata->output_stream, buffer + nb_written,
3524 nb_read - nb_written);
3525 if (G_UNLIKELY (len < 0))
3531 tny_stream_close (cache_stream);
3532 g_object_unref (cache_stream);
3535 tny_stream_close (fidata->output_stream);
3536 g_object_unref (fidata->output_stream);
3538 g_idle_add (on_fetch_image_idle_refresh_view, fidata);
3544 on_fetch_image (ModestMsgView *msgview,
3547 ModestMsgViewWindow *window)
3549 const gchar *current_account;
3550 ModestMsgViewWindowPrivate *priv;
3551 FetchImageData *fidata;
3553 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
3555 current_account = modest_window_get_active_account (MODEST_WINDOW (window));
3557 fidata = g_slice_new0 (FetchImageData);
3558 fidata->msg_view = g_object_ref (msgview);
3559 fidata->window = g_object_ref (window);
3560 fidata->uri = g_strdup (uri);
3561 fidata->cache_id = modest_images_cache_get_id (current_account, uri);
3562 fidata->output_stream = g_object_ref (stream);
3564 priv->fetching_images++;
3565 if (g_thread_create (on_fetch_image_thread, fidata, FALSE, NULL) == NULL) {
3566 g_object_unref (fidata->output_stream);
3567 g_free (fidata->cache_id);
3568 g_free (fidata->uri);
3569 g_object_unref (fidata->msg_view);
3570 g_slice_free (FetchImageData, fidata);
3571 tny_stream_close (stream);
3572 priv->fetching_images--;
3573 update_progress_hint (window);
3576 update_progress_hint (window);
3582 setup_menu (ModestMsgViewWindow *self)
3584 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW(self));
3586 /* Settings menu buttons */
3587 modest_hildon2_window_add_to_menu (MODEST_HILDON2_WINDOW (self), _("mcen_me_viewer_find"), NULL,
3588 APP_MENU_CALLBACK (modest_msg_view_window_show_find_toolbar),
3589 MODEST_DIMMING_CALLBACK (modest_ui_dimming_rules_on_find_in_msg));
3591 modest_hildon2_window_add_to_menu (MODEST_HILDON2_WINDOW (self),
3592 dngettext(GETTEXT_PACKAGE,
3593 "mcen_me_move_message",
3594 "mcen_me_move_messages",
3597 APP_MENU_CALLBACK (modest_ui_actions_on_move_to),
3598 MODEST_DIMMING_CALLBACK (modest_ui_dimming_rules_on_move_to));
3600 modest_hildon2_window_add_to_menu (MODEST_HILDON2_WINDOW (self), _("mcen_me_inbox_mark_as_read"), NULL,
3601 APP_MENU_CALLBACK (modest_ui_actions_on_mark_as_read),
3602 MODEST_DIMMING_CALLBACK (modest_ui_dimming_rules_on_mark_as_read_msg_in_view));
3604 modest_hildon2_window_add_to_menu (MODEST_HILDON2_WINDOW (self), _("mcen_me_inbox_mark_as_unread"), NULL,
3605 APP_MENU_CALLBACK (modest_ui_actions_on_mark_as_unread),
3606 MODEST_DIMMING_CALLBACK (modest_ui_dimming_rules_on_mark_as_unread_msg_in_view));
3608 modest_hildon2_window_add_to_menu (MODEST_HILDON2_WINDOW (self), _("mcen_me_viewer_save_attachments"), NULL,
3609 APP_MENU_CALLBACK (modest_ui_actions_save_attachments),
3610 MODEST_DIMMING_CALLBACK (modest_ui_dimming_rules_on_save_attachments));
3611 modest_hildon2_window_add_to_menu (MODEST_HILDON2_WINDOW (self), _("mcen_me_inbox_remove_attachments"), NULL,
3612 APP_MENU_CALLBACK (modest_ui_actions_remove_attachments),
3613 MODEST_DIMMING_CALLBACK (modest_ui_dimming_rules_on_remove_attachments));
3615 modest_hildon2_window_add_to_menu (MODEST_HILDON2_WINDOW (self), _("mcen_me_new_message"), "<Control>n",
3616 APP_MENU_CALLBACK (modest_ui_actions_on_new_msg),
3617 MODEST_DIMMING_CALLBACK (modest_ui_dimming_rules_on_new_msg));
3618 modest_hildon2_window_add_to_menu (MODEST_HILDON2_WINDOW (self), _("mcen_me_viewer_addtocontacts"), NULL,
3619 APP_MENU_CALLBACK (modest_ui_actions_add_to_contacts),
3620 MODEST_DIMMING_CALLBACK (modest_ui_dimming_rules_on_add_to_contacts));
3622 modest_hildon2_window_add_to_menu (MODEST_HILDON2_WINDOW (self), _("mcen_ti_message_properties"), NULL,
3623 APP_MENU_CALLBACK (modest_ui_actions_on_details),
3624 MODEST_DIMMING_CALLBACK (modest_ui_dimming_rules_on_details));
3628 modest_msg_view_window_add_to_contacts (ModestMsgViewWindow *self)
3630 ModestMsgViewWindowPrivate *priv;
3631 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
3632 GSList *recipients = NULL;
3635 msg = tny_msg_view_get_msg (TNY_MSG_VIEW (priv->msg_view));
3639 header = modest_msg_view_window_get_header (self);
3642 recipients = modest_tny_msg_header_get_all_recipients_list (header);
3643 g_object_unref (header);
3645 recipients = modest_tny_msg_get_all_recipients_list (msg);
3646 g_object_unref (msg);
3650 /* Offer the user to add recipients to the address book */
3651 modest_address_book_add_address_list_with_selector (recipients, (GtkWindow *) self);
3652 g_slist_foreach (recipients, (GFunc) g_free, NULL); g_slist_free (recipients);
3657 _modest_msg_view_window_map_event (GtkWidget *widget,
3661 ModestMsgViewWindow *self = (ModestMsgViewWindow *) userdata;
3663 update_progress_hint (self);
3669 modest_msg_view_window_fetch_images (ModestMsgViewWindow *self)
3671 ModestMsgViewWindowPrivate *priv;
3672 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
3674 modest_msg_view_request_fetch_images (MODEST_MSG_VIEW (priv->msg_view));
3678 modest_msg_view_window_has_blocked_external_images (ModestMsgViewWindow *self)
3680 ModestMsgViewWindowPrivate *priv;
3681 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
3683 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self), FALSE);
3685 return modest_msg_view_has_blocked_external_images (MODEST_MSG_VIEW (priv->msg_view));
3689 modest_msg_view_window_reload (ModestMsgViewWindow *self)
3691 ModestMsgViewWindowPrivate *priv;
3692 const gchar *msg_uid;
3693 TnyHeader *header = NULL;
3694 TnyFolder *folder = NULL;
3696 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self));
3698 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
3700 header = modest_msg_view_window_get_header (MODEST_MSG_VIEW_WINDOW (self));
3704 folder = tny_header_get_folder (header);
3705 g_object_unref (header);
3710 msg_uid = modest_msg_view_window_get_message_uid (self);
3712 GtkTreeRowReference *row_reference;
3714 if (priv->row_reference && gtk_tree_row_reference_valid (priv->row_reference)) {
3715 row_reference = priv->row_reference;
3717 row_reference = NULL;
3719 if (!message_reader (self, priv, NULL, msg_uid, folder, row_reference))
3720 g_warning ("Shouldn't happen, trying to reload a message failed");
3723 g_object_unref (folder);
3727 update_branding (ModestMsgViewWindow *self)
3729 const gchar *account;
3730 const gchar *mailbox;
3731 ModestAccountMgr *mgr;
3732 ModestProtocol *protocol = NULL;
3733 gchar *service_name = NULL;
3734 const GdkPixbuf *service_icon = NULL;
3735 ModestMsgViewWindowPrivate *priv;
3737 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
3739 account = modest_window_get_active_account (MODEST_WINDOW (self));
3740 mailbox = modest_window_get_active_mailbox (MODEST_WINDOW (self));
3742 mgr = modest_runtime_get_account_mgr ();
3744 if (modest_account_mgr_account_is_multimailbox (mgr, account, &protocol)) {
3745 if (MODEST_IS_ACCOUNT_PROTOCOL (protocol)) {
3746 service_name = modest_account_protocol_get_service_name (MODEST_ACCOUNT_PROTOCOL (protocol),
3748 service_icon = modest_account_protocol_get_service_icon (MODEST_ACCOUNT_PROTOCOL (protocol),
3749 account, mailbox, MODEST_ICON_SIZE_SMALL);
3753 modest_msg_view_set_branding (MODEST_MSG_VIEW (priv->msg_view), service_name, service_icon);
3754 g_free (service_name);
3758 sync_flags (ModestMsgViewWindow *self)
3760 TnyHeader *header = NULL;
3762 header = modest_msg_view_window_get_header (self);
3764 TnyMsg *msg = modest_msg_view_window_get_message (self);
3766 header = tny_msg_get_header (msg);
3767 g_object_unref (msg);
3772 TnyFolder *folder = tny_header_get_folder (header);
3775 ModestMailOperation *mail_op;
3777 /* Sync folder, we need this to save the seen flag */
3778 mail_op = modest_mail_operation_new (NULL);
3779 modest_mail_operation_queue_add (modest_runtime_get_mail_operation_queue (),
3781 modest_mail_operation_sync_folder (mail_op, folder, FALSE, NULL, NULL);
3782 g_object_unref (mail_op);
3783 g_object_unref (folder);
3785 g_object_unref (header);