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>
71 #define MYDOCS_ENV "MYDOCSDIR"
72 #define DOCS_FOLDER ".documents"
74 typedef struct _ModestMsgViewWindowPrivate ModestMsgViewWindowPrivate;
75 struct _ModestMsgViewWindowPrivate {
78 GtkWidget *main_scroll;
79 GtkWidget *find_toolbar;
82 /* Progress observers */
83 GSList *progress_widgets;
86 GtkWidget *prev_toolitem;
87 GtkWidget *next_toolitem;
88 gboolean progress_hint;
91 /* Optimized view enabled */
92 gboolean optimized_view;
94 /* Whether this was created via the *_new_for_search_result() function. */
95 gboolean is_search_result;
97 /* Whether the message is in outbox */
100 /* A reference to the @model of the header view
101 * to allow selecting previous/next messages,
102 * if the message is currently selected in the header view.
104 const gchar *header_folder_id;
105 GtkTreeModel *header_model;
106 GtkTreeRowReference *row_reference;
107 GtkTreeRowReference *next_row_reference;
109 gulong clipboard_change_handler;
110 gulong queue_change_handler;
111 gulong account_removed_handler;
112 gulong row_changed_handler;
113 gulong row_deleted_handler;
114 gulong row_inserted_handler;
115 gulong rows_reordered_handler;
118 GtkWidget *remove_attachment_banner;
121 TnyMimePart *other_body;
126 static void modest_msg_view_window_class_init (ModestMsgViewWindowClass *klass);
127 static void modest_msg_view_window_init (ModestMsgViewWindow *obj);
128 static void modest_header_view_observer_init (ModestHeaderViewObserverIface *iface_class);
129 static void modest_msg_view_window_finalize (GObject *obj);
130 static void modest_msg_view_window_show_find_toolbar (GtkWidget *obj, gpointer data);
131 static void modest_msg_view_window_find_toolbar_close (GtkWidget *widget,
132 ModestMsgViewWindow *obj);
133 static void modest_msg_view_window_find_toolbar_search (GtkWidget *widget,
134 ModestMsgViewWindow *obj);
135 static void modest_msg_view_window_toggle_find_toolbar (GtkWidget *obj,
137 static void modest_msg_view_window_disconnect_signals (ModestWindow *self);
139 static gdouble modest_msg_view_window_get_zoom (ModestWindow *window);
140 static void modest_msg_view_window_set_zoom (ModestWindow *window,
142 static gboolean modest_msg_view_window_zoom_minus (ModestWindow *window);
143 static gboolean modest_msg_view_window_zoom_plus (ModestWindow *window);
144 static gboolean modest_msg_view_window_key_event (GtkWidget *window,
147 static void modest_msg_view_window_update_priority (ModestMsgViewWindow *window);
149 static void modest_msg_view_window_show_toolbar (ModestWindow *window,
150 gboolean show_toolbar);
152 static void modest_msg_view_window_clipboard_owner_change (GtkClipboard *clipboard,
154 ModestMsgViewWindow *window);
156 static void modest_msg_view_window_on_row_changed (GtkTreeModel *header_model,
159 ModestMsgViewWindow *window);
161 static void modest_msg_view_window_on_row_deleted (GtkTreeModel *header_model,
163 ModestMsgViewWindow *window);
165 static void modest_msg_view_window_on_row_inserted (GtkTreeModel *header_model,
166 GtkTreePath *tree_path,
167 GtkTreeIter *tree_iter,
168 ModestMsgViewWindow *window);
170 static void modest_msg_view_window_on_row_reordered (GtkTreeModel *header_model,
174 ModestMsgViewWindow *window);
176 static void modest_msg_view_window_update_model_replaced (ModestHeaderViewObserver *window,
178 const gchar *tny_folder_id);
180 static void on_queue_changed (ModestMailOperationQueue *queue,
181 ModestMailOperation *mail_op,
182 ModestMailOperationQueueNotification type,
183 ModestMsgViewWindow *self);
185 static void on_account_removed (TnyAccountStore *account_store,
189 static void on_move_focus (GtkWidget *widget,
190 GtkDirectionType direction,
193 static void view_msg_cb (ModestMailOperation *mail_op,
200 static void set_progress_hint (ModestMsgViewWindow *self,
203 static void update_window_title (ModestMsgViewWindow *window);
205 static gboolean set_toolbar_transfer_mode (ModestMsgViewWindow *self);
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);
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 gboolean modest_msg_view_window_scroll_child (ModestMsgViewWindow *self,
300 GtkScrollType scroll_type,
304 ModestMsgViewWindowPrivate *priv;
305 gboolean return_value;
307 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(self);
308 g_signal_emit_by_name (priv->main_scroll, "scroll-child", scroll_type, horizontal, &return_value);
313 add_scroll_binding (GtkBindingSet *binding_set,
315 GtkScrollType scroll)
317 guint keypad_keyval = keyval - GDK_Left + GDK_KP_Left;
319 gtk_binding_entry_add_signal (binding_set, keyval, 0,
321 GTK_TYPE_SCROLL_TYPE, scroll,
322 G_TYPE_BOOLEAN, FALSE);
323 gtk_binding_entry_add_signal (binding_set, keypad_keyval, 0,
325 GTK_TYPE_SCROLL_TYPE, scroll,
326 G_TYPE_BOOLEAN, FALSE);
330 modest_msg_view_window_class_init (ModestMsgViewWindowClass *klass)
332 GObjectClass *gobject_class;
333 HildonWindowClass *hildon_window_class;
334 ModestWindowClass *modest_window_class;
335 GtkBindingSet *binding_set;
337 gobject_class = (GObjectClass*) klass;
338 hildon_window_class = (HildonWindowClass *) klass;
339 modest_window_class = (ModestWindowClass *) klass;
341 parent_class = g_type_class_peek_parent (klass);
342 gobject_class->finalize = modest_msg_view_window_finalize;
344 modest_window_class->set_zoom_func = modest_msg_view_window_set_zoom;
345 modest_window_class->get_zoom_func = modest_msg_view_window_get_zoom;
346 modest_window_class->zoom_plus_func = modest_msg_view_window_zoom_plus;
347 modest_window_class->zoom_minus_func = modest_msg_view_window_zoom_minus;
348 modest_window_class->show_toolbar_func = modest_msg_view_window_show_toolbar;
349 modest_window_class->disconnect_signals_func = modest_msg_view_window_disconnect_signals;
351 modest_window_class->save_state_func = save_state;
353 klass->scroll_child = modest_msg_view_window_scroll_child;
355 signals[MSG_CHANGED_SIGNAL] =
356 g_signal_new ("msg-changed",
357 G_TYPE_FROM_CLASS (gobject_class),
359 G_STRUCT_OFFSET (ModestMsgViewWindowClass, msg_changed),
361 modest_marshal_VOID__POINTER_POINTER,
362 G_TYPE_NONE, 2, G_TYPE_POINTER, G_TYPE_POINTER);
364 signals[SCROLL_CHILD_SIGNAL] =
365 g_signal_new ("scroll-child",
366 G_TYPE_FROM_CLASS (gobject_class),
367 G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
368 G_STRUCT_OFFSET (ModestMsgViewWindowClass, scroll_child),
370 modest_marshal_BOOLEAN__ENUM_BOOLEAN,
371 G_TYPE_BOOLEAN, 2, GTK_TYPE_SCROLL_TYPE, G_TYPE_BOOLEAN);
373 binding_set = gtk_binding_set_by_class (klass);
374 add_scroll_binding (binding_set, GDK_Up, GTK_SCROLL_STEP_UP);
375 add_scroll_binding (binding_set, GDK_Down, GTK_SCROLL_STEP_DOWN);
376 add_scroll_binding (binding_set, GDK_Page_Up, GTK_SCROLL_PAGE_UP);
377 add_scroll_binding (binding_set, GDK_Page_Down, GTK_SCROLL_PAGE_DOWN);
378 add_scroll_binding (binding_set, GDK_Home, GTK_SCROLL_START);
379 add_scroll_binding (binding_set, GDK_End, GTK_SCROLL_END);
381 g_type_class_add_private (gobject_class, sizeof(ModestMsgViewWindowPrivate));
385 static void modest_header_view_observer_init(
386 ModestHeaderViewObserverIface *iface_class)
388 iface_class->update_func = modest_msg_view_window_update_model_replaced;
392 modest_msg_view_window_init (ModestMsgViewWindow *obj)
394 ModestMsgViewWindowPrivate *priv;
395 ModestWindowPrivate *parent_priv = NULL;
396 GtkActionGroup *action_group = NULL;
397 GError *error = NULL;
398 GdkPixbuf *window_icon;
400 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(obj);
401 parent_priv = MODEST_WINDOW_GET_PRIVATE(obj);
402 parent_priv->ui_manager = gtk_ui_manager_new();
404 action_group = gtk_action_group_new ("ModestMsgViewWindowActions");
405 gtk_action_group_set_translation_domain (action_group, GETTEXT_PACKAGE);
407 /* Add common actions */
408 gtk_action_group_add_actions (action_group,
409 modest_action_entries,
410 G_N_ELEMENTS (modest_action_entries),
412 gtk_action_group_add_toggle_actions (action_group,
413 msg_view_toggle_action_entries,
414 G_N_ELEMENTS (msg_view_toggle_action_entries),
417 gtk_ui_manager_insert_action_group (parent_priv->ui_manager, action_group, 0);
418 g_object_unref (action_group);
420 /* Load the UI definition */
421 gtk_ui_manager_add_ui_from_file (parent_priv->ui_manager, MODEST_UIDIR "modest-msg-view-window-ui.xml",
424 g_printerr ("modest: could not merge modest-msg-view-window-ui.xml: %s\n", error->message);
425 g_error_free (error);
430 /* Add accelerators */
431 gtk_window_add_accel_group (GTK_WINDOW (obj),
432 gtk_ui_manager_get_accel_group (parent_priv->ui_manager));
434 priv->is_search_result = FALSE;
435 priv->is_outbox = FALSE;
437 priv->msg_view = NULL;
438 priv->header_model = NULL;
439 priv->header_folder_id = NULL;
440 priv->clipboard_change_handler = 0;
441 priv->queue_change_handler = 0;
442 priv->account_removed_handler = 0;
443 priv->row_changed_handler = 0;
444 priv->row_deleted_handler = 0;
445 priv->row_inserted_handler = 0;
446 priv->rows_reordered_handler = 0;
447 priv->progress_hint = FALSE;
448 priv->fetching_images = 0;
450 priv->optimized_view = FALSE;
451 priv->purge_timeout = 0;
452 priv->remove_attachment_banner = NULL;
453 priv->msg_uid = NULL;
454 priv->other_body = NULL;
456 priv->sighandlers = NULL;
459 init_window (MODEST_MSG_VIEW_WINDOW(obj));
461 /* Set window icon */
462 window_icon = modest_platform_get_icon (MODEST_APP_MSG_VIEW_ICON, MODEST_ICON_SIZE_BIG);
464 gtk_window_set_icon (GTK_WINDOW (obj), window_icon);
465 g_object_unref (window_icon);
468 hildon_program_add_window (hildon_program_get_instance(),
475 set_toolbar_transfer_mode (ModestMsgViewWindow *self)
477 ModestMsgViewWindowPrivate *priv = NULL;
479 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self), FALSE);
481 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(self);
483 set_progress_hint (self, TRUE);
489 update_progress_hint (ModestMsgViewWindow *self)
491 ModestMsgViewWindowPrivate *priv;
492 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(self);
494 if (GTK_WIDGET_VISIBLE (self)) {
495 hildon_gtk_window_set_progress_indicator (GTK_WINDOW (self),
496 (priv->progress_hint || (priv->fetching_images > 0))?1:0);
501 set_progress_hint (ModestMsgViewWindow *self,
504 ModestWindowPrivate *parent_priv;
505 ModestMsgViewWindowPrivate *priv;
507 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self));
509 parent_priv = MODEST_WINDOW_GET_PRIVATE(self);
510 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(self);
512 /* Sets current progress hint */
513 priv->progress_hint = enabled;
515 update_progress_hint (self);
521 init_window (ModestMsgViewWindow *obj)
523 GtkWidget *main_vbox;
524 ModestMsgViewWindowPrivate *priv;
526 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(obj);
528 priv->msg_view = GTK_WIDGET (tny_platform_factory_new_msg_view (modest_tny_platform_factory_get_instance ()));
529 modest_msg_view_set_shadow_type (MODEST_MSG_VIEW (priv->msg_view), GTK_SHADOW_NONE);
530 main_vbox = gtk_vbox_new (FALSE, 6);
531 priv->main_scroll = hildon_pannable_area_new ();
532 gtk_container_add (GTK_CONTAINER (priv->main_scroll), priv->msg_view);
533 gtk_box_pack_start (GTK_BOX(main_vbox), priv->main_scroll, TRUE, TRUE, 0);
534 gtk_container_add (GTK_CONTAINER(obj), main_vbox);
536 /* NULL-ize fields if the window is destroyed */
537 g_signal_connect (priv->msg_view, "destroy", G_CALLBACK (gtk_widget_destroyed), &(priv->msg_view));
539 gtk_widget_show_all (GTK_WIDGET(main_vbox));
543 modest_msg_view_window_disconnect_signals (ModestWindow *self)
545 ModestMsgViewWindowPrivate *priv;
546 GtkWidget *header_view = NULL;
547 GtkWindow *parent_window = NULL;
549 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
551 if (gtk_clipboard_get (GDK_SELECTION_PRIMARY) &&
552 g_signal_handler_is_connected (gtk_clipboard_get (GDK_SELECTION_PRIMARY),
553 priv->clipboard_change_handler))
554 g_signal_handler_disconnect (gtk_clipboard_get (GDK_SELECTION_PRIMARY),
555 priv->clipboard_change_handler);
557 if (g_signal_handler_is_connected (G_OBJECT (modest_runtime_get_mail_operation_queue ()),
558 priv->queue_change_handler))
559 g_signal_handler_disconnect (G_OBJECT (modest_runtime_get_mail_operation_queue ()),
560 priv->queue_change_handler);
562 if (g_signal_handler_is_connected (G_OBJECT (modest_runtime_get_account_store ()),
563 priv->account_removed_handler))
564 g_signal_handler_disconnect (G_OBJECT (modest_runtime_get_account_store ()),
565 priv->account_removed_handler);
567 if (priv->header_model) {
568 if (g_signal_handler_is_connected(G_OBJECT (priv->header_model),
569 priv->row_changed_handler))
570 g_signal_handler_disconnect(G_OBJECT (priv->header_model),
571 priv->row_changed_handler);
573 if (g_signal_handler_is_connected(G_OBJECT (priv->header_model),
574 priv->row_deleted_handler))
575 g_signal_handler_disconnect(G_OBJECT (priv->header_model),
576 priv->row_deleted_handler);
578 if (g_signal_handler_is_connected(G_OBJECT (priv->header_model),
579 priv->row_inserted_handler))
580 g_signal_handler_disconnect(G_OBJECT (priv->header_model),
581 priv->row_inserted_handler);
583 if (g_signal_handler_is_connected(G_OBJECT (priv->header_model),
584 priv->rows_reordered_handler))
585 g_signal_handler_disconnect(G_OBJECT (priv->header_model),
586 priv->rows_reordered_handler);
589 modest_signal_mgr_disconnect_all_and_destroy (priv->sighandlers);
590 priv->sighandlers = NULL;
592 parent_window = gtk_window_get_transient_for (GTK_WINDOW (self));
593 if (parent_window && MODEST_IS_HEADER_WINDOW (parent_window)) {
594 header_view = GTK_WIDGET (modest_header_window_get_header_view (MODEST_HEADER_WINDOW (parent_window)));
596 modest_header_view_remove_observer(MODEST_HEADER_VIEW (header_view),
597 MODEST_HEADER_VIEW_OBSERVER(self));
603 modest_msg_view_window_finalize (GObject *obj)
605 ModestMsgViewWindowPrivate *priv;
607 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (obj);
609 /* Sanity check: shouldn't be needed, the window mgr should
610 call this function before */
611 modest_msg_view_window_disconnect_signals (MODEST_WINDOW (obj));
613 if (priv->other_body != NULL) {
614 g_object_unref (priv->other_body);
615 priv->other_body = NULL;
618 if (priv->header_model != NULL) {
619 g_object_unref (priv->header_model);
620 priv->header_model = NULL;
623 if (priv->remove_attachment_banner) {
624 gtk_widget_destroy (priv->remove_attachment_banner);
625 g_object_unref (priv->remove_attachment_banner);
626 priv->remove_attachment_banner = NULL;
629 if (priv->purge_timeout > 0) {
630 g_source_remove (priv->purge_timeout);
631 priv->purge_timeout = 0;
634 if (priv->row_reference) {
635 gtk_tree_row_reference_free (priv->row_reference);
636 priv->row_reference = NULL;
639 if (priv->next_row_reference) {
640 gtk_tree_row_reference_free (priv->next_row_reference);
641 priv->next_row_reference = NULL;
645 g_free (priv->msg_uid);
646 priv->msg_uid = NULL;
649 G_OBJECT_CLASS(parent_class)->finalize (obj);
653 select_next_valid_row (GtkTreeModel *model,
654 GtkTreeRowReference **row_reference,
658 GtkTreeIter tmp_iter;
660 GtkTreePath *next = NULL;
661 gboolean retval = FALSE, finished;
663 g_return_val_if_fail (gtk_tree_row_reference_valid (*row_reference), FALSE);
665 path = gtk_tree_row_reference_get_path (*row_reference);
666 gtk_tree_model_get_iter (model, &tmp_iter, path);
667 gtk_tree_row_reference_free (*row_reference);
668 *row_reference = NULL;
672 TnyHeader *header = NULL;
674 if (gtk_tree_model_iter_next (model, &tmp_iter)) {
675 gtk_tree_model_get (model, &tmp_iter,
676 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
680 if (msg_is_visible (header, is_outbox)) {
681 next = gtk_tree_model_get_path (model, &tmp_iter);
682 *row_reference = gtk_tree_row_reference_new (model, next);
683 gtk_tree_path_free (next);
687 g_object_unref (header);
690 } else if (cycle && gtk_tree_model_get_iter_first (model, &tmp_iter)) {
691 next = gtk_tree_model_get_path (model, &tmp_iter);
693 /* Ensure that we are not selecting the same */
694 if (gtk_tree_path_compare (path, next) != 0) {
695 gtk_tree_model_get (model, &tmp_iter,
696 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
699 if (msg_is_visible (header, is_outbox)) {
700 *row_reference = gtk_tree_row_reference_new (model, next);
704 g_object_unref (header);
708 /* If we ended up in the same message
709 then there is no valid next
713 gtk_tree_path_free (next);
715 /* If there are no more messages and we don't
716 want to start again in the first one then
717 there is no valid next message */
723 gtk_tree_path_free (path);
728 /* TODO: This should be in _init(), with the parameters as properties. */
730 modest_msg_view_window_construct (ModestMsgViewWindow *self,
731 const gchar *modest_account_name,
732 const gchar *mailbox,
733 const gchar *msg_uid)
736 ModestMsgViewWindowPrivate *priv = NULL;
737 ModestWindowPrivate *parent_priv = NULL;
738 ModestDimmingRulesGroup *toolbar_rules_group = NULL;
739 ModestDimmingRulesGroup *clipboard_rules_group = NULL;
741 obj = G_OBJECT (self);
742 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(obj);
743 parent_priv = MODEST_WINDOW_GET_PRIVATE(obj);
745 priv->msg_uid = g_strdup (msg_uid);
748 parent_priv->menubar = NULL;
750 toolbar_rules_group = modest_dimming_rules_group_new (MODEST_DIMMING_RULES_TOOLBAR, TRUE);
751 clipboard_rules_group = modest_dimming_rules_group_new (MODEST_DIMMING_RULES_CLIPBOARD, FALSE);
754 /* Add common dimming rules */
755 modest_dimming_rules_group_add_rules (toolbar_rules_group,
756 modest_msg_view_toolbar_dimming_entries,
757 G_N_ELEMENTS (modest_msg_view_toolbar_dimming_entries),
758 MODEST_WINDOW (self));
759 modest_dimming_rules_group_add_rules (clipboard_rules_group,
760 modest_msg_view_clipboard_dimming_entries,
761 G_N_ELEMENTS (modest_msg_view_clipboard_dimming_entries),
762 MODEST_WINDOW (self));
764 /* Insert dimming rules group for this window */
765 modest_ui_dimming_manager_insert_rules_group (parent_priv->ui_dimming_manager, toolbar_rules_group);
766 modest_ui_dimming_manager_insert_rules_group (parent_priv->ui_dimming_manager, clipboard_rules_group);
767 g_object_unref (toolbar_rules_group);
768 g_object_unref (clipboard_rules_group);
770 /* g_signal_connect (G_OBJECT(obj), "delete-event", G_CALLBACK(on_delete_event), obj); */
772 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);
773 g_signal_connect (G_OBJECT(priv->msg_view), "activate_link",
774 G_CALLBACK (modest_ui_actions_on_msg_link_clicked), obj);
775 g_signal_connect (G_OBJECT(priv->msg_view), "link_hover",
776 G_CALLBACK (modest_ui_actions_on_msg_link_hover), obj);
777 g_signal_connect (G_OBJECT(priv->msg_view), "attachment_clicked",
778 G_CALLBACK (modest_ui_actions_on_msg_attachment_clicked), obj);
779 g_signal_connect (G_OBJECT(priv->msg_view), "recpt_activated",
780 G_CALLBACK (modest_ui_actions_on_msg_recpt_activated), obj);
781 g_signal_connect (G_OBJECT(priv->msg_view), "show_details",
782 G_CALLBACK (modest_ui_actions_on_details), obj);
783 g_signal_connect (G_OBJECT(priv->msg_view), "link_contextual",
784 G_CALLBACK (modest_ui_actions_on_msg_link_contextual), obj);
785 g_signal_connect (G_OBJECT (priv->msg_view), "fetch_image",
786 G_CALLBACK (on_fetch_image), obj);
788 g_signal_connect (G_OBJECT (obj), "key-release-event",
789 G_CALLBACK (modest_msg_view_window_key_event),
792 g_signal_connect (G_OBJECT (obj), "key-press-event",
793 G_CALLBACK (modest_msg_view_window_key_event),
796 g_signal_connect (G_OBJECT (obj), "move-focus",
797 G_CALLBACK (on_move_focus), obj);
799 g_signal_connect (G_OBJECT (obj), "map-event",
800 G_CALLBACK (_modest_msg_view_window_map_event),
803 /* Mail Operation Queue */
804 priv->queue_change_handler = g_signal_connect (G_OBJECT (modest_runtime_get_mail_operation_queue ()),
806 G_CALLBACK (on_queue_changed),
809 /* Account manager */
810 priv->account_removed_handler = g_signal_connect (G_OBJECT (modest_runtime_get_account_store ()),
812 G_CALLBACK(on_account_removed),
815 modest_window_set_active_account (MODEST_WINDOW(obj), modest_account_name);
816 modest_window_set_active_mailbox (MODEST_WINDOW(obj), mailbox);
818 /* First add out toolbar ... */
819 modest_msg_view_window_show_toolbar (MODEST_WINDOW (obj), TRUE);
821 /* ... and later the find toolbar. This way find toolbar will
822 be shown over the other */
823 priv->find_toolbar = hildon_find_toolbar_new (NULL);
824 hildon_window_add_toolbar (HILDON_WINDOW (obj), GTK_TOOLBAR (priv->find_toolbar));
825 gtk_widget_set_no_show_all (priv->find_toolbar, TRUE);
826 g_signal_connect (G_OBJECT (priv->find_toolbar), "close",
827 G_CALLBACK (modest_msg_view_window_find_toolbar_close), obj);
828 g_signal_connect (G_OBJECT (priv->find_toolbar), "search",
829 G_CALLBACK (modest_msg_view_window_find_toolbar_search), obj);
830 priv->last_search = NULL;
832 /* Init the clipboard actions dim status */
833 modest_msg_view_grab_focus(MODEST_MSG_VIEW (priv->msg_view));
835 update_window_title (MODEST_MSG_VIEW_WINDOW (obj));
840 /* FIXME: parameter checks */
842 modest_msg_view_window_new_with_header_model (TnyMsg *msg,
843 const gchar *modest_account_name,
844 const gchar *mailbox,
845 const gchar *msg_uid,
847 GtkTreeRowReference *row_reference)
849 ModestMsgViewWindow *window = NULL;
850 ModestMsgViewWindowPrivate *priv = NULL;
851 TnyFolder *header_folder = NULL;
852 ModestHeaderView *header_view = NULL;
853 ModestWindowMgr *mgr = NULL;
856 modest_tny_mime_part_to_string (TNY_MIME_PART (msg), 0);
859 mgr = modest_runtime_get_window_mgr ();
860 window = MODEST_MSG_VIEW_WINDOW (modest_window_mgr_get_msg_view_window (mgr));
861 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), NULL);
863 modest_msg_view_window_construct (window, modest_account_name, mailbox, msg_uid);
865 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
867 /* Remember the message list's TreeModel so we can detect changes
868 * and change the list selection when necessary: */
869 header_folder = modest_header_view_get_folder (header_view);
871 priv->is_outbox = (modest_tny_folder_guess_folder_type (header_folder) ==
872 TNY_FOLDER_TYPE_OUTBOX);
873 priv->header_folder_id = tny_folder_get_id (header_folder);
874 g_object_unref(header_folder);
877 /* Setup row references and connect signals */
878 priv->header_model = g_object_ref (model);
881 priv->row_reference = gtk_tree_row_reference_copy (row_reference);
882 priv->next_row_reference = gtk_tree_row_reference_copy (row_reference);
883 select_next_valid_row (model, &(priv->next_row_reference), TRUE, priv->is_outbox);
885 priv->row_reference = NULL;
886 priv->next_row_reference = NULL;
889 /* Connect signals */
890 priv->row_changed_handler =
891 g_signal_connect (GTK_TREE_MODEL(model), "row-changed",
892 G_CALLBACK(modest_msg_view_window_on_row_changed),
894 priv->row_deleted_handler =
895 g_signal_connect (GTK_TREE_MODEL(model), "row-deleted",
896 G_CALLBACK(modest_msg_view_window_on_row_deleted),
898 priv->row_inserted_handler =
899 g_signal_connect (GTK_TREE_MODEL(model), "row-inserted",
900 G_CALLBACK(modest_msg_view_window_on_row_inserted),
902 priv->rows_reordered_handler =
903 g_signal_connect(GTK_TREE_MODEL(model), "rows-reordered",
904 G_CALLBACK(modest_msg_view_window_on_row_reordered),
907 if (header_view != NULL){
908 modest_header_view_add_observer(header_view,
909 MODEST_HEADER_VIEW_OBSERVER(window));
912 tny_msg_view_set_msg (TNY_MSG_VIEW (priv->msg_view), msg);
913 update_window_title (MODEST_MSG_VIEW_WINDOW (window));
914 update_branding (MODEST_MSG_VIEW_WINDOW (window));
916 /* gtk_widget_show_all (GTK_WIDGET (window)); */
917 modest_msg_view_window_update_priority (window);
918 /* Check dimming rules */
919 modest_ui_actions_check_toolbar_dimming_rules (MODEST_WINDOW (window));
920 modest_ui_actions_check_menu_dimming_rules (MODEST_WINDOW (window));
921 modest_window_check_dimming_rules_group (MODEST_WINDOW (window), MODEST_DIMMING_RULES_CLIPBOARD);
923 return MODEST_WINDOW(window);
927 modest_msg_view_window_new_from_uid (const gchar *modest_account_name,
928 const gchar *mailbox,
929 const gchar *msg_uid)
931 ModestMsgViewWindow *window = NULL;
932 ModestMsgViewWindowPrivate *priv = NULL;
933 ModestWindowMgr *mgr = NULL;
935 TnyAccount *account = NULL;
937 mgr = modest_runtime_get_window_mgr ();
938 window = MODEST_MSG_VIEW_WINDOW (modest_window_mgr_get_msg_view_window (mgr));
939 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), NULL);
941 modest_msg_view_window_construct (window, modest_account_name, mailbox, msg_uid);
943 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
947 is_merge = g_str_has_prefix (msg_uid, "merge:");
949 /* Get the account */
951 account = tny_account_store_find_account (TNY_ACCOUNT_STORE (modest_runtime_get_account_store ()),
955 if (is_merge || account) {
956 TnyFolder *folder = NULL;
958 /* Try to get the message, if it's already downloaded
959 we don't need to connect */
961 folder = modest_tny_folder_store_find_folder_from_uri (TNY_FOLDER_STORE (account), msg_uid);
963 ModestTnyAccountStore *account_store;
964 ModestTnyLocalFoldersAccount *local_folders_account;
966 account_store = modest_runtime_get_account_store ();
967 local_folders_account = MODEST_TNY_LOCAL_FOLDERS_ACCOUNT (
968 modest_tny_account_store_get_local_folders_account (account_store));
969 folder = modest_tny_local_folders_account_get_merged_outbox (local_folders_account);
970 g_object_unref (local_folders_account);
974 gboolean device_online;
976 device = modest_runtime_get_device();
977 device_online = tny_device_is_online (device);
979 message_reader (window, priv, NULL, msg_uid, folder, NULL);
981 TnyMsg *msg = tny_folder_find_msg (folder, msg_uid, NULL);
983 tny_msg_view_set_msg (TNY_MSG_VIEW (priv->msg_view), msg);
984 update_window_title (MODEST_MSG_VIEW_WINDOW (window));
985 update_branding (MODEST_MSG_VIEW_WINDOW (window));
986 g_object_unref (msg);
988 message_reader (window, priv, NULL, msg_uid, folder, NULL);
991 g_object_unref (folder);
996 /* Check dimming rules */
997 modest_ui_actions_check_toolbar_dimming_rules (MODEST_WINDOW (window));
998 modest_ui_actions_check_menu_dimming_rules (MODEST_WINDOW (window));
999 modest_window_check_dimming_rules_group (MODEST_WINDOW (window), MODEST_DIMMING_RULES_CLIPBOARD);
1001 return MODEST_WINDOW(window);
1005 modest_msg_view_window_new_from_header_view (ModestHeaderView *header_view,
1006 const gchar *modest_account_name,
1007 const gchar *mailbox,
1008 const gchar *msg_uid,
1009 GtkTreeRowReference *row_reference)
1011 ModestMsgViewWindow *window = NULL;
1012 ModestMsgViewWindowPrivate *priv = NULL;
1013 TnyFolder *header_folder = NULL;
1014 ModestWindowMgr *mgr = NULL;
1018 mgr = modest_runtime_get_window_mgr ();
1019 window = MODEST_MSG_VIEW_WINDOW (modest_window_mgr_get_msg_view_window (mgr));
1020 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), NULL);
1022 modest_msg_view_window_construct (window, modest_account_name, mailbox, msg_uid);
1024 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1026 /* Remember the message list's TreeModel so we can detect changes
1027 * and change the list selection when necessary: */
1029 if (header_view != NULL){
1030 header_folder = modest_header_view_get_folder(header_view);
1031 /* This could happen if the header folder was
1032 unseleted before opening this msg window (for
1033 example if the user selects an account in the
1034 folder view of the main window */
1035 if (header_folder) {
1036 priv->is_outbox = (modest_tny_folder_guess_folder_type (header_folder) ==
1037 TNY_FOLDER_TYPE_OUTBOX);
1038 priv->header_folder_id = tny_folder_get_id(header_folder);
1039 g_object_unref(header_folder);
1043 /* Setup row references and connect signals */
1044 priv->header_model = gtk_tree_view_get_model (GTK_TREE_VIEW (header_view));
1045 g_object_ref (priv->header_model);
1047 if (row_reference) {
1048 priv->row_reference = gtk_tree_row_reference_copy (row_reference);
1049 priv->next_row_reference = gtk_tree_row_reference_copy (row_reference);
1050 select_next_valid_row (priv->header_model, &(priv->next_row_reference), TRUE, priv->is_outbox);
1052 priv->row_reference = NULL;
1053 priv->next_row_reference = NULL;
1056 /* Connect signals */
1057 priv->row_changed_handler =
1058 g_signal_connect (GTK_TREE_MODEL(priv->header_model), "row-changed",
1059 G_CALLBACK(modest_msg_view_window_on_row_changed),
1061 priv->row_deleted_handler =
1062 g_signal_connect (GTK_TREE_MODEL(priv->header_model), "row-deleted",
1063 G_CALLBACK(modest_msg_view_window_on_row_deleted),
1065 priv->row_inserted_handler =
1066 g_signal_connect (GTK_TREE_MODEL(priv->header_model), "row-inserted",
1067 G_CALLBACK(modest_msg_view_window_on_row_inserted),
1069 priv->rows_reordered_handler =
1070 g_signal_connect(GTK_TREE_MODEL(priv->header_model), "rows-reordered",
1071 G_CALLBACK(modest_msg_view_window_on_row_reordered),
1074 if (header_view != NULL){
1075 modest_header_view_add_observer(header_view,
1076 MODEST_HEADER_VIEW_OBSERVER(window));
1079 tny_msg_view_set_msg (TNY_MSG_VIEW (priv->msg_view), NULL);
1080 update_branding (MODEST_MSG_VIEW_WINDOW (window));
1082 path = gtk_tree_row_reference_get_path (row_reference);
1083 if (gtk_tree_model_get_iter (priv->header_model, &iter, path)) {
1085 gtk_tree_model_get (priv->header_model, &iter,
1086 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
1088 message_reader (window, priv, header, NULL, NULL, row_reference);
1089 g_object_unref (header);
1091 gtk_tree_path_free (path);
1093 /* Check dimming rules */
1094 modest_ui_actions_check_toolbar_dimming_rules (MODEST_WINDOW (window));
1095 modest_ui_actions_check_menu_dimming_rules (MODEST_WINDOW (window));
1096 modest_window_check_dimming_rules_group (MODEST_WINDOW (window), MODEST_DIMMING_RULES_CLIPBOARD);
1098 return MODEST_WINDOW(window);
1102 modest_msg_view_window_new_for_search_result (TnyMsg *msg,
1103 const gchar *modest_account_name,
1104 const gchar *mailbox,
1105 const gchar *msg_uid)
1107 ModestMsgViewWindow *window = NULL;
1108 ModestMsgViewWindowPrivate *priv = NULL;
1109 ModestWindowMgr *mgr = NULL;
1111 mgr = modest_runtime_get_window_mgr ();
1112 window = MODEST_MSG_VIEW_WINDOW (modest_window_mgr_get_msg_view_window (mgr));
1113 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), NULL);
1114 modest_msg_view_window_construct (window, modest_account_name, mailbox, msg_uid);
1116 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1118 /* Remember that this is a search result,
1119 * so we can disable some UI appropriately: */
1120 priv->is_search_result = TRUE;
1122 tny_msg_view_set_msg (TNY_MSG_VIEW (priv->msg_view), msg);
1123 update_branding (MODEST_MSG_VIEW_WINDOW (window));
1125 update_window_title (window);
1126 /* gtk_widget_show_all (GTK_WIDGET (window));*/
1127 modest_msg_view_window_update_priority (window);
1129 /* Check dimming rules */
1130 modest_ui_actions_check_toolbar_dimming_rules (MODEST_WINDOW (window));
1131 modest_ui_actions_check_menu_dimming_rules (MODEST_WINDOW (window));
1132 modest_window_check_dimming_rules_group (MODEST_WINDOW (window), MODEST_DIMMING_RULES_CLIPBOARD);
1134 return MODEST_WINDOW(window);
1138 modest_msg_view_window_is_other_body (ModestMsgViewWindow *self)
1140 ModestMsgViewWindowPrivate *priv = NULL;
1142 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self), FALSE);
1143 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
1145 return (priv->other_body != NULL);
1149 modest_msg_view_window_new_with_other_body (TnyMsg *msg,
1150 TnyMimePart *other_body,
1151 const gchar *modest_account_name,
1152 const gchar *mailbox,
1153 const gchar *msg_uid)
1155 GObject *obj = NULL;
1156 ModestMsgViewWindowPrivate *priv;
1157 ModestWindowMgr *mgr = NULL;
1159 g_return_val_if_fail (msg, NULL);
1160 mgr = modest_runtime_get_window_mgr ();
1161 obj = G_OBJECT (modest_window_mgr_get_msg_view_window (mgr));
1162 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (obj);
1163 modest_msg_view_window_construct (MODEST_MSG_VIEW_WINDOW (obj),
1164 modest_account_name, mailbox, msg_uid);
1167 priv->other_body = g_object_ref (other_body);
1168 modest_msg_view_set_msg_with_other_body (MODEST_MSG_VIEW (priv->msg_view), msg, other_body);
1170 tny_msg_view_set_msg (TNY_MSG_VIEW (priv->msg_view), msg);
1172 update_window_title (MODEST_MSG_VIEW_WINDOW (obj));
1173 update_branding (MODEST_MSG_VIEW_WINDOW (obj));
1175 /* gtk_widget_show_all (GTK_WIDGET (obj)); */
1177 /* Check dimming rules */
1178 modest_ui_actions_check_toolbar_dimming_rules (MODEST_WINDOW (obj));
1179 modest_ui_actions_check_menu_dimming_rules (MODEST_WINDOW (obj));
1180 modest_window_check_dimming_rules_group (MODEST_WINDOW (obj), MODEST_DIMMING_RULES_CLIPBOARD);
1182 return MODEST_WINDOW(obj);
1186 modest_msg_view_window_new_for_attachment (TnyMsg *msg,
1187 const gchar *modest_account_name,
1188 const gchar *mailbox,
1189 const gchar *msg_uid)
1191 return modest_msg_view_window_new_with_other_body (msg, NULL, modest_account_name, mailbox, msg_uid);
1195 modest_msg_view_window_on_row_changed (GtkTreeModel *header_model,
1198 ModestMsgViewWindow *window)
1200 check_dimming_rules_after_change (window);
1204 modest_msg_view_window_on_row_deleted(GtkTreeModel *header_model,
1206 ModestMsgViewWindow *window)
1208 check_dimming_rules_after_change (window);
1210 /* The window could have dissapeared */
1213 check_dimming_rules_after_change (ModestMsgViewWindow *window)
1215 modest_ui_actions_check_menu_dimming_rules (MODEST_WINDOW (window));
1216 modest_ui_actions_check_toolbar_dimming_rules (MODEST_WINDOW (window));
1220 /* On insertions we check if the folder still has the message we are
1221 * showing or do not. If do not, we do nothing. Which means we are still
1222 * not attached to any header folder and thus next/prev buttons are
1223 * still dimmed. Once the message that is shown by msg-view is found, the
1224 * new model of header-view will be attached and the references will be set.
1225 * On each further insertions dimming rules will be checked. However
1226 * this requires extra CPU time at least works.
1227 * (An message might be deleted from TnyFolder and thus will not be
1228 * inserted into the model again for example if it is removed by the
1229 * imap server and the header view is refreshed.)
1232 modest_msg_view_window_on_row_inserted (GtkTreeModel *model,
1233 GtkTreePath *tree_path,
1234 GtkTreeIter *tree_iter,
1235 ModestMsgViewWindow *window)
1237 ModestMsgViewWindowPrivate *priv = NULL;
1238 TnyHeader *header = NULL;
1240 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window));
1241 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1243 g_assert (model == priv->header_model);
1245 /* Check if the newly inserted message is the same we are actually
1246 * showing. IF not, we should remain detached from the header model
1247 * and thus prev and next toolbar buttons should remain dimmed. */
1248 gtk_tree_model_get (model, tree_iter,
1249 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
1252 if (TNY_IS_HEADER (header)) {
1255 uid = modest_tny_folder_get_header_unique_id (header);
1256 if (!g_str_equal(priv->msg_uid, uid)) {
1257 check_dimming_rules_after_change (window);
1259 g_object_unref (G_OBJECT(header));
1263 g_object_unref(G_OBJECT(header));
1266 if (priv->row_reference) {
1267 gtk_tree_row_reference_free (priv->row_reference);
1270 /* Setup row_reference for the actual msg. */
1271 priv->row_reference = gtk_tree_row_reference_new (priv->header_model, tree_path);
1272 if (priv->row_reference == NULL) {
1273 g_warning("%s: No reference for msg header item.", __FUNCTION__);
1277 /* Now set up next_row_reference. */
1278 if (priv->next_row_reference) {
1279 gtk_tree_row_reference_free (priv->next_row_reference);
1282 priv->next_row_reference = gtk_tree_row_reference_copy (priv->row_reference);
1283 select_next_valid_row (priv->header_model,
1284 &(priv->next_row_reference), FALSE, priv->is_outbox);
1286 /* Connect the remaining callbacks to become able to detect
1287 * changes in header-view. */
1288 priv->row_changed_handler =
1289 g_signal_connect (priv->header_model, "row-changed",
1290 G_CALLBACK (modest_msg_view_window_on_row_changed),
1292 priv->row_deleted_handler =
1293 g_signal_connect (priv->header_model, "row-deleted",
1294 G_CALLBACK (modest_msg_view_window_on_row_deleted),
1296 priv->rows_reordered_handler =
1297 g_signal_connect (priv->header_model, "rows-reordered",
1298 G_CALLBACK (modest_msg_view_window_on_row_reordered),
1301 check_dimming_rules_after_change (window);
1305 modest_msg_view_window_on_row_reordered (GtkTreeModel *header_model,
1309 ModestMsgViewWindow *window)
1311 ModestMsgViewWindowPrivate *priv = NULL;
1312 gboolean already_changed = FALSE;
1314 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(window);
1316 /* If the current row was reordered select the proper next
1317 valid row. The same if the next row reference changes */
1318 if (priv->row_reference &&
1319 gtk_tree_row_reference_valid (priv->row_reference)) {
1321 path = gtk_tree_row_reference_get_path (priv->row_reference);
1322 if (gtk_tree_path_compare (path, arg1) == 0) {
1323 if (priv->next_row_reference) {
1324 gtk_tree_row_reference_free (priv->next_row_reference);
1326 priv->next_row_reference = gtk_tree_row_reference_copy (priv->row_reference);
1327 select_next_valid_row (header_model, &(priv->next_row_reference), FALSE, priv->is_outbox);
1328 already_changed = TRUE;
1330 gtk_tree_path_free (path);
1332 if (!already_changed &&
1333 priv->next_row_reference &&
1334 gtk_tree_row_reference_valid (priv->next_row_reference)) {
1336 path = gtk_tree_row_reference_get_path (priv->next_row_reference);
1337 if (gtk_tree_path_compare (path, arg1) == 0) {
1338 if (priv->next_row_reference) {
1339 gtk_tree_row_reference_free (priv->next_row_reference);
1341 priv->next_row_reference = gtk_tree_row_reference_copy (priv->row_reference);
1342 select_next_valid_row (header_model, &(priv->next_row_reference), FALSE, priv->is_outbox);
1344 gtk_tree_path_free (path);
1346 check_dimming_rules_after_change (window);
1349 /* The modest_msg_view_window_update_model_replaced implements update
1350 * function for ModestHeaderViewObserver. Checks whether the TnyFolder
1351 * actually belongs to the header-view is the same as the TnyFolder of
1352 * the message of msg-view or not. If they are different, there is
1353 * nothing to do. If they are the same, then the model has replaced and
1354 * the reference in msg-view shall be replaced from the old model to
1355 * the new model. In this case the view will be detached from it's
1356 * header folder. From this point the next/prev buttons are dimmed.
1359 modest_msg_view_window_update_model_replaced (ModestHeaderViewObserver *observer,
1360 GtkTreeModel *model,
1361 const gchar *tny_folder_id)
1363 ModestMsgViewWindowPrivate *priv = NULL;
1364 ModestMsgViewWindow *window = NULL;
1366 g_assert(MODEST_IS_HEADER_VIEW_OBSERVER(observer));
1367 g_assert(MODEST_IS_MSG_VIEW_WINDOW(observer));
1369 window = MODEST_MSG_VIEW_WINDOW(observer);
1370 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(window);
1372 /* If there is an other folder in the header-view then we do
1373 * not care about it's model (msg list). Else if the
1374 * header-view shows the folder the msg shown by us is in, we
1375 * shall replace our model reference and make some check. */
1376 if(model == NULL || tny_folder_id == NULL ||
1377 (priv->header_folder_id && !g_str_equal(tny_folder_id, priv->header_folder_id)))
1380 /* Model is changed(replaced), so we should forget the old
1381 * one. Because there might be other references and there
1382 * might be some change on the model even if we unreferenced
1383 * it, we need to disconnect our signals here. */
1384 if (priv->header_model) {
1385 if (g_signal_handler_is_connected(G_OBJECT (priv->header_model),
1386 priv->row_changed_handler))
1387 g_signal_handler_disconnect(G_OBJECT (priv->header_model),
1388 priv->row_changed_handler);
1389 if (g_signal_handler_is_connected(G_OBJECT (priv->header_model),
1390 priv->row_deleted_handler))
1391 g_signal_handler_disconnect(G_OBJECT (priv->header_model),
1392 priv->row_deleted_handler);
1393 if (g_signal_handler_is_connected(G_OBJECT (priv->header_model),
1394 priv->row_inserted_handler))
1395 g_signal_handler_disconnect(G_OBJECT (priv->header_model),
1396 priv->row_inserted_handler);
1397 if (g_signal_handler_is_connected(G_OBJECT (priv->header_model),
1398 priv->rows_reordered_handler))
1399 g_signal_handler_disconnect(G_OBJECT (priv->header_model),
1400 priv->rows_reordered_handler);
1403 if (priv->row_reference)
1404 gtk_tree_row_reference_free (priv->row_reference);
1405 if (priv->next_row_reference)
1406 gtk_tree_row_reference_free (priv->next_row_reference);
1407 g_object_unref(priv->header_model);
1410 priv->row_changed_handler = 0;
1411 priv->row_deleted_handler = 0;
1412 priv->row_inserted_handler = 0;
1413 priv->rows_reordered_handler = 0;
1414 priv->next_row_reference = NULL;
1415 priv->row_reference = NULL;
1416 priv->header_model = NULL;
1419 priv->header_model = g_object_ref (model);
1421 /* Also we must connect to the new model for row insertions.
1422 * Only for insertions now. We will need other ones only after
1423 * the msg is show by msg-view is added to the new model. */
1424 priv->row_inserted_handler =
1425 g_signal_connect (priv->header_model, "row-inserted",
1426 G_CALLBACK(modest_msg_view_window_on_row_inserted),
1429 modest_ui_actions_check_menu_dimming_rules(MODEST_WINDOW(window));
1430 modest_ui_actions_check_toolbar_dimming_rules(MODEST_WINDOW(window));
1434 modest_msg_view_window_toolbar_on_transfer_mode (ModestMsgViewWindow *self)
1436 ModestMsgViewWindowPrivate *priv= NULL;
1438 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self), FALSE);
1439 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
1441 return priv->progress_hint;
1445 modest_msg_view_window_get_header (ModestMsgViewWindow *self)
1447 ModestMsgViewWindowPrivate *priv= NULL;
1449 TnyHeader *header = NULL;
1450 GtkTreePath *path = NULL;
1453 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self), NULL);
1454 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
1456 /* If the message was not obtained from a treemodel,
1457 * for instance if it was opened directly by the search UI:
1459 if (priv->header_model == NULL ||
1460 priv->row_reference == NULL ||
1461 !gtk_tree_row_reference_valid (priv->row_reference)) {
1462 msg = modest_msg_view_window_get_message (self);
1464 header = tny_msg_get_header (msg);
1465 g_object_unref (msg);
1470 /* Get iter of the currently selected message in the header view: */
1471 path = gtk_tree_row_reference_get_path (priv->row_reference);
1472 g_return_val_if_fail (path != NULL, NULL);
1473 gtk_tree_model_get_iter (priv->header_model,
1477 /* Get current message header */
1478 gtk_tree_model_get (priv->header_model, &iter,
1479 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
1482 gtk_tree_path_free (path);
1487 modest_msg_view_window_get_message (ModestMsgViewWindow *self)
1489 ModestMsgViewWindowPrivate *priv;
1491 g_return_val_if_fail (self, NULL);
1493 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(self);
1495 return tny_msg_view_get_msg (TNY_MSG_VIEW (priv->msg_view));
1499 modest_msg_view_window_get_message_uid (ModestMsgViewWindow *self)
1501 ModestMsgViewWindowPrivate *priv;
1503 g_return_val_if_fail (self, NULL);
1505 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
1507 return (const gchar*) priv->msg_uid;
1510 /* Used for the Ctrl+F accelerator */
1512 modest_msg_view_window_toggle_find_toolbar (GtkWidget *obj,
1515 ModestMsgViewWindow *window = MODEST_MSG_VIEW_WINDOW (data);
1516 ModestMsgViewWindowPrivate *priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1518 if (GTK_WIDGET_VISIBLE (priv->find_toolbar)) {
1519 modest_msg_view_window_find_toolbar_close (obj, data);
1521 modest_msg_view_window_show_find_toolbar (obj, data);
1525 /* Handler for menu option */
1527 modest_msg_view_window_show_find_toolbar (GtkWidget *obj,
1530 ModestMsgViewWindow *window = MODEST_MSG_VIEW_WINDOW (data);
1531 ModestMsgViewWindowPrivate *priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1533 gtk_widget_show (priv->find_toolbar);
1534 hildon_find_toolbar_highlight_entry (HILDON_FIND_TOOLBAR (priv->find_toolbar), TRUE);
1537 /* Handler for click on the "X" close button in find toolbar */
1539 modest_msg_view_window_find_toolbar_close (GtkWidget *widget,
1540 ModestMsgViewWindow *obj)
1542 ModestMsgViewWindowPrivate *priv;
1544 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (obj);
1547 gtk_widget_hide (priv->find_toolbar);
1548 modest_msg_view_grab_focus (MODEST_MSG_VIEW (priv->msg_view));
1552 modest_msg_view_window_find_toolbar_search (GtkWidget *widget,
1553 ModestMsgViewWindow *obj)
1555 gchar *current_search;
1556 ModestMsgViewWindowPrivate *priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (obj);
1558 if (modest_mime_part_view_is_empty (MODEST_MIME_PART_VIEW (priv->msg_view))) {
1559 hildon_banner_show_information (NULL, NULL, _("mail_ib_nothing_to_find"));
1563 g_object_get (G_OBJECT (widget), "prefix", ¤t_search, NULL);
1565 if ((current_search == NULL) || (strcmp (current_search, "") == 0)) {
1566 g_free (current_search);
1567 hildon_banner_show_information (NULL, NULL, _CS("ecdg_ib_find_rep_enter_text"));
1571 if ((priv->last_search == NULL) || (strcmp (priv->last_search, current_search) != 0)) {
1573 g_free (priv->last_search);
1574 priv->last_search = g_strdup (current_search);
1575 result = modest_isearch_view_search (MODEST_ISEARCH_VIEW (priv->msg_view),
1578 hildon_banner_show_information (NULL, NULL,
1579 _HL("ckct_ib_find_no_matches"));
1580 g_free (priv->last_search);
1581 priv->last_search = NULL;
1583 hildon_find_toolbar_highlight_entry (HILDON_FIND_TOOLBAR (priv->find_toolbar), TRUE);
1586 if (!modest_isearch_view_search_next (MODEST_ISEARCH_VIEW (priv->msg_view))) {
1587 hildon_banner_show_information (NULL, NULL,
1588 _HL("ckct_ib_find_search_complete"));
1589 g_free (priv->last_search);
1590 priv->last_search = NULL;
1592 hildon_find_toolbar_highlight_entry (HILDON_FIND_TOOLBAR (priv->find_toolbar), TRUE);
1596 g_free (current_search);
1601 modest_msg_view_window_set_zoom (ModestWindow *window,
1604 ModestMsgViewWindowPrivate *priv;
1605 ModestWindowPrivate *parent_priv;
1607 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window));
1609 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1610 parent_priv = MODEST_WINDOW_GET_PRIVATE (window);
1611 modest_zoomable_set_zoom (MODEST_ZOOMABLE (priv->msg_view), zoom);
1616 modest_msg_view_window_get_zoom (ModestWindow *window)
1618 ModestMsgViewWindowPrivate *priv;
1620 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), 1.0);
1622 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1623 return modest_zoomable_get_zoom (MODEST_ZOOMABLE (priv->msg_view));
1627 modest_msg_view_window_zoom_plus (ModestWindow *window)
1630 ModestMsgViewWindowPrivate *priv;
1634 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), 1.0);
1635 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1637 zoom_level = modest_zoomable_get_zoom (MODEST_ZOOMABLE (priv->msg_view));
1639 if (zoom_level >= 2.0) {
1640 hildon_banner_show_information (NULL, NULL,
1641 _CS("ckct_ib_max_zoom_level_reached"));
1643 } else if (zoom_level >= 1.5) {
1645 } else if (zoom_level >= 1.2) {
1647 } else if (zoom_level >= 1.0) {
1649 } else if (zoom_level >= 0.8) {
1651 } else if (zoom_level >= 0.5) {
1657 /* set zoom level */
1658 int_zoom = (gint) rint (zoom_level*100.0+0.1);
1659 banner_text = g_strdup_printf (_HL("wdgt_ib_zoom"), int_zoom);
1660 modest_platform_information_banner (GTK_WIDGET (window), NULL, banner_text);
1661 g_free (banner_text);
1662 modest_zoomable_set_zoom (MODEST_ZOOMABLE (priv->msg_view), zoom_level);
1668 modest_msg_view_window_zoom_minus (ModestWindow *window)
1671 ModestMsgViewWindowPrivate *priv;
1675 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), 1.0);
1676 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1678 zoom_level = modest_zoomable_get_zoom (MODEST_ZOOMABLE (priv->msg_view));
1680 if (zoom_level <= 0.5) {
1681 hildon_banner_show_information (NULL, NULL,
1682 _CS("ckct_ib_min_zoom_level_reached"));
1684 } else if (zoom_level <= 0.8) {
1686 } else if (zoom_level <= 1.0) {
1688 } else if (zoom_level <= 1.2) {
1690 } else if (zoom_level <= 1.5) {
1692 } else if (zoom_level <= 2.0) {
1698 /* set zoom level */
1699 int_zoom = (gint) rint (zoom_level*100.0+0.1);
1700 banner_text = g_strdup_printf (_HL("wdgt_ib_zoom"), int_zoom);
1701 modest_platform_information_banner (GTK_WIDGET (window), NULL, banner_text);
1702 g_free (banner_text);
1703 modest_zoomable_set_zoom (MODEST_ZOOMABLE (priv->msg_view), zoom_level);
1710 modest_msg_view_window_key_event (GtkWidget *window,
1716 focus = gtk_window_get_focus (GTK_WINDOW (window));
1718 /* for the find toolbar case */
1719 if (focus && GTK_IS_ENTRY (focus)) {
1720 if (event->keyval == GDK_BackSpace) {
1722 copy = gdk_event_copy ((GdkEvent *) event);
1723 gtk_widget_event (focus, copy);
1724 gdk_event_free (copy);
1729 if (event->keyval == GDK_Up || event->keyval == GDK_KP_Up ||
1730 event->keyval == GDK_Down || event->keyval == GDK_KP_Down ||
1731 event->keyval == GDK_Page_Up || event->keyval == GDK_KP_Page_Up ||
1732 event->keyval == GDK_Page_Down || event->keyval == GDK_KP_Page_Down ||
1733 event->keyval == GDK_Home || event->keyval == GDK_KP_Home ||
1734 event->keyval == GDK_End || event->keyval == GDK_KP_End) {
1735 /* ModestMsgViewWindowPrivate *priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window); */
1736 /* gboolean return_value; */
1738 if (event->type == GDK_KEY_PRESS) {
1739 GtkScrollType scroll_type;
1741 switch (event->keyval) {
1744 scroll_type = GTK_SCROLL_STEP_UP; break;
1747 scroll_type = GTK_SCROLL_STEP_DOWN; break;
1749 case GDK_KP_Page_Up:
1750 scroll_type = GTK_SCROLL_PAGE_UP; break;
1752 case GDK_KP_Page_Down:
1753 scroll_type = GTK_SCROLL_PAGE_DOWN; break;
1756 scroll_type = GTK_SCROLL_START; break;
1759 scroll_type = GTK_SCROLL_END; break;
1760 default: scroll_type = GTK_SCROLL_NONE;
1763 /* g_signal_emit_by_name (G_OBJECT (priv->main_scroll), "scroll-child", */
1764 /* scroll_type, FALSE, &return_value); */
1775 modest_msg_view_window_last_message_selected (ModestMsgViewWindow *window)
1778 ModestMsgViewWindowPrivate *priv;
1779 GtkTreeIter tmp_iter;
1780 gboolean is_last_selected;
1782 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), TRUE);
1783 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1785 /*if no model (so no rows at all), then virtually we are the last*/
1786 if (!priv->header_model || !priv->row_reference)
1789 if (!gtk_tree_row_reference_valid (priv->row_reference))
1792 path = gtk_tree_row_reference_get_path (priv->row_reference);
1796 is_last_selected = TRUE;
1797 while (is_last_selected) {
1799 gtk_tree_path_next (path);
1800 if (!gtk_tree_model_get_iter (priv->header_model, &tmp_iter, path))
1802 gtk_tree_model_get (priv->header_model, &tmp_iter,
1803 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
1806 if (msg_is_visible (header, priv->is_outbox))
1807 is_last_selected = FALSE;
1808 g_object_unref(G_OBJECT(header));
1811 gtk_tree_path_free (path);
1812 return is_last_selected;
1816 modest_msg_view_window_has_headers_model (ModestMsgViewWindow *window)
1818 ModestMsgViewWindowPrivate *priv;
1820 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), TRUE);
1821 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1823 return priv->header_model != NULL;
1827 modest_msg_view_window_is_search_result (ModestMsgViewWindow *window)
1829 ModestMsgViewWindowPrivate *priv;
1831 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), TRUE);
1832 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1834 return priv->is_search_result;
1838 msg_is_visible (TnyHeader *header, gboolean check_outbox)
1840 if ((tny_header_get_flags(header) & TNY_HEADER_FLAG_DELETED))
1842 if (!check_outbox) {
1845 ModestTnySendQueueStatus status;
1846 status = modest_tny_all_send_queues_get_msg_status (header);
1847 return ((status != MODEST_TNY_SEND_QUEUE_FAILED) &&
1848 (status != MODEST_TNY_SEND_QUEUE_SENDING));
1853 modest_msg_view_window_first_message_selected (ModestMsgViewWindow *window)
1856 ModestMsgViewWindowPrivate *priv;
1857 gboolean is_first_selected;
1858 GtkTreeIter tmp_iter;
1860 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), TRUE);
1861 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1863 /*if no model (so no rows at all), then virtually we are the first*/
1864 if (!priv->header_model || !priv->row_reference)
1867 if (!gtk_tree_row_reference_valid (priv->row_reference))
1870 path = gtk_tree_row_reference_get_path (priv->row_reference);
1874 is_first_selected = TRUE;
1875 while (is_first_selected) {
1877 if(!gtk_tree_path_prev (path))
1879 /* Here the 'if' is needless for logic, but let make sure
1880 * iter is valid for gtk_tree_model_get. */
1881 if (!gtk_tree_model_get_iter (priv->header_model, &tmp_iter, path))
1883 gtk_tree_model_get (priv->header_model, &tmp_iter,
1884 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
1887 if (msg_is_visible (header, priv->is_outbox))
1888 is_first_selected = FALSE;
1889 g_object_unref(G_OBJECT(header));
1892 gtk_tree_path_free (path);
1893 return is_first_selected;
1900 GtkTreeRowReference *row_reference;
1904 message_reader_performer (gboolean canceled,
1906 GtkWindow *parent_window,
1907 TnyAccount *account,
1910 ModestMailOperation *mail_op = NULL;
1911 MsgReaderInfo *info;
1913 info = (MsgReaderInfo *) user_data;
1914 if (canceled || err) {
1915 update_window_title (MODEST_MSG_VIEW_WINDOW (parent_window));
1919 /* Register the header - it'll be unregistered in the callback */
1921 modest_window_mgr_register_header (modest_runtime_get_window_mgr (), info->header, NULL);
1923 /* New mail operation */
1924 mail_op = modest_mail_operation_new_with_error_handling (G_OBJECT(parent_window),
1925 modest_ui_actions_disk_operations_error_handler,
1928 modest_mail_operation_queue_add (modest_runtime_get_mail_operation_queue (), mail_op);
1930 modest_mail_operation_get_msg (mail_op, info->header, TRUE, view_msg_cb, info->row_reference);
1932 modest_mail_operation_find_msg (mail_op, info->folder, info->msg_uid, TRUE, view_msg_cb, NULL);
1933 g_object_unref (mail_op);
1935 /* Update dimming rules */
1936 modest_ui_actions_check_toolbar_dimming_rules (MODEST_WINDOW (parent_window));
1937 modest_ui_actions_check_menu_dimming_rules (MODEST_WINDOW (parent_window));
1940 /* Frees. The row_reference will be freed by the view_msg_cb callback */
1941 g_free (info->msg_uid);
1943 g_object_unref (info->folder);
1945 g_object_unref (info->header);
1946 g_slice_free (MsgReaderInfo, info);
1951 * Reads the message whose summary item is @header. It takes care of
1952 * several things, among others:
1954 * If the message was not previously downloaded then ask the user
1955 * before downloading. If there is no connection launch the connection
1956 * dialog. Update toolbar dimming rules.
1958 * Returns: TRUE if the mail operation was started, otherwise if the
1959 * user do not want to download the message, or if the user do not
1960 * want to connect, then the operation is not issued
1963 message_reader (ModestMsgViewWindow *window,
1964 ModestMsgViewWindowPrivate *priv,
1966 const gchar *msg_uid,
1968 GtkTreeRowReference *row_reference)
1970 ModestWindowMgr *mgr;
1971 TnyAccount *account;
1972 MsgReaderInfo *info;
1974 /* We set the header from model while we're loading */
1975 tny_header_view_set_header (TNY_HEADER_VIEW (priv->msg_view), header);
1976 gtk_window_set_title (GTK_WINDOW (window), _CS("ckdg_pb_updating"));
1979 g_object_ref (folder);
1981 mgr = modest_runtime_get_window_mgr ();
1982 /* Msg download completed */
1983 if (!header || !(tny_header_get_flags (header) & TNY_HEADER_FLAG_CACHED)) {
1985 /* Ask the user if he wants to download the message if
1987 if (!tny_device_is_online (modest_runtime_get_device())) {
1988 GtkResponseType response;
1990 response = modest_platform_run_confirmation_dialog (GTK_WINDOW (window),
1991 _("mcen_nc_get_msg"));
1992 if (response == GTK_RESPONSE_CANCEL) {
1993 update_window_title (window);
1998 folder = tny_header_get_folder (header);
2000 info = g_slice_new (MsgReaderInfo);
2001 info->msg_uid = g_strdup (msg_uid);
2003 info->header = g_object_ref (header);
2005 info->header = NULL;
2007 info->folder = g_object_ref (folder);
2009 info->folder = NULL;
2010 if (row_reference) {
2011 info->row_reference = gtk_tree_row_reference_copy (row_reference);
2013 info->row_reference = NULL;
2016 /* Offer the connection dialog if necessary */
2017 modest_platform_connect_if_remote_and_perform ((GtkWindow *) window,
2019 TNY_FOLDER_STORE (folder),
2020 message_reader_performer,
2022 g_object_unref (folder);
2028 folder = tny_header_get_folder (header);
2030 account = tny_folder_get_account (folder);
2031 info = g_slice_new (MsgReaderInfo);
2032 info->msg_uid = g_strdup (msg_uid);
2034 info->folder = g_object_ref (folder);
2036 info->folder = NULL;
2038 info->header = g_object_ref (header);
2040 info->header = NULL;
2042 info->row_reference = gtk_tree_row_reference_copy (row_reference);
2044 info->row_reference = NULL;
2046 message_reader_performer (FALSE, NULL, (GtkWindow *) window, account, info);
2047 g_object_unref (account);
2048 g_object_unref (folder);
2054 modest_msg_view_window_select_next_message (ModestMsgViewWindow *window)
2056 ModestMsgViewWindowPrivate *priv;
2057 GtkTreePath *path= NULL;
2058 GtkTreeIter tmp_iter;
2060 gboolean retval = TRUE;
2061 GtkTreeRowReference *row_reference = NULL;
2063 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), FALSE);
2064 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
2066 if (!priv->row_reference)
2069 /* Update the next row reference if it's not valid. This could
2070 happen if for example the header which it was pointing to,
2071 was deleted. The best place to do it is in the row-deleted
2072 handler but the tinymail model do not work like the glib
2073 tree models and reports the deletion when the row is still
2075 if (!gtk_tree_row_reference_valid (priv->next_row_reference)) {
2076 if (gtk_tree_row_reference_valid (priv->row_reference)) {
2077 priv->next_row_reference = gtk_tree_row_reference_copy (priv->row_reference);
2078 select_next_valid_row (priv->header_model, &(priv->next_row_reference), FALSE, priv->is_outbox);
2081 if (priv->next_row_reference)
2082 path = gtk_tree_row_reference_get_path (priv->next_row_reference);
2086 row_reference = gtk_tree_row_reference_copy (priv->next_row_reference);
2088 gtk_tree_model_get_iter (priv->header_model,
2091 gtk_tree_path_free (path);
2093 gtk_tree_model_get (priv->header_model, &tmp_iter,
2094 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
2097 /* Read the message & show it */
2098 if (!message_reader (window, priv, header, NULL, NULL, row_reference)) {
2101 gtk_tree_row_reference_free (row_reference);
2104 g_object_unref (header);
2110 modest_msg_view_window_select_previous_message (ModestMsgViewWindow *window)
2112 ModestMsgViewWindowPrivate *priv = NULL;
2114 gboolean finished = FALSE;
2115 gboolean retval = FALSE;
2117 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), FALSE);
2118 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
2120 /* Return inmediatly if there is no header model */
2121 if (!priv->header_model || !priv->row_reference)
2124 path = gtk_tree_row_reference_get_path (priv->row_reference);
2125 while (!finished && gtk_tree_path_prev (path)) {
2129 gtk_tree_model_get_iter (priv->header_model, &iter, path);
2130 gtk_tree_model_get (priv->header_model, &iter,
2131 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
2135 if (msg_is_visible (header, priv->is_outbox)) {
2136 GtkTreeRowReference *row_reference;
2137 row_reference = gtk_tree_row_reference_new (priv->header_model, path);
2138 /* Read the message & show it */
2139 retval = message_reader (window, priv, header, NULL, NULL, row_reference);
2140 gtk_tree_row_reference_free (row_reference);
2144 g_object_unref (header);
2148 gtk_tree_path_free (path);
2153 view_msg_cb (ModestMailOperation *mail_op,
2160 ModestMsgViewWindow *self = NULL;
2161 ModestMsgViewWindowPrivate *priv = NULL;
2162 GtkTreeRowReference *row_reference = NULL;
2164 /* Unregister the header (it was registered before creating the mail operation) */
2165 modest_window_mgr_unregister_header (modest_runtime_get_window_mgr (), header);
2167 row_reference = (GtkTreeRowReference *) user_data;
2170 gtk_tree_row_reference_free (row_reference);
2171 self = (ModestMsgViewWindow *) modest_mail_operation_get_source (mail_op);
2173 /* Restore window title */
2174 update_window_title (self);
2175 g_object_unref (self);
2180 /* If there was any error */
2181 if (!modest_ui_actions_msg_retrieval_check (mail_op, header, msg)) {
2183 gtk_tree_row_reference_free (row_reference);
2184 self = (ModestMsgViewWindow *) modest_mail_operation_get_source (mail_op);
2186 /* Restore window title */
2187 update_window_title (self);
2188 g_object_unref (self);
2193 /* Get the window */
2194 self = (ModestMsgViewWindow *) modest_mail_operation_get_source (mail_op);
2195 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self));
2196 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
2198 /* Update the row reference */
2199 if (priv->row_reference != NULL) {
2200 gtk_tree_row_reference_free (priv->row_reference);
2201 priv->row_reference = gtk_tree_row_reference_copy (row_reference);
2202 if (priv->next_row_reference != NULL) {
2203 gtk_tree_row_reference_free (priv->next_row_reference);
2205 priv->next_row_reference = gtk_tree_row_reference_copy (priv->row_reference);
2206 select_next_valid_row (priv->header_model, &(priv->next_row_reference), TRUE, priv->is_outbox);
2209 /* Mark header as read */
2210 if (!(tny_header_get_flags (header) & TNY_HEADER_FLAG_SEEN))
2211 tny_header_set_flag (header, TNY_HEADER_FLAG_SEEN);
2213 /* Set new message */
2214 if (priv->msg_view != NULL && TNY_IS_MSG_VIEW (priv->msg_view)) {
2215 tny_msg_view_set_msg (TNY_MSG_VIEW (priv->msg_view), msg);
2216 modest_msg_view_window_update_priority (self);
2217 update_window_title (MODEST_MSG_VIEW_WINDOW (self));
2218 update_branding (MODEST_MSG_VIEW_WINDOW (self));
2219 modest_msg_view_grab_focus (MODEST_MSG_VIEW (priv->msg_view));
2222 /* Set the new message uid of the window */
2223 if (priv->msg_uid) {
2224 g_free (priv->msg_uid);
2225 priv->msg_uid = modest_tny_folder_get_header_unique_id (header);
2228 /* Notify the observers */
2229 g_signal_emit (G_OBJECT (self), signals[MSG_CHANGED_SIGNAL],
2230 0, priv->header_model, priv->row_reference);
2233 g_object_unref (self);
2235 gtk_tree_row_reference_free (row_reference);
2239 modest_msg_view_window_get_folder_type (ModestMsgViewWindow *window)
2241 ModestMsgViewWindowPrivate *priv;
2243 TnyFolderType folder_type;
2245 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
2247 folder_type = TNY_FOLDER_TYPE_UNKNOWN;
2249 msg = tny_msg_view_get_msg (TNY_MSG_VIEW (priv->msg_view));
2253 folder = tny_msg_get_folder (msg);
2255 folder_type = modest_tny_folder_guess_folder_type (folder);
2256 g_object_unref (folder);
2258 g_object_unref (msg);
2266 modest_msg_view_window_update_priority (ModestMsgViewWindow *window)
2268 ModestMsgViewWindowPrivate *priv;
2269 TnyHeader *header = NULL;
2270 TnyHeaderFlags flags = 0;
2272 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
2274 if (priv->header_model && priv->row_reference) {
2276 GtkTreePath *path = NULL;
2278 path = gtk_tree_row_reference_get_path (priv->row_reference);
2279 g_return_if_fail (path != NULL);
2280 gtk_tree_model_get_iter (priv->header_model,
2282 gtk_tree_row_reference_get_path (priv->row_reference));
2284 gtk_tree_model_get (priv->header_model, &iter, TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
2286 gtk_tree_path_free (path);
2289 msg = tny_msg_view_get_msg (TNY_MSG_VIEW (priv->msg_view));
2291 header = tny_msg_get_header (msg);
2292 g_object_unref (msg);
2297 flags = tny_header_get_flags (header);
2298 g_object_unref(G_OBJECT(header));
2301 modest_msg_view_set_priority (MODEST_MSG_VIEW(priv->msg_view), flags);
2306 toolbar_resize (ModestMsgViewWindow *self)
2308 ModestMsgViewWindowPrivate *priv = NULL;
2309 ModestWindowPrivate *parent_priv = NULL;
2311 gint static_button_size;
2312 ModestWindowMgr *mgr;
2314 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self));
2315 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
2316 parent_priv = MODEST_WINDOW_GET_PRIVATE(self);
2318 mgr = modest_runtime_get_window_mgr ();
2319 static_button_size = modest_window_mgr_get_fullscreen_mode (mgr)?120:120;
2321 if (parent_priv->toolbar) {
2322 /* left size buttons */
2323 widget = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/ToolbarMessageReply");
2324 gtk_tool_item_set_expand (GTK_TOOL_ITEM (widget), FALSE);
2325 gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (widget), FALSE);
2326 gtk_widget_set_size_request (GTK_WIDGET (widget), static_button_size, -1);
2327 widget = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/ToolbarMessageMoveTo");
2328 gtk_tool_item_set_expand (GTK_TOOL_ITEM (widget), FALSE);
2329 gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (widget), FALSE);
2330 gtk_widget_set_size_request (GTK_WIDGET (widget), static_button_size, -1);
2331 widget = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/ToolbarDeleteMessage");
2332 gtk_tool_item_set_expand (GTK_TOOL_ITEM (widget), FALSE);
2333 gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (widget), FALSE);
2334 gtk_widget_set_size_request (GTK_WIDGET (widget), static_button_size, -1);
2335 widget = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/FindInMessage");
2336 gtk_tool_item_set_expand (GTK_TOOL_ITEM (widget), FALSE);
2337 gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (widget), FALSE);
2338 gtk_widget_set_size_request (GTK_WIDGET (widget), static_button_size, -1);
2340 gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (priv->next_toolitem), TRUE);
2341 gtk_tool_item_set_expand (GTK_TOOL_ITEM (priv->next_toolitem), TRUE);
2342 gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (priv->prev_toolitem), TRUE);
2343 gtk_tool_item_set_expand (GTK_TOOL_ITEM (priv->prev_toolitem), TRUE);
2348 modest_msg_view_window_show_toolbar (ModestWindow *self,
2349 gboolean show_toolbar)
2351 ModestMsgViewWindowPrivate *priv = NULL;
2352 ModestWindowPrivate *parent_priv;
2354 parent_priv = MODEST_WINDOW_GET_PRIVATE(self);
2355 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(self);
2357 /* Set optimized view status */
2358 priv->optimized_view = !show_toolbar;
2360 if (!parent_priv->toolbar) {
2361 parent_priv->toolbar = gtk_ui_manager_get_widget (parent_priv->ui_manager,
2363 gtk_toolbar_set_icon_size (GTK_TOOLBAR (parent_priv->toolbar), HILDON_ICON_SIZE_FINGER);
2364 gtk_widget_set_no_show_all (parent_priv->toolbar, TRUE);
2366 priv->next_toolitem = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/ToolbarMessageNext");
2367 priv->prev_toolitem = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/ToolbarMessageBack");
2368 toolbar_resize (MODEST_MSG_VIEW_WINDOW (self));
2371 hildon_window_add_toolbar (HILDON_WINDOW (self),
2372 GTK_TOOLBAR (parent_priv->toolbar));
2377 /* Quick hack: this prevents toolbar icons "dance" when progress bar show status is changed */
2378 /* TODO: resize mode migth be GTK_RESIZE_QUEUE, in order to avoid unneccesary shows */
2379 gtk_container_set_resize_mode (GTK_CONTAINER(parent_priv->toolbar), GTK_RESIZE_IMMEDIATE);
2381 gtk_widget_show (GTK_WIDGET (parent_priv->toolbar));
2382 if (modest_msg_view_window_transfer_mode_enabled (MODEST_MSG_VIEW_WINDOW (self)))
2383 set_progress_hint (MODEST_MSG_VIEW_WINDOW (self), TRUE);
2385 set_progress_hint (MODEST_MSG_VIEW_WINDOW (self), FALSE);
2388 gtk_widget_set_no_show_all (parent_priv->toolbar, TRUE);
2389 gtk_widget_hide (GTK_WIDGET (parent_priv->toolbar));
2394 modest_msg_view_window_clipboard_owner_change (GtkClipboard *clipboard,
2396 ModestMsgViewWindow *window)
2398 if (!GTK_WIDGET_VISIBLE (window))
2401 modest_window_check_dimming_rules_group (MODEST_WINDOW (window), MODEST_DIMMING_RULES_CLIPBOARD);
2405 modest_msg_view_window_transfer_mode_enabled (ModestMsgViewWindow *self)
2407 ModestMsgViewWindowPrivate *priv;
2409 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self), FALSE);
2410 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(self);
2412 return priv->progress_hint;
2416 observers_empty (ModestMsgViewWindow *self)
2419 ModestMsgViewWindowPrivate *priv;
2420 gboolean is_empty = TRUE;
2421 guint pending_ops = 0;
2423 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(self);
2424 tmp = priv->progress_widgets;
2426 /* Check all observers */
2427 while (tmp && is_empty) {
2428 pending_ops = modest_progress_object_num_pending_operations (MODEST_PROGRESS_OBJECT(tmp->data));
2429 is_empty = pending_ops == 0;
2431 tmp = g_slist_next(tmp);
2438 on_account_removed (TnyAccountStore *account_store,
2439 TnyAccount *account,
2442 /* Do nothing if it's a transport account, because we only
2443 show the messages of a store account */
2444 if (tny_account_get_account_type(account) == TNY_ACCOUNT_TYPE_STORE) {
2445 const gchar *parent_acc = NULL;
2446 const gchar *our_acc = NULL;
2448 our_acc = modest_window_get_active_account (MODEST_WINDOW (user_data));
2449 parent_acc = modest_tny_account_get_parent_modest_account_name_for_server_account (account);
2451 /* Close this window if I'm showing a message of the removed account */
2452 if (our_acc && parent_acc && strcmp (parent_acc, our_acc) == 0)
2453 modest_ui_actions_on_close_window (NULL, MODEST_WINDOW (user_data));
2458 on_mail_operation_started (ModestMailOperation *mail_op,
2461 ModestMsgViewWindow *self;
2462 ModestMailOperationTypeOperation op_type;
2464 ModestMsgViewWindowPrivate *priv;
2465 GObject *source = NULL;
2467 self = MODEST_MSG_VIEW_WINDOW (user_data);
2468 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
2469 op_type = modest_mail_operation_get_type_operation (mail_op);
2470 tmp = priv->progress_widgets;
2471 source = modest_mail_operation_get_source(mail_op);
2472 if (G_OBJECT (self) == source) {
2473 if (op_type == MODEST_MAIL_OPERATION_TYPE_RECEIVE) {
2474 set_toolbar_transfer_mode(self);
2476 modest_progress_object_add_operation (
2477 MODEST_PROGRESS_OBJECT (tmp->data),
2479 tmp = g_slist_next (tmp);
2483 g_object_unref (source);
2487 on_mail_operation_finished (ModestMailOperation *mail_op,
2490 ModestMsgViewWindow *self;
2491 ModestMailOperationTypeOperation op_type;
2493 ModestMsgViewWindowPrivate *priv;
2495 self = MODEST_MSG_VIEW_WINDOW (user_data);
2496 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
2497 op_type = modest_mail_operation_get_type_operation (mail_op);
2498 tmp = priv->progress_widgets;
2500 if (op_type == MODEST_MAIL_OPERATION_TYPE_RECEIVE) {
2502 modest_progress_object_remove_operation (MODEST_PROGRESS_OBJECT (tmp->data),
2504 tmp = g_slist_next (tmp);
2507 /* If no more operations are being observed, NORMAL mode is enabled again */
2508 if (observers_empty (self)) {
2509 set_progress_hint (self, FALSE);
2513 /* Update dimming rules. We have to do this right here
2514 and not in view_msg_cb because at that point the
2515 transfer mode is still enabled so the dimming rule
2516 won't let the user delete the message that has been
2517 readed for example */
2518 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 } DecodeAsyncHelper;
2578 on_decode_to_stream_async_handler (TnyMimePart *mime_part,
2584 DecodeAsyncHelper *helper = (DecodeAsyncHelper *) user_data;
2586 /* It could happen that the window was closed */
2587 if (GTK_WIDGET_VISIBLE (helper->self))
2588 set_progress_hint (helper->self, FALSE);
2590 if (cancelled || err) {
2592 gchar *msg = g_strdup_printf (_KR("cerm_device_memory_full"), "");
2593 modest_platform_information_banner (NULL, NULL, msg);
2599 /* make the file read-only */
2600 g_chmod(helper->file_path, 0444);
2602 /* Activate the file */
2603 modest_platform_activate_file (helper->file_path, tny_mime_part_get_content_type (mime_part));
2607 g_object_unref (helper->self);
2608 g_free (helper->file_path);
2609 g_slice_free (DecodeAsyncHelper, helper);
2613 modest_msg_view_window_view_attachment (ModestMsgViewWindow *window,
2614 TnyMimePart *mime_part)
2616 ModestMsgViewWindowPrivate *priv;
2617 const gchar *msg_uid;
2618 gchar *attachment_uid = NULL;
2619 gint attachment_index = 0;
2620 TnyList *attachments;
2622 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window));
2623 g_return_if_fail (TNY_IS_MIME_PART (mime_part) || (mime_part == NULL));
2624 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
2626 msg_uid = modest_msg_view_window_get_message_uid (MODEST_MSG_VIEW_WINDOW (window));
2627 attachments = modest_msg_view_get_attachments (MODEST_MSG_VIEW (priv->msg_view));
2628 attachment_index = modest_list_index (attachments, (GObject *) mime_part);
2629 g_object_unref (attachments);
2631 if (msg_uid && attachment_index >= 0) {
2632 attachment_uid = g_strdup_printf ("%s/%d", msg_uid, attachment_index);
2635 if (mime_part == NULL) {
2636 gboolean error = FALSE;
2637 TnyList *selected_attachments = modest_msg_view_get_selected_attachments (MODEST_MSG_VIEW (priv->msg_view));
2638 if (selected_attachments == NULL || tny_list_get_length (selected_attachments) == 0) {
2640 } else if (tny_list_get_length (selected_attachments) > 1) {
2641 hildon_banner_show_information (NULL, NULL, _("mcen_ib_unable_to_display_more"));
2645 iter = tny_list_create_iterator (selected_attachments);
2646 mime_part = (TnyMimePart *) tny_iterator_get_current (iter);
2647 g_object_unref (iter);
2649 if (selected_attachments)
2650 g_object_unref (selected_attachments);
2655 g_object_ref (mime_part);
2658 if (tny_mime_part_is_purged (mime_part))
2661 if (!modest_tny_mime_part_is_msg (mime_part) && tny_mime_part_get_filename (mime_part)) {
2662 gchar *filepath = NULL;
2663 const gchar *att_filename = tny_mime_part_get_filename (mime_part);
2664 gboolean show_error_banner = FALSE;
2665 TnyFsStream *temp_stream = NULL;
2666 temp_stream = modest_utils_create_temp_stream (att_filename, attachment_uid,
2669 if (temp_stream != NULL) {
2670 DecodeAsyncHelper *helper;
2672 /* Activate progress hint */
2673 set_progress_hint (window, TRUE);
2675 helper = g_slice_new0 (DecodeAsyncHelper);
2676 helper->self = g_object_ref (window);
2677 helper->file_path = g_strdup (filepath);
2679 tny_mime_part_decode_to_stream_async (mime_part, TNY_STREAM (temp_stream),
2680 on_decode_to_stream_async_handler,
2683 g_object_unref (temp_stream);
2684 /* NOTE: files in the temporary area will be automatically
2685 * cleaned after some time if they are no longer in use */
2688 const gchar *content_type;
2689 /* the file may already exist but it isn't writable,
2690 * let's try to open it anyway */
2691 content_type = tny_mime_part_get_content_type (mime_part);
2692 modest_platform_activate_file (filepath, content_type);
2694 g_warning ("%s: modest_utils_create_temp_stream failed", __FUNCTION__);
2695 show_error_banner = TRUE;
2700 if (show_error_banner)
2701 modest_platform_information_banner (NULL, NULL, _("mail_ib_file_operation_failed"));
2702 } else if (!modest_tny_mime_part_is_msg (mime_part)) {
2703 ModestWindowMgr *mgr;
2704 ModestWindow *msg_win = NULL;
2705 TnyMsg *current_msg;
2709 current_msg = modest_msg_view_window_get_message (MODEST_MSG_VIEW_WINDOW (window));
2710 mgr = modest_runtime_get_window_mgr ();
2711 header = tny_msg_get_header (TNY_MSG (current_msg));
2712 found = modest_window_mgr_find_registered_message_uid (mgr,
2717 g_debug ("window for this body is already being created");
2720 /* it's not found, so create a new window for it */
2721 modest_window_mgr_register_header (mgr, header, attachment_uid); /* register the uid before building the window */
2722 gchar *account = g_strdup (modest_window_get_active_account (MODEST_WINDOW (window)));
2723 const gchar *mailbox = modest_window_get_active_mailbox (MODEST_WINDOW (window));
2725 account = modest_account_mgr_get_default_account (modest_runtime_get_account_mgr ());
2727 msg_win = modest_msg_view_window_new_with_other_body (TNY_MSG (current_msg), TNY_MIME_PART (mime_part),
2728 account, mailbox, attachment_uid);
2730 modest_window_set_zoom (MODEST_WINDOW (msg_win),
2731 modest_window_get_zoom (MODEST_WINDOW (window)));
2732 if (modest_window_mgr_register_window (mgr, msg_win, MODEST_WINDOW (window)))
2733 gtk_widget_show_all (GTK_WIDGET (msg_win));
2735 gtk_widget_destroy (GTK_WIDGET (msg_win));
2737 g_object_unref (current_msg);
2739 /* message attachment */
2740 TnyHeader *header = NULL;
2741 ModestWindowMgr *mgr;
2742 ModestWindow *msg_win = NULL;
2745 header = tny_msg_get_header (TNY_MSG (mime_part));
2746 mgr = modest_runtime_get_window_mgr ();
2747 found = modest_window_mgr_find_registered_header (mgr, header, &msg_win);
2750 /* if it's found, but there is no msg_win, it's probably in the process of being created;
2751 * thus, we don't do anything */
2752 g_debug ("window for is already being created");
2754 /* it's not found, so create a new window for it */
2755 modest_window_mgr_register_header (mgr, header, attachment_uid); /* register the uid before building the window */
2756 gchar *account = g_strdup (modest_window_get_active_account (MODEST_WINDOW (window)));
2757 const gchar *mailbox = modest_window_get_active_mailbox (MODEST_WINDOW (window));
2759 account = modest_account_mgr_get_default_account (modest_runtime_get_account_mgr ());
2760 msg_win = modest_msg_view_window_new_for_attachment (TNY_MSG (mime_part), account,
2761 mailbox, attachment_uid);
2762 modest_window_set_zoom (MODEST_WINDOW (msg_win),
2763 modest_window_get_zoom (MODEST_WINDOW (window)));
2764 if (modest_window_mgr_register_window (mgr, msg_win, MODEST_WINDOW (window)))
2765 gtk_widget_show_all (GTK_WIDGET (msg_win));
2767 gtk_widget_destroy (GTK_WIDGET (msg_win));
2773 g_free (attachment_uid);
2775 g_object_unref (mime_part);
2787 GnomeVFSResult result;
2790 static void save_mime_part_info_free (SaveMimePartInfo *info, gboolean with_struct);
2791 static gboolean idle_save_mime_part_show_result (SaveMimePartInfo *info);
2792 static gpointer save_mime_part_to_file (SaveMimePartInfo *info);
2793 static void save_mime_parts_to_file_with_checks (GtkWindow *parent, SaveMimePartInfo *info);
2796 save_mime_part_info_free (SaveMimePartInfo *info, gboolean with_struct)
2800 for (node = info->pairs; node != NULL; node = g_list_next (node)) {
2801 SaveMimePartPair *pair = (SaveMimePartPair *) node->data;
2802 g_free (pair->filename);
2803 g_object_unref (pair->part);
2804 g_slice_free (SaveMimePartPair, pair);
2806 g_list_free (info->pairs);
2809 g_slice_free (SaveMimePartInfo, info);
2814 idle_save_mime_part_show_result (SaveMimePartInfo *info)
2816 /* This is a GDK lock because we are an idle callback and
2817 * hildon_banner_show_information is or does Gtk+ code */
2819 gdk_threads_enter (); /* CHECKED */
2820 if (info->result == GNOME_VFS_OK) {
2821 hildon_banner_show_information (NULL, NULL, _CS("sfil_ib_saved"));
2822 } else if (info->result == GNOME_VFS_ERROR_NO_SPACE) {
2823 gchar *msg = g_strdup_printf (_KR("cerm_device_memory_full"), "");
2824 modest_platform_information_banner (NULL, NULL, msg);
2827 hildon_banner_show_information (NULL, NULL, _("mail_ib_file_operation_failed"));
2829 save_mime_part_info_free (info, FALSE);
2830 gdk_threads_leave (); /* CHECKED */
2836 save_mime_part_to_file (SaveMimePartInfo *info)
2838 GnomeVFSHandle *handle;
2840 SaveMimePartPair *pair = (SaveMimePartPair *) info->pairs->data;
2842 info->result = gnome_vfs_create (&handle, pair->filename, GNOME_VFS_OPEN_WRITE, FALSE, 0644);
2843 if (info->result == GNOME_VFS_OK) {
2844 GError *error = NULL;
2845 stream = tny_vfs_stream_new (handle);
2846 if (tny_mime_part_decode_to_stream (pair->part, stream, &error) < 0) {
2847 g_warning ("modest: could not save attachment %s: %d (%s)\n", pair->filename, error?error->code:-1, error?error->message:"Unknown error");
2849 if ((error->domain == TNY_ERROR_DOMAIN) &&
2850 (error->code == TNY_IO_ERROR_WRITE) &&
2851 (errno == ENOSPC)) {
2852 info->result = GNOME_VFS_ERROR_NO_SPACE;
2854 info->result = GNOME_VFS_ERROR_IO;
2857 g_object_unref (G_OBJECT (stream));
2859 g_warning ("Could not create save attachment %s: %s\n",
2860 pair->filename, gnome_vfs_result_to_string (info->result));
2863 /* Go on saving remaining files */
2864 info->pairs = g_list_remove_link (info->pairs, info->pairs);
2865 if (info->pairs != NULL) {
2866 save_mime_part_to_file (info);
2868 g_idle_add ((GSourceFunc) idle_save_mime_part_show_result, info);
2875 save_mime_parts_to_file_with_checks (GtkWindow *parent,
2876 SaveMimePartInfo *info)
2878 gboolean is_ok = TRUE;
2879 gint replaced_files = 0;
2880 const GList *files = info->pairs;
2881 const GList *iter, *to_replace = NULL;
2883 for (iter = files; (iter != NULL) && (replaced_files < 2); iter = g_list_next(iter)) {
2884 SaveMimePartPair *pair = iter->data;
2885 if (modest_utils_file_exists (pair->filename)) {
2887 if (replaced_files == 1)
2891 if (replaced_files) {
2894 if (replaced_files == 1) {
2895 SaveMimePartPair *pair = to_replace->data;
2896 const gchar *basename = strrchr (pair->filename, G_DIR_SEPARATOR) + 1;
2897 gchar *escaped_basename, *message;
2899 escaped_basename = g_uri_unescape_string (basename, NULL);
2900 message = g_strdup_printf ("%s\n%s",
2901 _FM("docm_nc_replace_file"),
2902 (escaped_basename) ? escaped_basename : "");
2903 response = modest_platform_run_confirmation_dialog (parent, message);
2905 g_free (escaped_basename);
2907 response = modest_platform_run_confirmation_dialog (parent,
2908 _FM("docm_nc_replace_multiple"));
2910 if (response != GTK_RESPONSE_OK)
2915 save_mime_part_info_free (info, TRUE);
2917 g_thread_create ((GThreadFunc)save_mime_part_to_file, info, FALSE, NULL);
2923 save_attachments_response (GtkDialog *dialog,
2927 TnyList *mime_parts;
2929 GList *files_to_save = NULL;
2930 gchar *current_folder;
2932 mime_parts = TNY_LIST (user_data);
2934 if (arg1 != GTK_RESPONSE_OK)
2937 chooser_uri = gtk_file_chooser_get_uri (GTK_FILE_CHOOSER (dialog));
2938 current_folder = gtk_file_chooser_get_current_folder_uri (GTK_FILE_CHOOSER (dialog));
2939 if (current_folder && current_folder != '\0') {
2941 modest_conf_set_string (modest_runtime_get_conf (), MODEST_CONF_LATEST_SAVE_ATTACHMENT_PATH,
2942 current_folder,&err);
2944 g_debug ("Error storing latest used folder: %s", err->message);
2948 g_free (current_folder);
2950 if (!modest_utils_folder_writable (chooser_uri)) {
2951 hildon_banner_show_information
2952 (NULL, NULL, _FM("sfil_ib_readonly_location"));
2956 iter = tny_list_create_iterator (mime_parts);
2957 while (!tny_iterator_is_done (iter)) {
2958 TnyMimePart *mime_part = (TnyMimePart *) tny_iterator_get_current (iter);
2960 if ((modest_tny_mime_part_is_attachment_for_modest (mime_part)) &&
2961 !tny_mime_part_is_purged (mime_part) &&
2962 (tny_mime_part_get_filename (mime_part) != NULL)) {
2963 SaveMimePartPair *pair;
2965 pair = g_slice_new0 (SaveMimePartPair);
2967 if (tny_list_get_length (mime_parts) > 1) {
2969 gnome_vfs_escape_slashes (tny_mime_part_get_filename (mime_part));
2970 pair->filename = g_build_filename (chooser_uri, escaped, NULL);
2973 pair->filename = g_strdup (chooser_uri);
2975 pair->part = mime_part;
2976 files_to_save = g_list_prepend (files_to_save, pair);
2978 tny_iterator_next (iter);
2980 g_object_unref (iter);
2982 g_free (chooser_uri);
2984 if (files_to_save != NULL) {
2985 SaveMimePartInfo *info = g_slice_new0 (SaveMimePartInfo);
2986 info->pairs = files_to_save;
2987 info->result = TRUE;
2988 save_mime_parts_to_file_with_checks ((GtkWindow *) dialog, info);
2992 /* Free and close the dialog */
2993 g_object_unref (mime_parts);
2994 gtk_widget_destroy (GTK_WIDGET (dialog));
2998 modest_msg_view_window_save_attachments (ModestMsgViewWindow *window,
2999 TnyList *mime_parts)
3001 ModestMsgViewWindowPrivate *priv;
3002 GtkWidget *save_dialog = NULL;
3003 gchar *conf_folder = NULL;
3004 gchar *filename = NULL;
3005 gchar *save_multiple_str = NULL;
3007 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window));
3008 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
3010 if (mime_parts == NULL) {
3011 /* In Hildon 2.2 save and delete operate over all the attachments as there's no
3012 * selection available */
3013 mime_parts = modest_msg_view_get_attachments (MODEST_MSG_VIEW (priv->msg_view));
3014 if (mime_parts && !modest_maemo_utils_select_attachments (GTK_WINDOW (window), mime_parts, FALSE)) {
3015 g_object_unref (mime_parts);
3018 if (mime_parts == NULL || tny_list_get_length (mime_parts) == 0) {
3020 g_object_unref (mime_parts);
3026 g_object_ref (mime_parts);
3029 /* prepare dialog */
3030 if (tny_list_get_length (mime_parts) == 1) {
3032 /* only one attachment selected */
3033 iter = tny_list_create_iterator (mime_parts);
3034 TnyMimePart *mime_part = (TnyMimePart *) tny_iterator_get_current (iter);
3035 g_object_unref (iter);
3036 if (!modest_tny_mime_part_is_msg (mime_part) &&
3037 modest_tny_mime_part_is_attachment_for_modest (mime_part) &&
3038 !tny_mime_part_is_purged (mime_part)) {
3039 filename = g_strdup (tny_mime_part_get_filename (mime_part));
3041 /* TODO: show any error? */
3042 g_warning ("%s: Tried to save a non-file attachment", __FUNCTION__);
3043 g_object_unref (mime_parts);
3046 g_object_unref (mime_part);
3048 gint num = tny_list_get_length (mime_parts);
3049 save_multiple_str = g_strdup_printf (dngettext("hildon-fm",
3050 "sfil_va_number_of_objects_attachment",
3051 "sfil_va_number_of_objects_attachments",
3055 save_dialog = hildon_file_chooser_dialog_new (GTK_WINDOW (window),
3056 GTK_FILE_CHOOSER_ACTION_SAVE);
3059 conf_folder = modest_conf_get_string (modest_runtime_get_conf (), MODEST_CONF_LATEST_SAVE_ATTACHMENT_PATH, NULL);
3060 if (conf_folder && conf_folder[0] != '\0') {
3061 gtk_file_chooser_set_current_folder_uri (GTK_FILE_CHOOSER (save_dialog), conf_folder);
3064 /* Set the default folder to images folder */
3065 docs_folder = g_build_filename (g_getenv (MYDOCS_ENV), DOCS_FOLDER, NULL);
3066 gtk_file_chooser_set_current_folder (GTK_FILE_CHOOSER (save_dialog), docs_folder);
3067 g_free (docs_folder);
3069 g_free (conf_folder);
3073 gtk_file_chooser_set_current_name (GTK_FILE_CHOOSER (save_dialog),
3078 /* if multiple, set multiple string */
3079 if (save_multiple_str) {
3080 g_object_set (G_OBJECT (save_dialog), "save-multiple", save_multiple_str, NULL);
3081 gtk_window_set_title (GTK_WINDOW (save_dialog), _FM("sfil_ti_save_objects_files"));
3084 /* We must run this asynchronously, because the hildon dialog
3085 performs a gtk_dialog_run by itself which leads to gdk
3087 g_signal_connect (save_dialog, "response",
3088 G_CALLBACK (save_attachments_response), mime_parts);
3090 gtk_widget_show_all (save_dialog);
3094 show_remove_attachment_information (gpointer userdata)
3096 ModestMsgViewWindow *window = (ModestMsgViewWindow *) userdata;
3097 ModestMsgViewWindowPrivate *priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
3099 /* We're outside the main lock */
3100 gdk_threads_enter ();
3102 if (priv->remove_attachment_banner != NULL) {
3103 gtk_widget_destroy (priv->remove_attachment_banner);
3104 g_object_unref (priv->remove_attachment_banner);
3107 priv->remove_attachment_banner = g_object_ref (
3108 hildon_banner_show_animation (NULL, NULL, _("mcen_me_inbox_remove_attachments")));
3110 gdk_threads_leave ();
3116 modest_msg_view_window_remove_attachments (ModestMsgViewWindow *window, gboolean get_all)
3118 ModestMsgViewWindowPrivate *priv;
3119 TnyList *mime_parts = NULL, *tmp;
3120 gchar *confirmation_message;
3126 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window));
3127 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
3129 /* In hildon 2.2 we ignore the get_all flag as we always get all attachments. This is
3130 * because we don't have selection
3132 mime_parts = modest_msg_view_get_attachments (MODEST_MSG_VIEW (priv->msg_view));
3134 /* Remove already purged messages from mime parts list. We use
3135 a copy of the list to remove items in the original one */
3136 tmp = tny_list_copy (mime_parts);
3137 iter = tny_list_create_iterator (tmp);
3138 while (!tny_iterator_is_done (iter)) {
3139 TnyMimePart *part = TNY_MIME_PART (tny_iterator_get_current (iter));
3140 if (tny_mime_part_is_purged (part))
3141 tny_list_remove (mime_parts, (GObject *) part);
3143 g_object_unref (part);
3144 tny_iterator_next (iter);
3146 g_object_unref (tmp);
3147 g_object_unref (iter);
3149 if (!modest_maemo_utils_select_attachments (GTK_WINDOW (window), mime_parts, TRUE) ||
3150 tny_list_get_length (mime_parts) == 0) {
3151 g_object_unref (mime_parts);
3155 n_attachments = tny_list_get_length (mime_parts);
3156 if (n_attachments == 1) {
3160 iter = tny_list_create_iterator (mime_parts);
3161 part = (TnyMimePart *) tny_iterator_get_current (iter);
3162 g_object_unref (iter);
3163 if (modest_tny_mime_part_is_msg (part)) {
3165 header = tny_msg_get_header (TNY_MSG (part));
3166 filename = tny_header_dup_subject (header);
3167 g_object_unref (header);
3168 if (filename == NULL)
3169 filename = g_strdup (_("mail_va_no_subject"));
3171 filename = g_strdup (tny_mime_part_get_filename (TNY_MIME_PART (part)));
3173 confirmation_message = g_strdup_printf (_("mcen_nc_purge_file_text"), filename);
3175 g_object_unref (part);
3177 confirmation_message = g_strdup_printf (ngettext("mcen_nc_purge_file_text",
3178 "mcen_nc_purge_files_text",
3179 n_attachments), n_attachments);
3181 response = modest_platform_run_confirmation_dialog (GTK_WINDOW (window),
3182 confirmation_message);
3183 g_free (confirmation_message);
3185 if (response != GTK_RESPONSE_OK) {
3186 g_object_unref (mime_parts);
3190 priv->purge_timeout = g_timeout_add (2000, show_remove_attachment_information, window);
3192 iter = tny_list_create_iterator (mime_parts);
3193 while (!tny_iterator_is_done (iter)) {
3196 part = (TnyMimePart *) tny_iterator_get_current (iter);
3197 tny_mime_part_set_purged (TNY_MIME_PART (part));
3198 g_object_unref (part);
3199 tny_iterator_next (iter);
3201 g_object_unref (iter);
3203 msg = tny_msg_view_get_msg (TNY_MSG_VIEW (priv->msg_view));
3204 tny_msg_view_clear (TNY_MSG_VIEW (priv->msg_view));
3205 tny_msg_rewrite_cache (msg);
3206 tny_msg_view_set_msg (TNY_MSG_VIEW (priv->msg_view), msg);
3207 g_object_unref (msg);
3208 update_branding (MODEST_MSG_VIEW_WINDOW (window));
3210 g_object_unref (mime_parts);
3212 if (priv->purge_timeout > 0) {
3213 g_source_remove (priv->purge_timeout);
3214 priv->purge_timeout = 0;
3217 if (priv->remove_attachment_banner) {
3218 gtk_widget_destroy (priv->remove_attachment_banner);
3219 g_object_unref (priv->remove_attachment_banner);
3220 priv->remove_attachment_banner = NULL;
3226 update_window_title (ModestMsgViewWindow *window)
3228 ModestMsgViewWindowPrivate *priv;
3230 TnyHeader *header = NULL;
3231 gchar *subject = NULL;
3233 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
3235 /* Note that if the window is closed while we're retrieving
3236 the message, this widget could de deleted */
3237 if (!priv->msg_view)
3240 msg = tny_msg_view_get_msg (TNY_MSG_VIEW (priv->msg_view));
3242 if (priv->other_body) {
3245 description = modest_tny_mime_part_get_header_value (priv->other_body, "Content-Description");
3247 g_strstrip (description);
3248 subject = description;
3250 } else if (msg != NULL) {
3251 header = tny_msg_get_header (msg);
3252 subject = tny_header_dup_subject (header);
3253 g_object_unref (header);
3254 g_object_unref (msg);
3257 if ((subject == NULL)||(subject[0] == '\0')) {
3259 subject = g_strdup (_("mail_va_no_subject"));
3262 gtk_window_set_title (GTK_WINDOW (window), subject);
3267 on_move_focus (GtkWidget *widget,
3268 GtkDirectionType direction,
3271 g_signal_stop_emission_by_name (G_OBJECT (widget), "move-focus");
3275 fetch_image_open_stream (TnyStreamCache *self, gint64 *expected_size, gchar *uri)
3277 GnomeVFSResult result;
3278 GnomeVFSHandle *handle = NULL;
3279 GnomeVFSFileInfo *info = NULL;
3282 result = gnome_vfs_open (&handle, uri, GNOME_VFS_OPEN_READ);
3283 if (result != GNOME_VFS_OK) {
3288 info = gnome_vfs_file_info_new ();
3289 result = gnome_vfs_get_file_info_from_handle (handle, info, GNOME_VFS_FILE_INFO_DEFAULT);
3290 if (result != GNOME_VFS_OK || ! (info->valid_fields & GNOME_VFS_FILE_INFO_FIELDS_SIZE)) {
3291 /* We put a "safe" default size for going to cache */
3292 *expected_size = (300*1024);
3294 *expected_size = info->size;
3296 gnome_vfs_file_info_unref (info);
3298 stream = tny_vfs_stream_new (handle);
3307 TnyStream *output_stream;
3308 GtkWidget *msg_view;
3313 on_fetch_image_idle_refresh_view (gpointer userdata)
3316 FetchImageData *fidata = (FetchImageData *) userdata;
3318 gdk_threads_enter ();
3319 if (GTK_WIDGET_DRAWABLE (fidata->msg_view)) {
3320 ModestMsgViewWindowPrivate *priv;
3322 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (fidata->window);
3323 priv->fetching_images--;
3324 gtk_widget_queue_draw (fidata->msg_view);
3325 update_progress_hint (MODEST_MSG_VIEW_WINDOW (fidata->window));
3327 gdk_threads_leave ();
3329 g_object_unref (fidata->msg_view);
3330 g_object_unref (fidata->window);
3331 g_slice_free (FetchImageData, fidata);
3336 on_fetch_image_thread (gpointer userdata)
3338 FetchImageData *fidata = (FetchImageData *) userdata;
3339 TnyStreamCache *cache;
3340 TnyStream *cache_stream;
3342 cache = modest_runtime_get_images_cache ();
3344 tny_stream_cache_get_stream (cache,
3346 (TnyStreamCacheOpenStreamFetcher) fetch_image_open_stream,
3347 (gpointer) fidata->uri);
3348 g_free (fidata->cache_id);
3349 g_free (fidata->uri);
3351 if (cache_stream != NULL) {
3354 while (G_LIKELY (!tny_stream_is_eos (cache_stream))) {
3357 nb_read = tny_stream_read (cache_stream, buffer, sizeof (buffer));
3358 if (G_UNLIKELY (nb_read < 0)) {
3360 } else if (G_LIKELY (nb_read > 0)) {
3361 gssize nb_written = 0;
3363 while (G_UNLIKELY (nb_written < nb_read)) {
3366 len = tny_stream_write (fidata->output_stream, buffer + nb_written,
3367 nb_read - nb_written);
3368 if (G_UNLIKELY (len < 0))
3374 tny_stream_close (cache_stream);
3375 g_object_unref (cache_stream);
3378 tny_stream_close (fidata->output_stream);
3379 g_object_unref (fidata->output_stream);
3381 g_idle_add (on_fetch_image_idle_refresh_view, fidata);
3387 on_fetch_image (ModestMsgView *msgview,
3390 ModestMsgViewWindow *window)
3392 const gchar *current_account;
3393 ModestMsgViewWindowPrivate *priv;
3394 FetchImageData *fidata;
3396 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
3398 current_account = modest_window_get_active_account (MODEST_WINDOW (window));
3400 fidata = g_slice_new0 (FetchImageData);
3401 fidata->msg_view = g_object_ref (msgview);
3402 fidata->window = g_object_ref (window);
3403 fidata->uri = g_strdup (uri);
3404 fidata->cache_id = modest_images_cache_get_id (current_account, uri);
3405 fidata->output_stream = g_object_ref (stream);
3407 priv->fetching_images++;
3408 if (g_thread_create (on_fetch_image_thread, fidata, FALSE, NULL) == NULL) {
3409 g_object_unref (fidata->output_stream);
3410 g_free (fidata->cache_id);
3411 g_free (fidata->uri);
3412 g_object_unref (fidata->msg_view);
3413 g_slice_free (FetchImageData, fidata);
3414 tny_stream_close (stream);
3415 priv->fetching_images--;
3416 update_progress_hint (window);
3419 update_progress_hint (window);
3425 setup_menu (ModestMsgViewWindow *self)
3427 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW(self));
3429 /* Settings menu buttons */
3430 modest_hildon2_window_add_to_menu (MODEST_HILDON2_WINDOW (self), _("mcen_me_viewer_find"), NULL,
3431 APP_MENU_CALLBACK (modest_msg_view_window_show_find_toolbar),
3432 MODEST_DIMMING_CALLBACK (modest_ui_dimming_rules_on_find_in_msg));
3434 modest_hildon2_window_add_to_menu (MODEST_HILDON2_WINDOW (self), _("mcen_me_move_messages"), NULL,
3435 APP_MENU_CALLBACK (modest_ui_actions_on_move_to),
3436 MODEST_DIMMING_CALLBACK (modest_ui_dimming_rules_on_move_to));
3438 modest_hildon2_window_add_to_menu (MODEST_HILDON2_WINDOW (self), _("mcen_me_inbox_forward"), "<Control>d",
3439 APP_MENU_CALLBACK (modest_ui_actions_on_forward),
3440 MODEST_DIMMING_CALLBACK (modest_ui_dimming_rules_on_reply_msg));
3442 modest_hildon2_window_add_to_menu (MODEST_HILDON2_WINDOW (self), _("mcen_me_inbox_mark_as_read"), NULL,
3443 APP_MENU_CALLBACK (modest_ui_actions_on_mark_as_read),
3444 MODEST_DIMMING_CALLBACK (modest_ui_dimming_rules_on_mark_as_read_msg_in_view));
3446 modest_hildon2_window_add_to_menu (MODEST_HILDON2_WINDOW (self), _("mcen_me_inbox_mark_as_unread"), NULL,
3447 APP_MENU_CALLBACK (modest_ui_actions_on_mark_as_unread),
3448 MODEST_DIMMING_CALLBACK (modest_ui_dimming_rules_on_mark_as_unread_msg_in_view));
3450 modest_hildon2_window_add_to_menu (MODEST_HILDON2_WINDOW (self), _("mcen_me_viewer_save_attachments"), NULL,
3451 APP_MENU_CALLBACK (modest_ui_actions_save_attachments),
3452 MODEST_DIMMING_CALLBACK (modest_ui_dimming_rules_on_save_attachments));
3453 modest_hildon2_window_add_to_menu (MODEST_HILDON2_WINDOW (self), _("mcen_me_inbox_remove_attachments"), NULL,
3454 APP_MENU_CALLBACK (modest_ui_actions_remove_attachments),
3455 MODEST_DIMMING_CALLBACK (modest_ui_dimming_rules_on_remove_attachments));
3457 modest_hildon2_window_add_to_menu (MODEST_HILDON2_WINDOW (self), _("mcen_me_new_message"), "<Control>n",
3458 APP_MENU_CALLBACK (modest_ui_actions_on_new_msg),
3459 MODEST_DIMMING_CALLBACK (modest_ui_dimming_rules_on_new_msg));
3460 modest_hildon2_window_add_to_menu (MODEST_HILDON2_WINDOW (self), _("mcen_me_viewer_addtocontacts"), NULL,
3461 APP_MENU_CALLBACK (modest_ui_actions_add_to_contacts),
3462 MODEST_DIMMING_CALLBACK (modest_ui_dimming_rules_on_add_to_contacts));
3464 modest_hildon2_window_add_to_menu (MODEST_HILDON2_WINDOW (self), _("mcen_ti_message_properties"), NULL,
3465 APP_MENU_CALLBACK (modest_ui_actions_on_details),
3466 MODEST_DIMMING_CALLBACK (modest_ui_dimming_rules_on_details));
3470 modest_msg_view_window_add_to_contacts (ModestMsgViewWindow *self)
3472 ModestMsgViewWindowPrivate *priv;
3473 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
3474 GSList *recipients = NULL;
3476 gboolean contacts_to_add = FALSE;
3478 msg = tny_msg_view_get_msg (TNY_MSG_VIEW (priv->msg_view));
3482 header = modest_msg_view_window_get_header (self);
3485 recipients = modest_tny_msg_header_get_all_recipients_list (header);
3486 g_object_unref (header);
3488 recipients = modest_tny_msg_get_all_recipients_list (msg);
3489 g_object_unref (msg);
3492 if (recipients != NULL) {
3493 GtkWidget *picker_dialog;
3494 GtkWidget *selector;
3496 gchar *selected = NULL;
3498 selector = hildon_touch_selector_new_text ();
3499 g_object_ref (selector);
3501 for (node = recipients; node != NULL; node = g_slist_next (node)) {
3502 if (!modest_address_book_has_address ((const gchar *) node->data)) {
3503 hildon_touch_selector_append_text (HILDON_TOUCH_SELECTOR (selector),
3504 (const gchar *) node->data);
3505 contacts_to_add = TRUE;
3509 if (contacts_to_add) {
3512 picker_dialog = hildon_picker_dialog_new (GTK_WINDOW (self));
3513 gtk_window_set_title (GTK_WINDOW (picker_dialog), _("mcen_me_viewer_addtocontacts"));
3515 hildon_picker_dialog_set_selector (HILDON_PICKER_DIALOG (picker_dialog),
3516 HILDON_TOUCH_SELECTOR (selector));
3518 picker_result = gtk_dialog_run (GTK_DIALOG (picker_dialog));
3520 if (picker_result == GTK_RESPONSE_OK) {
3521 selected = hildon_touch_selector_get_current_text (HILDON_TOUCH_SELECTOR (selector));
3523 gtk_widget_destroy (picker_dialog);
3526 modest_address_book_add_address (selected);
3531 g_object_unref (selector);
3536 if (recipients) {g_slist_foreach (recipients, (GFunc) g_free, NULL); g_slist_free (recipients);}
3540 _modest_msg_view_window_map_event (GtkWidget *widget,
3544 ModestMsgViewWindow *self = (ModestMsgViewWindow *) userdata;
3546 update_progress_hint (self);
3552 modest_msg_view_window_fetch_images (ModestMsgViewWindow *self)
3554 ModestMsgViewWindowPrivate *priv;
3555 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
3557 modest_msg_view_request_fetch_images (MODEST_MSG_VIEW (priv->msg_view));
3561 modest_msg_view_window_has_blocked_external_images (ModestMsgViewWindow *self)
3563 ModestMsgViewWindowPrivate *priv;
3564 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
3566 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self), FALSE);
3568 return modest_msg_view_has_blocked_external_images (MODEST_MSG_VIEW (priv->msg_view));
3572 modest_msg_view_window_reload (ModestMsgViewWindow *self)
3574 ModestMsgViewWindowPrivate *priv;
3577 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self));
3579 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
3580 header = modest_msg_view_window_get_header (MODEST_MSG_VIEW_WINDOW (self));
3582 if (!message_reader (self, priv, header, NULL, NULL, priv->row_reference)) {
3583 g_warning ("Shouldn't happen, trying to reload a message failed");
3586 g_object_unref (header);
3590 update_branding (ModestMsgViewWindow *self)
3592 const gchar *account;
3593 const gchar *mailbox;
3594 ModestAccountMgr *mgr;
3595 ModestProtocol *protocol = NULL;
3596 gchar *service_name = NULL;
3597 const GdkPixbuf *service_icon = NULL;
3598 ModestMsgViewWindowPrivate *priv;
3600 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
3602 account = modest_window_get_active_account (MODEST_WINDOW (self));
3603 mailbox = modest_window_get_active_mailbox (MODEST_WINDOW (self));
3605 mgr = modest_runtime_get_account_mgr ();
3607 if (modest_account_mgr_account_is_multimailbox (mgr, account, &protocol)) {
3608 if (MODEST_IS_ACCOUNT_PROTOCOL (protocol)) {
3609 service_name = modest_account_protocol_get_service_name (MODEST_ACCOUNT_PROTOCOL (protocol),
3611 service_icon = modest_account_protocol_get_service_icon (MODEST_ACCOUNT_PROTOCOL (protocol),
3612 account, mailbox, MODEST_ICON_SIZE_SMALL);
3616 modest_msg_view_set_branding (MODEST_MSG_VIEW (priv->msg_view), service_name, service_icon);
3617 g_free (service_name);