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>
70 #define MYDOCS_ENV "MYDOCSDIR"
71 #define DOCS_FOLDER ".documents"
73 typedef struct _ModestMsgViewWindowPrivate ModestMsgViewWindowPrivate;
74 struct _ModestMsgViewWindowPrivate {
77 GtkWidget *main_scroll;
78 GtkWidget *find_toolbar;
81 /* Progress observers */
82 GSList *progress_widgets;
85 GtkWidget *prev_toolitem;
86 GtkWidget *next_toolitem;
87 gboolean progress_hint;
90 /* Optimized view enabled */
91 gboolean optimized_view;
93 /* Whether this was created via the *_new_for_search_result() function. */
94 gboolean is_search_result;
96 /* Whether the message is in outbox */
99 /* A reference to the @model of the header view
100 * to allow selecting previous/next messages,
101 * if the message is currently selected in the header view.
103 const gchar *header_folder_id;
104 GtkTreeModel *header_model;
105 GtkTreeRowReference *row_reference;
106 GtkTreeRowReference *next_row_reference;
108 gulong clipboard_change_handler;
109 gulong queue_change_handler;
110 gulong account_removed_handler;
111 gulong row_changed_handler;
112 gulong row_deleted_handler;
113 gulong row_inserted_handler;
114 gulong rows_reordered_handler;
117 GtkWidget *remove_attachment_banner;
120 TnyMimePart *other_body;
125 static void modest_msg_view_window_class_init (ModestMsgViewWindowClass *klass);
126 static void modest_msg_view_window_init (ModestMsgViewWindow *obj);
127 static void modest_header_view_observer_init(
128 ModestHeaderViewObserverIface *iface_class);
129 static void modest_msg_view_window_finalize (GObject *obj);
130 static void modest_msg_view_window_toggle_find_toolbar (GtkToggleAction *obj,
132 static void modest_msg_view_window_find_toolbar_close (GtkWidget *widget,
133 ModestMsgViewWindow *obj);
134 static void modest_msg_view_window_find_toolbar_search (GtkWidget *widget,
135 ModestMsgViewWindow *obj);
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 GtkTreeRowReference *row_reference);
226 static void setup_menu (ModestMsgViewWindow *self);
227 static gboolean _modest_msg_view_window_map_event (GtkWidget *widget,
232 /* list my signals */
239 static const GtkToggleActionEntry msg_view_toggle_action_entries [] = {
240 { "FindInMessage", MODEST_TOOLBAR_ICON_FIND, N_("qgn_toolb_gene_find"), NULL, NULL, G_CALLBACK (modest_msg_view_window_toggle_find_toolbar), FALSE },
244 #define MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE((o), \
245 MODEST_TYPE_MSG_VIEW_WINDOW, \
246 ModestMsgViewWindowPrivate))
248 static GtkWindowClass *parent_class = NULL;
250 /* uncomment the following if you have defined any signals */
251 static guint signals[LAST_SIGNAL] = {0};
254 modest_msg_view_window_get_type (void)
256 static GType my_type = 0;
258 static const GTypeInfo my_info = {
259 sizeof(ModestMsgViewWindowClass),
260 NULL, /* base init */
261 NULL, /* base finalize */
262 (GClassInitFunc) modest_msg_view_window_class_init,
263 NULL, /* class finalize */
264 NULL, /* class data */
265 sizeof(ModestMsgViewWindow),
267 (GInstanceInitFunc) modest_msg_view_window_init,
270 my_type = g_type_register_static (MODEST_TYPE_HILDON2_WINDOW,
271 "ModestMsgViewWindow",
274 static const GInterfaceInfo modest_header_view_observer_info =
276 (GInterfaceInitFunc) modest_header_view_observer_init,
277 NULL, /* interface_finalize */
278 NULL /* interface_data */
281 g_type_add_interface_static (my_type,
282 MODEST_TYPE_HEADER_VIEW_OBSERVER,
283 &modest_header_view_observer_info);
289 save_state (ModestWindow *self)
291 modest_widget_memory_save (modest_runtime_get_conf (),
293 MODEST_CONF_MSG_VIEW_WINDOW_KEY);
297 gboolean modest_msg_view_window_scroll_child (ModestMsgViewWindow *self,
298 GtkScrollType scroll_type,
302 ModestMsgViewWindowPrivate *priv;
303 gboolean return_value;
305 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(self);
306 g_signal_emit_by_name (priv->main_scroll, "scroll-child", scroll_type, horizontal, &return_value);
311 add_scroll_binding (GtkBindingSet *binding_set,
313 GtkScrollType scroll)
315 guint keypad_keyval = keyval - GDK_Left + GDK_KP_Left;
317 gtk_binding_entry_add_signal (binding_set, keyval, 0,
319 GTK_TYPE_SCROLL_TYPE, scroll,
320 G_TYPE_BOOLEAN, FALSE);
321 gtk_binding_entry_add_signal (binding_set, keypad_keyval, 0,
323 GTK_TYPE_SCROLL_TYPE, scroll,
324 G_TYPE_BOOLEAN, FALSE);
328 modest_msg_view_window_class_init (ModestMsgViewWindowClass *klass)
330 GObjectClass *gobject_class;
331 HildonWindowClass *hildon_window_class;
332 ModestWindowClass *modest_window_class;
333 GtkBindingSet *binding_set;
335 gobject_class = (GObjectClass*) klass;
336 hildon_window_class = (HildonWindowClass *) klass;
337 modest_window_class = (ModestWindowClass *) klass;
339 parent_class = g_type_class_peek_parent (klass);
340 gobject_class->finalize = modest_msg_view_window_finalize;
342 modest_window_class->set_zoom_func = modest_msg_view_window_set_zoom;
343 modest_window_class->get_zoom_func = modest_msg_view_window_get_zoom;
344 modest_window_class->zoom_plus_func = modest_msg_view_window_zoom_plus;
345 modest_window_class->zoom_minus_func = modest_msg_view_window_zoom_minus;
346 modest_window_class->show_toolbar_func = modest_msg_view_window_show_toolbar;
347 modest_window_class->disconnect_signals_func = modest_msg_view_window_disconnect_signals;
349 modest_window_class->save_state_func = save_state;
351 klass->scroll_child = modest_msg_view_window_scroll_child;
353 signals[MSG_CHANGED_SIGNAL] =
354 g_signal_new ("msg-changed",
355 G_TYPE_FROM_CLASS (gobject_class),
357 G_STRUCT_OFFSET (ModestMsgViewWindowClass, msg_changed),
359 modest_marshal_VOID__POINTER_POINTER,
360 G_TYPE_NONE, 2, G_TYPE_POINTER, G_TYPE_POINTER);
362 signals[SCROLL_CHILD_SIGNAL] =
363 g_signal_new ("scroll-child",
364 G_TYPE_FROM_CLASS (gobject_class),
365 G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
366 G_STRUCT_OFFSET (ModestMsgViewWindowClass, scroll_child),
368 modest_marshal_BOOLEAN__ENUM_BOOLEAN,
369 G_TYPE_BOOLEAN, 2, GTK_TYPE_SCROLL_TYPE, G_TYPE_BOOLEAN);
371 binding_set = gtk_binding_set_by_class (klass);
372 add_scroll_binding (binding_set, GDK_Up, GTK_SCROLL_STEP_UP);
373 add_scroll_binding (binding_set, GDK_Down, GTK_SCROLL_STEP_DOWN);
374 add_scroll_binding (binding_set, GDK_Page_Up, GTK_SCROLL_PAGE_UP);
375 add_scroll_binding (binding_set, GDK_Page_Down, GTK_SCROLL_PAGE_DOWN);
376 add_scroll_binding (binding_set, GDK_Home, GTK_SCROLL_START);
377 add_scroll_binding (binding_set, GDK_End, GTK_SCROLL_END);
379 g_type_class_add_private (gobject_class, sizeof(ModestMsgViewWindowPrivate));
383 static void modest_header_view_observer_init(
384 ModestHeaderViewObserverIface *iface_class)
386 iface_class->update_func = modest_msg_view_window_update_model_replaced;
390 modest_msg_view_window_init (ModestMsgViewWindow *obj)
392 ModestMsgViewWindowPrivate *priv;
393 ModestWindowPrivate *parent_priv = NULL;
394 GtkActionGroup *action_group = NULL;
395 GError *error = NULL;
396 GdkPixbuf *window_icon;
398 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(obj);
399 parent_priv = MODEST_WINDOW_GET_PRIVATE(obj);
400 parent_priv->ui_manager = gtk_ui_manager_new();
402 action_group = gtk_action_group_new ("ModestMsgViewWindowActions");
403 gtk_action_group_set_translation_domain (action_group, GETTEXT_PACKAGE);
405 /* Add common actions */
406 gtk_action_group_add_actions (action_group,
407 modest_action_entries,
408 G_N_ELEMENTS (modest_action_entries),
410 gtk_action_group_add_toggle_actions (action_group,
411 msg_view_toggle_action_entries,
412 G_N_ELEMENTS (msg_view_toggle_action_entries),
415 gtk_ui_manager_insert_action_group (parent_priv->ui_manager, action_group, 0);
416 g_object_unref (action_group);
418 /* Load the UI definition */
419 gtk_ui_manager_add_ui_from_file (parent_priv->ui_manager, MODEST_UIDIR "modest-msg-view-window-ui.xml",
422 g_printerr ("modest: could not merge modest-msg-view-window-ui.xml: %s\n", error->message);
423 g_error_free (error);
428 /* Add accelerators */
429 gtk_window_add_accel_group (GTK_WINDOW (obj),
430 gtk_ui_manager_get_accel_group (parent_priv->ui_manager));
432 priv->is_search_result = FALSE;
433 priv->is_outbox = FALSE;
435 priv->msg_view = NULL;
436 priv->header_model = NULL;
437 priv->header_folder_id = NULL;
438 priv->clipboard_change_handler = 0;
439 priv->queue_change_handler = 0;
440 priv->account_removed_handler = 0;
441 priv->row_changed_handler = 0;
442 priv->row_deleted_handler = 0;
443 priv->row_inserted_handler = 0;
444 priv->rows_reordered_handler = 0;
445 priv->progress_hint = FALSE;
446 priv->fetching_images = 0;
448 priv->optimized_view = FALSE;
449 priv->purge_timeout = 0;
450 priv->remove_attachment_banner = NULL;
451 priv->msg_uid = NULL;
452 priv->other_body = NULL;
454 priv->sighandlers = NULL;
457 init_window (MODEST_MSG_VIEW_WINDOW(obj));
459 /* Set window icon */
460 window_icon = modest_platform_get_icon (MODEST_APP_MSG_VIEW_ICON, MODEST_ICON_SIZE_BIG);
462 gtk_window_set_icon (GTK_WINDOW (obj), window_icon);
463 g_object_unref (window_icon);
466 hildon_program_add_window (hildon_program_get_instance(),
473 set_toolbar_transfer_mode (ModestMsgViewWindow *self)
475 ModestMsgViewWindowPrivate *priv = NULL;
477 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self), FALSE);
479 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(self);
481 set_progress_hint (self, TRUE);
487 update_progress_hint (ModestMsgViewWindow *self)
489 ModestMsgViewWindowPrivate *priv;
490 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(self);
492 if (GTK_WIDGET_VISIBLE (self)) {
493 hildon_gtk_window_set_progress_indicator (GTK_WINDOW (self),
494 (priv->progress_hint || (priv->fetching_images > 0))?1:0);
499 set_progress_hint (ModestMsgViewWindow *self,
502 ModestWindowPrivate *parent_priv;
503 ModestMsgViewWindowPrivate *priv;
505 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self));
507 parent_priv = MODEST_WINDOW_GET_PRIVATE(self);
508 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(self);
510 /* Sets current progress hint */
511 priv->progress_hint = enabled;
513 update_progress_hint (self);
519 init_window (ModestMsgViewWindow *obj)
521 GtkWidget *main_vbox;
522 ModestMsgViewWindowPrivate *priv;
524 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(obj);
526 priv->msg_view = GTK_WIDGET (tny_platform_factory_new_msg_view (modest_tny_platform_factory_get_instance ()));
527 modest_msg_view_set_shadow_type (MODEST_MSG_VIEW (priv->msg_view), GTK_SHADOW_NONE);
528 main_vbox = gtk_vbox_new (FALSE, 6);
529 priv->main_scroll = hildon_pannable_area_new ();
530 gtk_container_add (GTK_CONTAINER (priv->main_scroll), priv->msg_view);
531 gtk_box_pack_start (GTK_BOX(main_vbox), priv->main_scroll, TRUE, TRUE, 0);
532 gtk_container_add (GTK_CONTAINER(obj), main_vbox);
534 priv->find_toolbar = hildon_find_toolbar_new (NULL);
535 hildon_window_add_toolbar (HILDON_WINDOW (obj), GTK_TOOLBAR (priv->find_toolbar));
536 gtk_widget_set_no_show_all (priv->find_toolbar, TRUE);
538 /* NULL-ize fields if the window is destroyed */
539 g_signal_connect (priv->msg_view, "destroy", G_CALLBACK (gtk_widget_destroyed), &(priv->msg_view));
541 gtk_widget_show_all (GTK_WIDGET(main_vbox));
545 modest_msg_view_window_disconnect_signals (ModestWindow *self)
547 ModestMsgViewWindowPrivate *priv;
548 GtkWidget *header_view = NULL;
549 GtkWindow *parent_window = NULL;
551 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
553 if (gtk_clipboard_get (GDK_SELECTION_PRIMARY) &&
554 g_signal_handler_is_connected (gtk_clipboard_get (GDK_SELECTION_PRIMARY),
555 priv->clipboard_change_handler))
556 g_signal_handler_disconnect (gtk_clipboard_get (GDK_SELECTION_PRIMARY),
557 priv->clipboard_change_handler);
559 if (g_signal_handler_is_connected (G_OBJECT (modest_runtime_get_mail_operation_queue ()),
560 priv->queue_change_handler))
561 g_signal_handler_disconnect (G_OBJECT (modest_runtime_get_mail_operation_queue ()),
562 priv->queue_change_handler);
564 if (g_signal_handler_is_connected (G_OBJECT (modest_runtime_get_account_store ()),
565 priv->account_removed_handler))
566 g_signal_handler_disconnect (G_OBJECT (modest_runtime_get_account_store ()),
567 priv->account_removed_handler);
569 if (priv->header_model) {
570 if (g_signal_handler_is_connected(G_OBJECT (priv->header_model),
571 priv->row_changed_handler))
572 g_signal_handler_disconnect(G_OBJECT (priv->header_model),
573 priv->row_changed_handler);
575 if (g_signal_handler_is_connected(G_OBJECT (priv->header_model),
576 priv->row_deleted_handler))
577 g_signal_handler_disconnect(G_OBJECT (priv->header_model),
578 priv->row_deleted_handler);
580 if (g_signal_handler_is_connected(G_OBJECT (priv->header_model),
581 priv->row_inserted_handler))
582 g_signal_handler_disconnect(G_OBJECT (priv->header_model),
583 priv->row_inserted_handler);
585 if (g_signal_handler_is_connected(G_OBJECT (priv->header_model),
586 priv->rows_reordered_handler))
587 g_signal_handler_disconnect(G_OBJECT (priv->header_model),
588 priv->rows_reordered_handler);
591 modest_signal_mgr_disconnect_all_and_destroy (priv->sighandlers);
592 priv->sighandlers = NULL;
594 parent_window = gtk_window_get_transient_for (GTK_WINDOW (self));
595 if (parent_window && MODEST_IS_HEADER_WINDOW (parent_window)) {
596 header_view = GTK_WIDGET (modest_header_window_get_header_view (MODEST_HEADER_WINDOW (parent_window)));
598 modest_header_view_remove_observer(MODEST_HEADER_VIEW (header_view),
599 MODEST_HEADER_VIEW_OBSERVER(self));
605 modest_msg_view_window_finalize (GObject *obj)
607 ModestMsgViewWindowPrivate *priv;
609 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (obj);
611 /* Sanity check: shouldn't be needed, the window mgr should
612 call this function before */
613 modest_msg_view_window_disconnect_signals (MODEST_WINDOW (obj));
615 if (priv->other_body != NULL) {
616 g_object_unref (priv->other_body);
617 priv->other_body = NULL;
620 if (priv->header_model != NULL) {
621 g_object_unref (priv->header_model);
622 priv->header_model = NULL;
625 if (priv->remove_attachment_banner) {
626 gtk_widget_destroy (priv->remove_attachment_banner);
627 g_object_unref (priv->remove_attachment_banner);
628 priv->remove_attachment_banner = NULL;
631 if (priv->purge_timeout > 0) {
632 g_source_remove (priv->purge_timeout);
633 priv->purge_timeout = 0;
636 if (priv->row_reference) {
637 gtk_tree_row_reference_free (priv->row_reference);
638 priv->row_reference = NULL;
641 if (priv->next_row_reference) {
642 gtk_tree_row_reference_free (priv->next_row_reference);
643 priv->next_row_reference = NULL;
647 g_free (priv->msg_uid);
648 priv->msg_uid = NULL;
651 G_OBJECT_CLASS(parent_class)->finalize (obj);
655 select_next_valid_row (GtkTreeModel *model,
656 GtkTreeRowReference **row_reference,
660 GtkTreeIter tmp_iter;
662 GtkTreePath *next = NULL;
663 gboolean retval = FALSE, finished;
665 g_return_val_if_fail (gtk_tree_row_reference_valid (*row_reference), FALSE);
667 path = gtk_tree_row_reference_get_path (*row_reference);
668 gtk_tree_model_get_iter (model, &tmp_iter, path);
669 gtk_tree_row_reference_free (*row_reference);
670 *row_reference = NULL;
674 TnyHeader *header = NULL;
676 if (gtk_tree_model_iter_next (model, &tmp_iter)) {
677 gtk_tree_model_get (model, &tmp_iter,
678 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
682 if (msg_is_visible (header, is_outbox)) {
683 next = gtk_tree_model_get_path (model, &tmp_iter);
684 *row_reference = gtk_tree_row_reference_new (model, next);
685 gtk_tree_path_free (next);
689 g_object_unref (header);
692 } else if (cycle && gtk_tree_model_get_iter_first (model, &tmp_iter)) {
693 next = gtk_tree_model_get_path (model, &tmp_iter);
695 /* Ensure that we are not selecting the same */
696 if (gtk_tree_path_compare (path, next) != 0) {
697 gtk_tree_model_get (model, &tmp_iter,
698 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
701 if (msg_is_visible (header, is_outbox)) {
702 *row_reference = gtk_tree_row_reference_new (model, next);
706 g_object_unref (header);
710 /* If we ended up in the same message
711 then there is no valid next
715 gtk_tree_path_free (next);
717 /* If there are no more messages and we don't
718 want to start again in the first one then
719 there is no valid next message */
725 gtk_tree_path_free (path);
730 /* TODO: This should be in _init(), with the parameters as properties. */
732 modest_msg_view_window_construct (ModestMsgViewWindow *self,
733 const gchar *modest_account_name,
734 const gchar *mailbox,
735 const gchar *msg_uid)
738 ModestMsgViewWindowPrivate *priv = NULL;
739 ModestWindowPrivate *parent_priv = NULL;
740 ModestDimmingRulesGroup *toolbar_rules_group = NULL;
741 ModestDimmingRulesGroup *clipboard_rules_group = NULL;
743 obj = G_OBJECT (self);
744 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(obj);
745 parent_priv = MODEST_WINDOW_GET_PRIVATE(obj);
747 priv->msg_uid = g_strdup (msg_uid);
750 parent_priv->menubar = NULL;
752 toolbar_rules_group = modest_dimming_rules_group_new (MODEST_DIMMING_RULES_TOOLBAR, TRUE);
753 clipboard_rules_group = modest_dimming_rules_group_new (MODEST_DIMMING_RULES_CLIPBOARD, FALSE);
756 /* Add common dimming rules */
757 modest_dimming_rules_group_add_rules (toolbar_rules_group,
758 modest_msg_view_toolbar_dimming_entries,
759 G_N_ELEMENTS (modest_msg_view_toolbar_dimming_entries),
760 MODEST_WINDOW (self));
761 modest_dimming_rules_group_add_rules (clipboard_rules_group,
762 modest_msg_view_clipboard_dimming_entries,
763 G_N_ELEMENTS (modest_msg_view_clipboard_dimming_entries),
764 MODEST_WINDOW (self));
766 /* Insert dimming rules group for this window */
767 modest_ui_dimming_manager_insert_rules_group (parent_priv->ui_dimming_manager, toolbar_rules_group);
768 modest_ui_dimming_manager_insert_rules_group (parent_priv->ui_dimming_manager, clipboard_rules_group);
769 g_object_unref (toolbar_rules_group);
770 g_object_unref (clipboard_rules_group);
772 /* g_signal_connect (G_OBJECT(obj), "delete-event", G_CALLBACK(on_delete_event), obj); */
774 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);
775 g_signal_connect (G_OBJECT(priv->msg_view), "activate_link",
776 G_CALLBACK (modest_ui_actions_on_msg_link_clicked), obj);
777 g_signal_connect (G_OBJECT(priv->msg_view), "link_hover",
778 G_CALLBACK (modest_ui_actions_on_msg_link_hover), obj);
779 g_signal_connect (G_OBJECT(priv->msg_view), "attachment_clicked",
780 G_CALLBACK (modest_ui_actions_on_msg_attachment_clicked), obj);
781 g_signal_connect (G_OBJECT(priv->msg_view), "recpt_activated",
782 G_CALLBACK (modest_ui_actions_on_msg_recpt_activated), obj);
783 g_signal_connect (G_OBJECT(priv->msg_view), "show_details",
784 G_CALLBACK (modest_ui_actions_on_details), obj);
785 g_signal_connect (G_OBJECT(priv->msg_view), "link_contextual",
786 G_CALLBACK (modest_ui_actions_on_msg_link_contextual), obj);
787 g_signal_connect (G_OBJECT (priv->msg_view), "fetch_image",
788 G_CALLBACK (on_fetch_image), obj);
790 g_signal_connect (G_OBJECT (obj), "key-release-event",
791 G_CALLBACK (modest_msg_view_window_key_event),
794 g_signal_connect (G_OBJECT (obj), "key-press-event",
795 G_CALLBACK (modest_msg_view_window_key_event),
798 g_signal_connect (G_OBJECT (obj), "move-focus",
799 G_CALLBACK (on_move_focus), obj);
801 g_signal_connect (G_OBJECT (obj), "map-event",
802 G_CALLBACK (_modest_msg_view_window_map_event),
805 /* Mail Operation Queue */
806 priv->queue_change_handler = g_signal_connect (G_OBJECT (modest_runtime_get_mail_operation_queue ()),
808 G_CALLBACK (on_queue_changed),
811 /* Account manager */
812 priv->account_removed_handler = g_signal_connect (G_OBJECT (modest_runtime_get_account_store ()),
814 G_CALLBACK(on_account_removed),
817 modest_window_set_active_account (MODEST_WINDOW(obj), modest_account_name);
818 modest_window_set_active_mailbox (MODEST_WINDOW(obj), mailbox);
820 g_signal_connect (G_OBJECT (priv->find_toolbar), "close", G_CALLBACK (modest_msg_view_window_find_toolbar_close), obj);
821 g_signal_connect (G_OBJECT (priv->find_toolbar), "search", G_CALLBACK (modest_msg_view_window_find_toolbar_search), obj);
822 priv->last_search = NULL;
824 modest_msg_view_window_show_toolbar (MODEST_WINDOW (obj), TRUE);
826 /* Init the clipboard actions dim status */
827 modest_msg_view_grab_focus(MODEST_MSG_VIEW (priv->msg_view));
829 update_window_title (MODEST_MSG_VIEW_WINDOW (obj));
834 /* FIXME: parameter checks */
836 modest_msg_view_window_new_with_header_model (TnyMsg *msg,
837 const gchar *modest_account_name,
838 const gchar *mailbox,
839 const gchar *msg_uid,
841 GtkTreeRowReference *row_reference)
843 ModestMsgViewWindow *window = NULL;
844 ModestMsgViewWindowPrivate *priv = NULL;
845 TnyFolder *header_folder = NULL;
846 ModestHeaderView *header_view = NULL;
847 ModestWindowMgr *mgr = NULL;
850 modest_tny_mime_part_to_string (TNY_MIME_PART (msg), 0);
853 mgr = modest_runtime_get_window_mgr ();
854 window = MODEST_MSG_VIEW_WINDOW (modest_window_mgr_get_msg_view_window (mgr));
855 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), NULL);
857 modest_msg_view_window_construct (window, modest_account_name, mailbox, msg_uid);
859 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
861 /* Remember the message list's TreeModel so we can detect changes
862 * and change the list selection when necessary: */
863 header_folder = modest_header_view_get_folder (header_view);
865 priv->is_outbox = (modest_tny_folder_guess_folder_type (header_folder) ==
866 TNY_FOLDER_TYPE_OUTBOX);
867 priv->header_folder_id = tny_folder_get_id (header_folder);
868 g_object_unref(header_folder);
871 /* Setup row references and connect signals */
872 priv->header_model = g_object_ref (model);
875 priv->row_reference = gtk_tree_row_reference_copy (row_reference);
876 priv->next_row_reference = gtk_tree_row_reference_copy (row_reference);
877 select_next_valid_row (model, &(priv->next_row_reference), TRUE, priv->is_outbox);
879 priv->row_reference = NULL;
880 priv->next_row_reference = NULL;
883 /* Connect signals */
884 priv->row_changed_handler =
885 g_signal_connect (GTK_TREE_MODEL(model), "row-changed",
886 G_CALLBACK(modest_msg_view_window_on_row_changed),
888 priv->row_deleted_handler =
889 g_signal_connect (GTK_TREE_MODEL(model), "row-deleted",
890 G_CALLBACK(modest_msg_view_window_on_row_deleted),
892 priv->row_inserted_handler =
893 g_signal_connect (GTK_TREE_MODEL(model), "row-inserted",
894 G_CALLBACK(modest_msg_view_window_on_row_inserted),
896 priv->rows_reordered_handler =
897 g_signal_connect(GTK_TREE_MODEL(model), "rows-reordered",
898 G_CALLBACK(modest_msg_view_window_on_row_reordered),
901 if (header_view != NULL){
902 modest_header_view_add_observer(header_view,
903 MODEST_HEADER_VIEW_OBSERVER(window));
906 tny_msg_view_set_msg (TNY_MSG_VIEW (priv->msg_view), msg);
907 update_window_title (MODEST_MSG_VIEW_WINDOW (window));
909 /* gtk_widget_show_all (GTK_WIDGET (window)); */
910 modest_msg_view_window_update_priority (window);
911 /* Check dimming rules */
912 modest_ui_actions_check_toolbar_dimming_rules (MODEST_WINDOW (window));
913 modest_ui_actions_check_menu_dimming_rules (MODEST_WINDOW (window));
914 modest_window_check_dimming_rules_group (MODEST_WINDOW (window), MODEST_DIMMING_RULES_CLIPBOARD);
916 return MODEST_WINDOW(window);
920 modest_msg_view_window_new_from_header_view (ModestHeaderView *header_view,
921 const gchar *modest_account_name,
922 const gchar *mailbox,
923 const gchar *msg_uid,
924 GtkTreeRowReference *row_reference)
926 ModestMsgViewWindow *window = NULL;
927 ModestMsgViewWindowPrivate *priv = NULL;
928 TnyFolder *header_folder = NULL;
929 ModestWindowMgr *mgr = NULL;
933 mgr = modest_runtime_get_window_mgr ();
934 window = MODEST_MSG_VIEW_WINDOW (modest_window_mgr_get_msg_view_window (mgr));
935 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), NULL);
937 modest_msg_view_window_construct (window, modest_account_name, mailbox, msg_uid);
939 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
941 /* Remember the message list's TreeModel so we can detect changes
942 * and change the list selection when necessary: */
944 if (header_view != NULL){
945 header_folder = modest_header_view_get_folder(header_view);
946 /* This could happen if the header folder was
947 unseleted before opening this msg window (for
948 example if the user selects an account in the
949 folder view of the main window */
951 priv->is_outbox = (modest_tny_folder_guess_folder_type (header_folder) ==
952 TNY_FOLDER_TYPE_OUTBOX);
953 priv->header_folder_id = tny_folder_get_id(header_folder);
954 g_object_unref(header_folder);
958 /* Setup row references and connect signals */
959 priv->header_model = gtk_tree_view_get_model (GTK_TREE_VIEW (header_view));
960 g_object_ref (priv->header_model);
963 priv->row_reference = gtk_tree_row_reference_copy (row_reference);
964 priv->next_row_reference = gtk_tree_row_reference_copy (row_reference);
965 select_next_valid_row (priv->header_model, &(priv->next_row_reference), TRUE, priv->is_outbox);
967 priv->row_reference = NULL;
968 priv->next_row_reference = NULL;
971 /* Connect signals */
972 priv->row_changed_handler =
973 g_signal_connect (GTK_TREE_MODEL(priv->header_model), "row-changed",
974 G_CALLBACK(modest_msg_view_window_on_row_changed),
976 priv->row_deleted_handler =
977 g_signal_connect (GTK_TREE_MODEL(priv->header_model), "row-deleted",
978 G_CALLBACK(modest_msg_view_window_on_row_deleted),
980 priv->row_inserted_handler =
981 g_signal_connect (GTK_TREE_MODEL(priv->header_model), "row-inserted",
982 G_CALLBACK(modest_msg_view_window_on_row_inserted),
984 priv->rows_reordered_handler =
985 g_signal_connect(GTK_TREE_MODEL(priv->header_model), "rows-reordered",
986 G_CALLBACK(modest_msg_view_window_on_row_reordered),
989 if (header_view != NULL){
990 modest_header_view_add_observer(header_view,
991 MODEST_HEADER_VIEW_OBSERVER(window));
994 tny_msg_view_set_msg (TNY_MSG_VIEW (priv->msg_view), NULL);
996 path = gtk_tree_row_reference_get_path (row_reference);
997 if (gtk_tree_model_get_iter (priv->header_model, &iter, path)) {
999 gtk_tree_model_get (priv->header_model, &iter,
1000 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
1002 message_reader (window, priv, header, row_reference);
1003 g_object_unref (header);
1005 gtk_tree_path_free (path);
1007 /* Check dimming rules */
1008 modest_ui_actions_check_toolbar_dimming_rules (MODEST_WINDOW (window));
1009 modest_ui_actions_check_menu_dimming_rules (MODEST_WINDOW (window));
1010 modest_window_check_dimming_rules_group (MODEST_WINDOW (window), MODEST_DIMMING_RULES_CLIPBOARD);
1012 return MODEST_WINDOW(window);
1016 modest_msg_view_window_new_for_search_result (TnyMsg *msg,
1017 const gchar *modest_account_name,
1018 const gchar *mailbox,
1019 const gchar *msg_uid)
1021 ModestMsgViewWindow *window = NULL;
1022 ModestMsgViewWindowPrivate *priv = NULL;
1023 ModestWindowMgr *mgr = NULL;
1025 mgr = modest_runtime_get_window_mgr ();
1026 window = MODEST_MSG_VIEW_WINDOW (modest_window_mgr_get_msg_view_window (mgr));
1027 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), NULL);
1028 modest_msg_view_window_construct (window, modest_account_name, mailbox, msg_uid);
1030 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1032 /* Remember that this is a search result,
1033 * so we can disable some UI appropriately: */
1034 priv->is_search_result = TRUE;
1036 tny_msg_view_set_msg (TNY_MSG_VIEW (priv->msg_view), msg);
1038 update_window_title (window);
1039 /* gtk_widget_show_all (GTK_WIDGET (window));*/
1040 modest_msg_view_window_update_priority (window);
1042 /* Check dimming rules */
1043 modest_ui_actions_check_toolbar_dimming_rules (MODEST_WINDOW (window));
1044 modest_ui_actions_check_menu_dimming_rules (MODEST_WINDOW (window));
1045 modest_window_check_dimming_rules_group (MODEST_WINDOW (window), MODEST_DIMMING_RULES_CLIPBOARD);
1047 return MODEST_WINDOW(window);
1051 modest_msg_view_window_is_other_body (ModestMsgViewWindow *self)
1053 ModestMsgViewWindowPrivate *priv = NULL;
1055 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self), FALSE);
1056 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
1058 return (priv->other_body != NULL);
1062 modest_msg_view_window_new_with_other_body (TnyMsg *msg,
1063 TnyMimePart *other_body,
1064 const gchar *modest_account_name,
1065 const gchar *mailbox,
1066 const gchar *msg_uid)
1068 GObject *obj = NULL;
1069 ModestMsgViewWindowPrivate *priv;
1070 ModestWindowMgr *mgr = NULL;
1072 g_return_val_if_fail (msg, NULL);
1073 mgr = modest_runtime_get_window_mgr ();
1074 obj = G_OBJECT (modest_window_mgr_get_msg_view_window (mgr));
1075 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (obj);
1076 modest_msg_view_window_construct (MODEST_MSG_VIEW_WINDOW (obj),
1077 modest_account_name, mailbox, msg_uid);
1080 priv->other_body = g_object_ref (other_body);
1081 modest_msg_view_set_msg_with_other_body (MODEST_MSG_VIEW (priv->msg_view), msg, other_body);
1083 tny_msg_view_set_msg (TNY_MSG_VIEW (priv->msg_view), msg);
1085 update_window_title (MODEST_MSG_VIEW_WINDOW (obj));
1087 /* gtk_widget_show_all (GTK_WIDGET (obj)); */
1089 /* Check dimming rules */
1090 modest_ui_actions_check_toolbar_dimming_rules (MODEST_WINDOW (obj));
1091 modest_ui_actions_check_menu_dimming_rules (MODEST_WINDOW (obj));
1092 modest_window_check_dimming_rules_group (MODEST_WINDOW (obj), MODEST_DIMMING_RULES_CLIPBOARD);
1094 return MODEST_WINDOW(obj);
1098 modest_msg_view_window_new_for_attachment (TnyMsg *msg,
1099 const gchar *modest_account_name,
1100 const gchar *mailbox,
1101 const gchar *msg_uid)
1103 return modest_msg_view_window_new_with_other_body (msg, NULL, modest_account_name, mailbox, msg_uid);
1107 modest_msg_view_window_on_row_changed (GtkTreeModel *header_model,
1110 ModestMsgViewWindow *window)
1112 check_dimming_rules_after_change (window);
1116 modest_msg_view_window_on_row_deleted(GtkTreeModel *header_model,
1118 ModestMsgViewWindow *window)
1120 check_dimming_rules_after_change (window);
1122 /* The window could have dissapeared */
1125 check_dimming_rules_after_change (ModestMsgViewWindow *window)
1127 modest_ui_actions_check_menu_dimming_rules (MODEST_WINDOW (window));
1128 modest_ui_actions_check_toolbar_dimming_rules (MODEST_WINDOW (window));
1132 /* On insertions we check if the folder still has the message we are
1133 * showing or do not. If do not, we do nothing. Which means we are still
1134 * not attached to any header folder and thus next/prev buttons are
1135 * still dimmed. Once the message that is shown by msg-view is found, the
1136 * new model of header-view will be attached and the references will be set.
1137 * On each further insertions dimming rules will be checked. However
1138 * this requires extra CPU time at least works.
1139 * (An message might be deleted from TnyFolder and thus will not be
1140 * inserted into the model again for example if it is removed by the
1141 * imap server and the header view is refreshed.)
1144 modest_msg_view_window_on_row_inserted (GtkTreeModel *model,
1145 GtkTreePath *tree_path,
1146 GtkTreeIter *tree_iter,
1147 ModestMsgViewWindow *window)
1149 ModestMsgViewWindowPrivate *priv = NULL;
1150 TnyHeader *header = NULL;
1152 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window));
1153 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1155 g_assert (model == priv->header_model);
1157 /* Check if the newly inserted message is the same we are actually
1158 * showing. IF not, we should remain detached from the header model
1159 * and thus prev and next toolbar buttons should remain dimmed. */
1160 gtk_tree_model_get (model, tree_iter,
1161 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
1164 if (TNY_IS_HEADER (header)) {
1167 uid = modest_tny_folder_get_header_unique_id (header);
1168 if (!g_str_equal(priv->msg_uid, uid)) {
1169 check_dimming_rules_after_change (window);
1171 g_object_unref (G_OBJECT(header));
1175 g_object_unref(G_OBJECT(header));
1178 if (priv->row_reference) {
1179 gtk_tree_row_reference_free (priv->row_reference);
1182 /* Setup row_reference for the actual msg. */
1183 priv->row_reference = gtk_tree_row_reference_new (priv->header_model, tree_path);
1184 if (priv->row_reference == NULL) {
1185 g_warning("No reference for msg header item.");
1189 /* Now set up next_row_reference. */
1190 if (priv->next_row_reference) {
1191 gtk_tree_row_reference_free (priv->next_row_reference);
1194 priv->next_row_reference = gtk_tree_row_reference_copy (priv->row_reference);
1195 select_next_valid_row (priv->header_model,
1196 &(priv->next_row_reference), FALSE, priv->is_outbox);
1198 /* Connect the remaining callbacks to become able to detect
1199 * changes in header-view. */
1200 priv->row_changed_handler =
1201 g_signal_connect (priv->header_model, "row-changed",
1202 G_CALLBACK (modest_msg_view_window_on_row_changed),
1204 priv->row_deleted_handler =
1205 g_signal_connect (priv->header_model, "row-deleted",
1206 G_CALLBACK (modest_msg_view_window_on_row_deleted),
1208 priv->rows_reordered_handler =
1209 g_signal_connect (priv->header_model, "rows-reordered",
1210 G_CALLBACK (modest_msg_view_window_on_row_reordered),
1213 check_dimming_rules_after_change (window);
1217 modest_msg_view_window_on_row_reordered (GtkTreeModel *header_model,
1221 ModestMsgViewWindow *window)
1223 ModestMsgViewWindowPrivate *priv = NULL;
1224 gboolean already_changed = FALSE;
1226 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(window);
1228 /* If the current row was reordered select the proper next
1229 valid row. The same if the next row reference changes */
1230 if (priv->row_reference &&
1231 gtk_tree_row_reference_valid (priv->row_reference)) {
1233 path = gtk_tree_row_reference_get_path (priv->row_reference);
1234 if (gtk_tree_path_compare (path, arg1) == 0) {
1235 if (priv->next_row_reference) {
1236 gtk_tree_row_reference_free (priv->next_row_reference);
1238 priv->next_row_reference = gtk_tree_row_reference_copy (priv->row_reference);
1239 select_next_valid_row (header_model, &(priv->next_row_reference), FALSE, priv->is_outbox);
1240 already_changed = TRUE;
1242 gtk_tree_path_free (path);
1244 if (!already_changed &&
1245 priv->next_row_reference &&
1246 gtk_tree_row_reference_valid (priv->next_row_reference)) {
1248 path = gtk_tree_row_reference_get_path (priv->next_row_reference);
1249 if (gtk_tree_path_compare (path, arg1) == 0) {
1250 if (priv->next_row_reference) {
1251 gtk_tree_row_reference_free (priv->next_row_reference);
1253 priv->next_row_reference = gtk_tree_row_reference_copy (priv->row_reference);
1254 select_next_valid_row (header_model, &(priv->next_row_reference), FALSE, priv->is_outbox);
1256 gtk_tree_path_free (path);
1258 check_dimming_rules_after_change (window);
1261 /* The modest_msg_view_window_update_model_replaced implements update
1262 * function for ModestHeaderViewObserver. Checks whether the TnyFolder
1263 * actually belongs to the header-view is the same as the TnyFolder of
1264 * the message of msg-view or not. If they are different, there is
1265 * nothing to do. If they are the same, then the model has replaced and
1266 * the reference in msg-view shall be replaced from the old model to
1267 * the new model. In this case the view will be detached from it's
1268 * header folder. From this point the next/prev buttons are dimmed.
1271 modest_msg_view_window_update_model_replaced (ModestHeaderViewObserver *observer,
1272 GtkTreeModel *model,
1273 const gchar *tny_folder_id)
1275 ModestMsgViewWindowPrivate *priv = NULL;
1276 ModestMsgViewWindow *window = NULL;
1278 g_assert(MODEST_IS_HEADER_VIEW_OBSERVER(observer));
1279 g_assert(MODEST_IS_MSG_VIEW_WINDOW(observer));
1281 window = MODEST_MSG_VIEW_WINDOW(observer);
1282 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(window);
1284 /* If there is an other folder in the header-view then we do
1285 * not care about it's model (msg list). Else if the
1286 * header-view shows the folder the msg shown by us is in, we
1287 * shall replace our model reference and make some check. */
1288 if(model == NULL || tny_folder_id == NULL ||
1289 (priv->header_folder_id && !g_str_equal(tny_folder_id, priv->header_folder_id)))
1292 /* Model is changed(replaced), so we should forget the old
1293 * one. Because there might be other references and there
1294 * might be some change on the model even if we unreferenced
1295 * it, we need to disconnect our signals here. */
1296 if (priv->header_model) {
1297 if (g_signal_handler_is_connected(G_OBJECT (priv->header_model),
1298 priv->row_changed_handler))
1299 g_signal_handler_disconnect(G_OBJECT (priv->header_model),
1300 priv->row_changed_handler);
1301 if (g_signal_handler_is_connected(G_OBJECT (priv->header_model),
1302 priv->row_deleted_handler))
1303 g_signal_handler_disconnect(G_OBJECT (priv->header_model),
1304 priv->row_deleted_handler);
1305 if (g_signal_handler_is_connected(G_OBJECT (priv->header_model),
1306 priv->row_inserted_handler))
1307 g_signal_handler_disconnect(G_OBJECT (priv->header_model),
1308 priv->row_inserted_handler);
1309 if (g_signal_handler_is_connected(G_OBJECT (priv->header_model),
1310 priv->rows_reordered_handler))
1311 g_signal_handler_disconnect(G_OBJECT (priv->header_model),
1312 priv->rows_reordered_handler);
1315 if (priv->row_reference)
1316 gtk_tree_row_reference_free (priv->row_reference);
1317 if (priv->next_row_reference)
1318 gtk_tree_row_reference_free (priv->next_row_reference);
1319 g_object_unref(priv->header_model);
1322 priv->row_changed_handler = 0;
1323 priv->row_deleted_handler = 0;
1324 priv->row_inserted_handler = 0;
1325 priv->rows_reordered_handler = 0;
1326 priv->next_row_reference = NULL;
1327 priv->row_reference = NULL;
1328 priv->header_model = NULL;
1331 priv->header_model = g_object_ref (model);
1333 /* Also we must connect to the new model for row insertions.
1334 * Only for insertions now. We will need other ones only after
1335 * the msg is show by msg-view is added to the new model. */
1336 priv->row_inserted_handler =
1337 g_signal_connect (priv->header_model, "row-inserted",
1338 G_CALLBACK(modest_msg_view_window_on_row_inserted),
1341 modest_ui_actions_check_menu_dimming_rules(MODEST_WINDOW(window));
1342 modest_ui_actions_check_toolbar_dimming_rules(MODEST_WINDOW(window));
1346 modest_msg_view_window_toolbar_on_transfer_mode (ModestMsgViewWindow *self)
1348 ModestMsgViewWindowPrivate *priv= NULL;
1350 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self), FALSE);
1351 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
1353 return priv->progress_hint;
1357 modest_msg_view_window_get_header (ModestMsgViewWindow *self)
1359 ModestMsgViewWindowPrivate *priv= NULL;
1361 TnyHeader *header = NULL;
1362 GtkTreePath *path = NULL;
1365 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self), NULL);
1366 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
1368 /* If the message was not obtained from a treemodel,
1369 * for instance if it was opened directly by the search UI:
1371 if (priv->header_model == NULL ||
1372 priv->row_reference == NULL ||
1373 !gtk_tree_row_reference_valid (priv->row_reference)) {
1374 msg = modest_msg_view_window_get_message (self);
1376 header = tny_msg_get_header (msg);
1377 g_object_unref (msg);
1382 /* Get iter of the currently selected message in the header view: */
1383 path = gtk_tree_row_reference_get_path (priv->row_reference);
1384 g_return_val_if_fail (path != NULL, NULL);
1385 gtk_tree_model_get_iter (priv->header_model,
1389 /* Get current message header */
1390 gtk_tree_model_get (priv->header_model, &iter,
1391 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
1394 gtk_tree_path_free (path);
1399 modest_msg_view_window_get_message (ModestMsgViewWindow *self)
1401 ModestMsgViewWindowPrivate *priv;
1403 g_return_val_if_fail (self, NULL);
1405 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(self);
1407 return tny_msg_view_get_msg (TNY_MSG_VIEW (priv->msg_view));
1411 modest_msg_view_window_get_message_uid (ModestMsgViewWindow *self)
1413 ModestMsgViewWindowPrivate *priv;
1415 g_return_val_if_fail (self, NULL);
1417 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
1419 return (const gchar*) priv->msg_uid;
1423 modest_msg_view_window_toggle_find_toolbar (GtkToggleAction *toggle,
1426 ModestMsgViewWindow *window = MODEST_MSG_VIEW_WINDOW (data);
1427 ModestMsgViewWindowPrivate *priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1428 ModestWindowPrivate *parent_priv = MODEST_WINDOW_GET_PRIVATE (window);
1432 is_active = gtk_toggle_action_get_active (toggle);
1435 gtk_widget_show (priv->find_toolbar);
1436 hildon_find_toolbar_highlight_entry (HILDON_FIND_TOOLBAR (priv->find_toolbar), TRUE);
1438 gtk_widget_hide (priv->find_toolbar);
1439 modest_msg_view_grab_focus (MODEST_MSG_VIEW (priv->msg_view));
1442 /* update the toggle buttons status */
1443 action = gtk_ui_manager_get_action (parent_priv->ui_manager, "/ToolBar/FindInMessage");
1445 modest_utils_toggle_action_set_active_block_notify (GTK_TOGGLE_ACTION (action), is_active);
1450 modest_msg_view_window_find_toolbar_close (GtkWidget *widget,
1451 ModestMsgViewWindow *obj)
1453 GtkToggleAction *toggle;
1454 ModestWindowPrivate *parent_priv;
1455 ModestMsgViewWindowPrivate *priv;
1457 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (obj);
1458 parent_priv = MODEST_WINDOW_GET_PRIVATE (obj);
1460 toggle = GTK_TOGGLE_ACTION (gtk_ui_manager_get_action (parent_priv->ui_manager, "/ToolBar/FindInMessage"));
1461 gtk_toggle_action_set_active (toggle, FALSE);
1462 modest_msg_view_grab_focus (MODEST_MSG_VIEW (priv->msg_view));
1466 modest_msg_view_window_find_toolbar_search (GtkWidget *widget,
1467 ModestMsgViewWindow *obj)
1469 gchar *current_search;
1470 ModestMsgViewWindowPrivate *priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (obj);
1472 if (modest_mime_part_view_is_empty (MODEST_MIME_PART_VIEW (priv->msg_view))) {
1473 hildon_banner_show_information (NULL, NULL, _("mail_ib_nothing_to_find"));
1477 g_object_get (G_OBJECT (widget), "prefix", ¤t_search, NULL);
1479 if ((current_search == NULL) || (strcmp (current_search, "") == 0)) {
1480 g_free (current_search);
1481 hildon_banner_show_information (NULL, NULL, _CS("ecdg_ib_find_rep_enter_text"));
1485 if ((priv->last_search == NULL) || (strcmp (priv->last_search, current_search) != 0)) {
1487 g_free (priv->last_search);
1488 priv->last_search = g_strdup (current_search);
1489 result = modest_isearch_view_search (MODEST_ISEARCH_VIEW (priv->msg_view),
1492 hildon_banner_show_information (NULL, NULL,
1493 _HL("ckct_ib_find_no_matches"));
1494 g_free (priv->last_search);
1495 priv->last_search = NULL;
1497 hildon_find_toolbar_highlight_entry (HILDON_FIND_TOOLBAR (priv->find_toolbar), TRUE);
1500 if (!modest_isearch_view_search_next (MODEST_ISEARCH_VIEW (priv->msg_view))) {
1501 hildon_banner_show_information (NULL, NULL,
1502 _HL("ckct_ib_find_search_complete"));
1503 g_free (priv->last_search);
1504 priv->last_search = NULL;
1506 hildon_find_toolbar_highlight_entry (HILDON_FIND_TOOLBAR (priv->find_toolbar), TRUE);
1510 g_free (current_search);
1515 modest_msg_view_window_set_zoom (ModestWindow *window,
1518 ModestMsgViewWindowPrivate *priv;
1519 ModestWindowPrivate *parent_priv;
1521 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window));
1523 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1524 parent_priv = MODEST_WINDOW_GET_PRIVATE (window);
1525 modest_zoomable_set_zoom (MODEST_ZOOMABLE (priv->msg_view), zoom);
1530 modest_msg_view_window_get_zoom (ModestWindow *window)
1532 ModestMsgViewWindowPrivate *priv;
1534 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), 1.0);
1536 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1537 return modest_zoomable_get_zoom (MODEST_ZOOMABLE (priv->msg_view));
1541 modest_msg_view_window_zoom_plus (ModestWindow *window)
1544 ModestMsgViewWindowPrivate *priv;
1548 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), 1.0);
1549 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1551 zoom_level = modest_zoomable_get_zoom (MODEST_ZOOMABLE (priv->msg_view));
1553 if (zoom_level >= 2.0) {
1554 hildon_banner_show_information (NULL, NULL,
1555 _CS("ckct_ib_max_zoom_level_reached"));
1557 } else if (zoom_level >= 1.5) {
1559 } else if (zoom_level >= 1.2) {
1561 } else if (zoom_level >= 1.0) {
1563 } else if (zoom_level >= 0.8) {
1565 } else if (zoom_level >= 0.5) {
1571 /* set zoom level */
1572 int_zoom = (gint) rint (zoom_level*100.0+0.1);
1573 banner_text = g_strdup_printf (_HL("wdgt_ib_zoom"), int_zoom);
1574 modest_platform_information_banner (GTK_WIDGET (window), NULL, banner_text);
1575 g_free (banner_text);
1576 modest_zoomable_set_zoom (MODEST_ZOOMABLE (priv->msg_view), zoom_level);
1582 modest_msg_view_window_zoom_minus (ModestWindow *window)
1585 ModestMsgViewWindowPrivate *priv;
1589 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), 1.0);
1590 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1592 zoom_level = modest_zoomable_get_zoom (MODEST_ZOOMABLE (priv->msg_view));
1594 if (zoom_level <= 0.5) {
1595 hildon_banner_show_information (NULL, NULL,
1596 _CS("ckct_ib_min_zoom_level_reached"));
1598 } else if (zoom_level <= 0.8) {
1600 } else if (zoom_level <= 1.0) {
1602 } else if (zoom_level <= 1.2) {
1604 } else if (zoom_level <= 1.5) {
1606 } else if (zoom_level <= 2.0) {
1612 /* set zoom level */
1613 int_zoom = (gint) rint (zoom_level*100.0+0.1);
1614 banner_text = g_strdup_printf (_HL("wdgt_ib_zoom"), int_zoom);
1615 modest_platform_information_banner (GTK_WIDGET (window), NULL, banner_text);
1616 g_free (banner_text);
1617 modest_zoomable_set_zoom (MODEST_ZOOMABLE (priv->msg_view), zoom_level);
1624 modest_msg_view_window_key_event (GtkWidget *window,
1630 focus = gtk_window_get_focus (GTK_WINDOW (window));
1632 /* for the find toolbar case */
1633 if (focus && GTK_IS_ENTRY (focus)) {
1634 if (event->keyval == GDK_BackSpace) {
1636 copy = gdk_event_copy ((GdkEvent *) event);
1637 gtk_widget_event (focus, copy);
1638 gdk_event_free (copy);
1643 if (event->keyval == GDK_Up || event->keyval == GDK_KP_Up ||
1644 event->keyval == GDK_Down || event->keyval == GDK_KP_Down ||
1645 event->keyval == GDK_Page_Up || event->keyval == GDK_KP_Page_Up ||
1646 event->keyval == GDK_Page_Down || event->keyval == GDK_KP_Page_Down ||
1647 event->keyval == GDK_Home || event->keyval == GDK_KP_Home ||
1648 event->keyval == GDK_End || event->keyval == GDK_KP_End) {
1649 /* ModestMsgViewWindowPrivate *priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window); */
1650 /* gboolean return_value; */
1652 if (event->type == GDK_KEY_PRESS) {
1653 GtkScrollType scroll_type;
1655 switch (event->keyval) {
1658 scroll_type = GTK_SCROLL_STEP_UP; break;
1661 scroll_type = GTK_SCROLL_STEP_DOWN; break;
1663 case GDK_KP_Page_Up:
1664 scroll_type = GTK_SCROLL_PAGE_UP; break;
1666 case GDK_KP_Page_Down:
1667 scroll_type = GTK_SCROLL_PAGE_DOWN; break;
1670 scroll_type = GTK_SCROLL_START; break;
1673 scroll_type = GTK_SCROLL_END; break;
1674 default: scroll_type = GTK_SCROLL_NONE;
1677 /* g_signal_emit_by_name (G_OBJECT (priv->main_scroll), "scroll-child", */
1678 /* scroll_type, FALSE, &return_value); */
1689 modest_msg_view_window_last_message_selected (ModestMsgViewWindow *window)
1692 ModestMsgViewWindowPrivate *priv;
1693 GtkTreeIter tmp_iter;
1694 gboolean is_last_selected;
1696 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), TRUE);
1697 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1699 /*if no model (so no rows at all), then virtually we are the last*/
1700 if (!priv->header_model || !priv->row_reference)
1703 if (!gtk_tree_row_reference_valid (priv->row_reference))
1706 path = gtk_tree_row_reference_get_path (priv->row_reference);
1710 is_last_selected = TRUE;
1711 while (is_last_selected) {
1713 gtk_tree_path_next (path);
1714 if (!gtk_tree_model_get_iter (priv->header_model, &tmp_iter, path))
1716 gtk_tree_model_get (priv->header_model, &tmp_iter,
1717 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
1720 if (msg_is_visible (header, priv->is_outbox))
1721 is_last_selected = FALSE;
1722 g_object_unref(G_OBJECT(header));
1725 gtk_tree_path_free (path);
1726 return is_last_selected;
1730 modest_msg_view_window_has_headers_model (ModestMsgViewWindow *window)
1732 ModestMsgViewWindowPrivate *priv;
1734 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), TRUE);
1735 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1737 return priv->header_model != NULL;
1741 modest_msg_view_window_is_search_result (ModestMsgViewWindow *window)
1743 ModestMsgViewWindowPrivate *priv;
1745 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), TRUE);
1746 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1748 return priv->is_search_result;
1752 msg_is_visible (TnyHeader *header, gboolean check_outbox)
1754 if ((tny_header_get_flags(header) & TNY_HEADER_FLAG_DELETED))
1756 if (!check_outbox) {
1759 ModestTnySendQueueStatus status;
1760 status = modest_tny_all_send_queues_get_msg_status (header);
1761 return ((status != MODEST_TNY_SEND_QUEUE_FAILED) &&
1762 (status != MODEST_TNY_SEND_QUEUE_SENDING));
1767 modest_msg_view_window_first_message_selected (ModestMsgViewWindow *window)
1770 ModestMsgViewWindowPrivate *priv;
1771 gboolean is_first_selected;
1772 GtkTreeIter tmp_iter;
1774 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), TRUE);
1775 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1777 /*if no model (so no rows at all), then virtually we are the first*/
1778 if (!priv->header_model || !priv->row_reference)
1781 if (!gtk_tree_row_reference_valid (priv->row_reference))
1784 path = gtk_tree_row_reference_get_path (priv->row_reference);
1788 is_first_selected = TRUE;
1789 while (is_first_selected) {
1791 if(!gtk_tree_path_prev (path))
1793 /* Here the 'if' is needless for logic, but let make sure
1794 * iter is valid for gtk_tree_model_get. */
1795 if (!gtk_tree_model_get_iter (priv->header_model, &tmp_iter, path))
1797 gtk_tree_model_get (priv->header_model, &tmp_iter,
1798 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
1801 if (msg_is_visible (header, priv->is_outbox))
1802 is_first_selected = FALSE;
1803 g_object_unref(G_OBJECT(header));
1806 gtk_tree_path_free (path);
1807 return is_first_selected;
1812 GtkTreeRowReference *row_reference;
1816 message_reader_performer (gboolean canceled,
1818 GtkWindow *parent_window,
1819 TnyAccount *account,
1822 ModestMailOperation *mail_op = NULL;
1823 MsgReaderInfo *info;
1825 info = (MsgReaderInfo *) user_data;
1826 if (canceled || err) {
1827 update_window_title (MODEST_MSG_VIEW_WINDOW (parent_window));
1831 /* Register the header - it'll be unregistered in the callback */
1832 modest_window_mgr_register_header (modest_runtime_get_window_mgr (), info->header, NULL);
1834 /* New mail operation */
1835 mail_op = modest_mail_operation_new_with_error_handling (G_OBJECT(parent_window),
1836 modest_ui_actions_disk_operations_error_handler,
1839 modest_mail_operation_queue_add (modest_runtime_get_mail_operation_queue (), mail_op);
1840 modest_mail_operation_get_msg (mail_op, info->header, TRUE, view_msg_cb, info->row_reference);
1841 g_object_unref (mail_op);
1843 /* Update dimming rules */
1844 modest_ui_actions_check_toolbar_dimming_rules (MODEST_WINDOW (parent_window));
1845 modest_ui_actions_check_menu_dimming_rules (MODEST_WINDOW (parent_window));
1848 /* Frees. The row_reference will be freed by the view_msg_cb callback */
1849 g_object_unref (info->header);
1850 g_slice_free (MsgReaderInfo, info);
1855 * Reads the message whose summary item is @header. It takes care of
1856 * several things, among others:
1858 * If the message was not previously downloaded then ask the user
1859 * before downloading. If there is no connection launch the connection
1860 * dialog. Update toolbar dimming rules.
1862 * Returns: TRUE if the mail operation was started, otherwise if the
1863 * user do not want to download the message, or if the user do not
1864 * want to connect, then the operation is not issued
1867 message_reader (ModestMsgViewWindow *window,
1868 ModestMsgViewWindowPrivate *priv,
1870 GtkTreeRowReference *row_reference)
1872 ModestWindowMgr *mgr;
1873 TnyAccount *account;
1875 MsgReaderInfo *info;
1877 g_return_val_if_fail (row_reference != NULL, FALSE);
1879 /* We set the header from model while we're loading */
1880 tny_header_view_set_header (TNY_HEADER_VIEW (priv->msg_view), header);
1881 gtk_window_set_title (GTK_WINDOW (window), _CS("ckdg_pb_updating"));
1883 mgr = modest_runtime_get_window_mgr ();
1884 /* Msg download completed */
1885 if (!(tny_header_get_flags (header) & TNY_HEADER_FLAG_CACHED)) {
1887 /* Ask the user if he wants to download the message if
1889 if (!tny_device_is_online (modest_runtime_get_device())) {
1890 GtkResponseType response;
1892 response = modest_platform_run_confirmation_dialog (GTK_WINDOW (window),
1893 _("mcen_nc_get_msg"));
1894 if (response == GTK_RESPONSE_CANCEL) {
1895 update_window_title (window);
1899 folder = tny_header_get_folder (header);
1900 info = g_slice_new (MsgReaderInfo);
1901 info->header = g_object_ref (header);
1902 info->row_reference = gtk_tree_row_reference_copy (row_reference);
1904 /* Offer the connection dialog if necessary */
1905 modest_platform_connect_if_remote_and_perform ((GtkWindow *) window,
1907 TNY_FOLDER_STORE (folder),
1908 message_reader_performer,
1910 g_object_unref (folder);
1915 folder = tny_header_get_folder (header);
1916 account = tny_folder_get_account (folder);
1917 info = g_slice_new (MsgReaderInfo);
1918 info->header = g_object_ref (header);
1919 info->row_reference = gtk_tree_row_reference_copy (row_reference);
1921 message_reader_performer (FALSE, NULL, (GtkWindow *) window, account, info);
1922 g_object_unref (account);
1923 g_object_unref (folder);
1929 modest_msg_view_window_select_next_message (ModestMsgViewWindow *window)
1931 ModestMsgViewWindowPrivate *priv;
1932 GtkTreePath *path= NULL;
1933 GtkTreeIter tmp_iter;
1935 gboolean retval = TRUE;
1936 GtkTreeRowReference *row_reference = NULL;
1938 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), FALSE);
1939 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1941 if (!priv->row_reference)
1944 /* Update the next row reference if it's not valid. This could
1945 happen if for example the header which it was pointing to,
1946 was deleted. The best place to do it is in the row-deleted
1947 handler but the tinymail model do not work like the glib
1948 tree models and reports the deletion when the row is still
1950 if (!gtk_tree_row_reference_valid (priv->next_row_reference)) {
1951 if (gtk_tree_row_reference_valid (priv->row_reference)) {
1952 priv->next_row_reference = gtk_tree_row_reference_copy (priv->row_reference);
1953 select_next_valid_row (priv->header_model, &(priv->next_row_reference), FALSE, priv->is_outbox);
1956 if (priv->next_row_reference)
1957 path = gtk_tree_row_reference_get_path (priv->next_row_reference);
1961 row_reference = gtk_tree_row_reference_copy (priv->next_row_reference);
1963 gtk_tree_model_get_iter (priv->header_model,
1966 gtk_tree_path_free (path);
1968 gtk_tree_model_get (priv->header_model, &tmp_iter,
1969 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
1972 /* Read the message & show it */
1973 if (!message_reader (window, priv, header, row_reference)) {
1976 gtk_tree_row_reference_free (row_reference);
1979 g_object_unref (header);
1985 modest_msg_view_window_select_previous_message (ModestMsgViewWindow *window)
1987 ModestMsgViewWindowPrivate *priv = NULL;
1989 gboolean finished = FALSE;
1990 gboolean retval = FALSE;
1992 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), FALSE);
1993 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1995 /* Return inmediatly if there is no header model */
1996 if (!priv->header_model || !priv->row_reference)
1999 path = gtk_tree_row_reference_get_path (priv->row_reference);
2000 while (!finished && gtk_tree_path_prev (path)) {
2004 gtk_tree_model_get_iter (priv->header_model, &iter, path);
2005 gtk_tree_model_get (priv->header_model, &iter,
2006 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
2010 if (msg_is_visible (header, priv->is_outbox)) {
2011 GtkTreeRowReference *row_reference;
2012 row_reference = gtk_tree_row_reference_new (priv->header_model, path);
2013 /* Read the message & show it */
2014 retval = message_reader (window, priv, header, row_reference);
2015 gtk_tree_row_reference_free (row_reference);
2019 g_object_unref (header);
2023 gtk_tree_path_free (path);
2028 view_msg_cb (ModestMailOperation *mail_op,
2035 ModestMsgViewWindow *self = NULL;
2036 ModestMsgViewWindowPrivate *priv = NULL;
2037 GtkTreeRowReference *row_reference = NULL;
2039 /* Unregister the header (it was registered before creating the mail operation) */
2040 modest_window_mgr_unregister_header (modest_runtime_get_window_mgr (), header);
2042 row_reference = (GtkTreeRowReference *) user_data;
2044 gtk_tree_row_reference_free (row_reference);
2045 self = (ModestMsgViewWindow *) modest_mail_operation_get_source (mail_op);
2047 /* Restore window title */
2048 update_window_title (self);
2049 g_object_unref (self);
2054 /* If there was any error */
2055 if (!modest_ui_actions_msg_retrieval_check (mail_op, header, msg)) {
2056 gtk_tree_row_reference_free (row_reference);
2057 self = (ModestMsgViewWindow *) modest_mail_operation_get_source (mail_op);
2059 /* Restore window title */
2060 update_window_title (self);
2061 g_object_unref (self);
2066 /* Get the window */
2067 self = (ModestMsgViewWindow *) modest_mail_operation_get_source (mail_op);
2068 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self));
2069 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
2071 /* Update the row reference */
2072 if (priv->row_reference != NULL) {
2073 gtk_tree_row_reference_free (priv->row_reference);
2074 priv->row_reference = gtk_tree_row_reference_copy (row_reference);
2075 if (priv->next_row_reference != NULL) {
2076 gtk_tree_row_reference_free (priv->next_row_reference);
2078 priv->next_row_reference = gtk_tree_row_reference_copy (priv->row_reference);
2079 select_next_valid_row (priv->header_model, &(priv->next_row_reference), TRUE, priv->is_outbox);
2082 /* Mark header as read */
2083 if (!(tny_header_get_flags (header) & TNY_HEADER_FLAG_SEEN))
2084 tny_header_set_flag (header, TNY_HEADER_FLAG_SEEN);
2086 /* Set new message */
2087 if (priv->msg_view != NULL && TNY_IS_MSG_VIEW (priv->msg_view)) {
2088 tny_msg_view_set_msg (TNY_MSG_VIEW (priv->msg_view), msg);
2089 modest_msg_view_window_update_priority (self);
2090 update_window_title (MODEST_MSG_VIEW_WINDOW (self));
2091 modest_msg_view_grab_focus (MODEST_MSG_VIEW (priv->msg_view));
2094 /* Set the new message uid of the window */
2095 if (priv->msg_uid) {
2096 g_free (priv->msg_uid);
2097 priv->msg_uid = modest_tny_folder_get_header_unique_id (header);
2100 /* Notify the observers */
2101 g_signal_emit (G_OBJECT (self), signals[MSG_CHANGED_SIGNAL],
2102 0, priv->header_model, priv->row_reference);
2105 g_object_unref (self);
2106 gtk_tree_row_reference_free (row_reference);
2110 modest_msg_view_window_get_folder_type (ModestMsgViewWindow *window)
2112 ModestMsgViewWindowPrivate *priv;
2114 TnyFolderType folder_type;
2116 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
2118 folder_type = TNY_FOLDER_TYPE_UNKNOWN;
2120 msg = tny_msg_view_get_msg (TNY_MSG_VIEW (priv->msg_view));
2124 folder = tny_msg_get_folder (msg);
2126 folder_type = modest_tny_folder_guess_folder_type (folder);
2127 g_object_unref (folder);
2129 g_object_unref (msg);
2137 modest_msg_view_window_update_priority (ModestMsgViewWindow *window)
2139 ModestMsgViewWindowPrivate *priv;
2140 TnyHeader *header = NULL;
2141 TnyHeaderFlags flags = 0;
2143 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
2145 if (priv->header_model && priv->row_reference) {
2147 GtkTreePath *path = NULL;
2149 path = gtk_tree_row_reference_get_path (priv->row_reference);
2150 g_return_if_fail (path != NULL);
2151 gtk_tree_model_get_iter (priv->header_model,
2153 gtk_tree_row_reference_get_path (priv->row_reference));
2155 gtk_tree_model_get (priv->header_model, &iter, TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
2157 gtk_tree_path_free (path);
2160 msg = tny_msg_view_get_msg (TNY_MSG_VIEW (priv->msg_view));
2162 header = tny_msg_get_header (msg);
2163 g_object_unref (msg);
2168 flags = tny_header_get_flags (header);
2169 g_object_unref(G_OBJECT(header));
2172 modest_msg_view_set_priority (MODEST_MSG_VIEW(priv->msg_view), flags);
2177 toolbar_resize (ModestMsgViewWindow *self)
2179 ModestMsgViewWindowPrivate *priv = NULL;
2180 ModestWindowPrivate *parent_priv = NULL;
2182 gint static_button_size;
2183 ModestWindowMgr *mgr;
2185 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self));
2186 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
2187 parent_priv = MODEST_WINDOW_GET_PRIVATE(self);
2189 mgr = modest_runtime_get_window_mgr ();
2190 static_button_size = modest_window_mgr_get_fullscreen_mode (mgr)?120:120;
2192 if (parent_priv->toolbar) {
2193 /* left size buttons */
2194 widget = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/ToolbarMessageReply");
2195 gtk_tool_item_set_expand (GTK_TOOL_ITEM (widget), FALSE);
2196 gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (widget), FALSE);
2197 gtk_widget_set_size_request (GTK_WIDGET (widget), static_button_size, -1);
2198 widget = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/ToolbarMessageMoveTo");
2199 gtk_tool_item_set_expand (GTK_TOOL_ITEM (widget), FALSE);
2200 gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (widget), FALSE);
2201 gtk_widget_set_size_request (GTK_WIDGET (widget), static_button_size, -1);
2202 widget = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/ToolbarDeleteMessage");
2203 gtk_tool_item_set_expand (GTK_TOOL_ITEM (widget), FALSE);
2204 gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (widget), FALSE);
2205 gtk_widget_set_size_request (GTK_WIDGET (widget), static_button_size, -1);
2206 widget = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/FindInMessage");
2207 gtk_tool_item_set_expand (GTK_TOOL_ITEM (widget), FALSE);
2208 gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (widget), FALSE);
2209 gtk_widget_set_size_request (GTK_WIDGET (widget), static_button_size, -1);
2211 gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (priv->next_toolitem), TRUE);
2212 gtk_tool_item_set_expand (GTK_TOOL_ITEM (priv->next_toolitem), TRUE);
2213 gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (priv->prev_toolitem), TRUE);
2214 gtk_tool_item_set_expand (GTK_TOOL_ITEM (priv->prev_toolitem), TRUE);
2219 modest_msg_view_window_show_toolbar (ModestWindow *self,
2220 gboolean show_toolbar)
2222 ModestMsgViewWindowPrivate *priv = NULL;
2223 ModestWindowPrivate *parent_priv;
2225 parent_priv = MODEST_WINDOW_GET_PRIVATE(self);
2226 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(self);
2228 /* Set optimized view status */
2229 priv->optimized_view = !show_toolbar;
2231 if (!parent_priv->toolbar) {
2232 parent_priv->toolbar = gtk_ui_manager_get_widget (parent_priv->ui_manager,
2234 gtk_toolbar_set_icon_size (GTK_TOOLBAR (parent_priv->toolbar), HILDON_ICON_SIZE_FINGER);
2235 gtk_widget_set_no_show_all (parent_priv->toolbar, TRUE);
2237 priv->next_toolitem = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/ToolbarMessageNext");
2238 priv->prev_toolitem = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/ToolbarMessageBack");
2239 toolbar_resize (MODEST_MSG_VIEW_WINDOW (self));
2242 hildon_window_add_toolbar (HILDON_WINDOW (self),
2243 GTK_TOOLBAR (parent_priv->toolbar));
2248 /* Quick hack: this prevents toolbar icons "dance" when progress bar show status is changed */
2249 /* TODO: resize mode migth be GTK_RESIZE_QUEUE, in order to avoid unneccesary shows */
2250 gtk_container_set_resize_mode (GTK_CONTAINER(parent_priv->toolbar), GTK_RESIZE_IMMEDIATE);
2252 gtk_widget_show (GTK_WIDGET (parent_priv->toolbar));
2253 if (modest_msg_view_window_transfer_mode_enabled (MODEST_MSG_VIEW_WINDOW (self)))
2254 set_progress_hint (MODEST_MSG_VIEW_WINDOW (self), TRUE);
2256 set_progress_hint (MODEST_MSG_VIEW_WINDOW (self), FALSE);
2259 gtk_widget_set_no_show_all (parent_priv->toolbar, TRUE);
2260 gtk_widget_hide (GTK_WIDGET (parent_priv->toolbar));
2265 modest_msg_view_window_clipboard_owner_change (GtkClipboard *clipboard,
2267 ModestMsgViewWindow *window)
2269 if (!GTK_WIDGET_VISIBLE (window))
2272 modest_window_check_dimming_rules_group (MODEST_WINDOW (window), MODEST_DIMMING_RULES_CLIPBOARD);
2276 modest_msg_view_window_transfer_mode_enabled (ModestMsgViewWindow *self)
2278 ModestMsgViewWindowPrivate *priv;
2280 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self), FALSE);
2281 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(self);
2283 return priv->progress_hint;
2287 observers_empty (ModestMsgViewWindow *self)
2290 ModestMsgViewWindowPrivate *priv;
2291 gboolean is_empty = TRUE;
2292 guint pending_ops = 0;
2294 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(self);
2295 tmp = priv->progress_widgets;
2297 /* Check all observers */
2298 while (tmp && is_empty) {
2299 pending_ops = modest_progress_object_num_pending_operations (MODEST_PROGRESS_OBJECT(tmp->data));
2300 is_empty = pending_ops == 0;
2302 tmp = g_slist_next(tmp);
2309 on_account_removed (TnyAccountStore *account_store,
2310 TnyAccount *account,
2313 /* Do nothing if it's a transport account, because we only
2314 show the messages of a store account */
2315 if (tny_account_get_account_type(account) == TNY_ACCOUNT_TYPE_STORE) {
2316 const gchar *parent_acc = NULL;
2317 const gchar *our_acc = NULL;
2319 our_acc = modest_window_get_active_account (MODEST_WINDOW (user_data));
2320 parent_acc = modest_tny_account_get_parent_modest_account_name_for_server_account (account);
2322 /* Close this window if I'm showing a message of the removed account */
2323 if (our_acc && parent_acc && strcmp (parent_acc, our_acc) == 0)
2324 modest_ui_actions_on_close_window (NULL, MODEST_WINDOW (user_data));
2329 on_mail_operation_started (ModestMailOperation *mail_op,
2332 ModestMsgViewWindow *self;
2333 ModestMailOperationTypeOperation op_type;
2335 ModestMsgViewWindowPrivate *priv;
2336 GObject *source = NULL;
2338 self = MODEST_MSG_VIEW_WINDOW (user_data);
2339 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
2340 op_type = modest_mail_operation_get_type_operation (mail_op);
2341 tmp = priv->progress_widgets;
2342 source = modest_mail_operation_get_source(mail_op);
2343 if (G_OBJECT (self) == source) {
2344 if (op_type == MODEST_MAIL_OPERATION_TYPE_RECEIVE) {
2345 set_toolbar_transfer_mode(self);
2347 modest_progress_object_add_operation (
2348 MODEST_PROGRESS_OBJECT (tmp->data),
2350 tmp = g_slist_next (tmp);
2354 g_object_unref (source);
2358 on_mail_operation_finished (ModestMailOperation *mail_op,
2361 ModestMsgViewWindow *self;
2362 ModestMailOperationTypeOperation op_type;
2364 ModestMsgViewWindowPrivate *priv;
2366 self = MODEST_MSG_VIEW_WINDOW (user_data);
2367 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
2368 op_type = modest_mail_operation_get_type_operation (mail_op);
2369 tmp = priv->progress_widgets;
2371 if (op_type == MODEST_MAIL_OPERATION_TYPE_RECEIVE) {
2373 modest_progress_object_remove_operation (MODEST_PROGRESS_OBJECT (tmp->data),
2375 tmp = g_slist_next (tmp);
2378 /* If no more operations are being observed, NORMAL mode is enabled again */
2379 if (observers_empty (self)) {
2380 set_progress_hint (self, FALSE);
2384 /* Update dimming rules. We have to do this right here
2385 and not in view_msg_cb because at that point the
2386 transfer mode is still enabled so the dimming rule
2387 won't let the user delete the message that has been
2388 readed for example */
2389 check_dimming_rules_after_change (self);
2394 on_queue_changed (ModestMailOperationQueue *queue,
2395 ModestMailOperation *mail_op,
2396 ModestMailOperationQueueNotification type,
2397 ModestMsgViewWindow *self)
2399 ModestMsgViewWindowPrivate *priv;
2401 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
2403 /* If this operations was created by another window, do nothing */
2404 if (!modest_mail_operation_is_mine (mail_op, G_OBJECT(self)))
2407 if (type == MODEST_MAIL_OPERATION_QUEUE_OPERATION_ADDED) {
2408 priv->sighandlers = modest_signal_mgr_connect (priv->sighandlers,
2410 "operation-started",
2411 G_CALLBACK (on_mail_operation_started),
2413 priv->sighandlers = modest_signal_mgr_connect (priv->sighandlers,
2415 "operation-finished",
2416 G_CALLBACK (on_mail_operation_finished),
2418 } else if (type == MODEST_MAIL_OPERATION_QUEUE_OPERATION_REMOVED) {
2419 priv->sighandlers = modest_signal_mgr_disconnect (priv->sighandlers,
2421 "operation-started");
2422 priv->sighandlers = modest_signal_mgr_disconnect (priv->sighandlers,
2424 "operation-finished");
2429 modest_msg_view_window_get_attachments (ModestMsgViewWindow *win)
2431 ModestMsgViewWindowPrivate *priv;
2432 TnyList *selected_attachments = NULL;
2434 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (win), NULL);
2435 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (win);
2437 /* In Hildon 2.2 as there's no selection we assume we have all attachments selected */
2438 selected_attachments = modest_msg_view_get_attachments (MODEST_MSG_VIEW (priv->msg_view));
2440 return selected_attachments;
2444 ModestMsgViewWindow *self;
2446 } DecodeAsyncHelper;
2449 on_decode_to_stream_async_handler (TnyMimePart *mime_part,
2455 DecodeAsyncHelper *helper = (DecodeAsyncHelper *) user_data;
2457 /* It could happen that the window was closed */
2458 if (GTK_WIDGET_VISIBLE (helper->self))
2459 set_progress_hint (helper->self, FALSE);
2461 if (cancelled || err) {
2463 gchar *msg = g_strdup_printf (_KR("cerm_device_memory_full"), "");
2464 modest_platform_information_banner (NULL, NULL, msg);
2470 /* make the file read-only */
2471 g_chmod(helper->file_path, 0444);
2473 /* Activate the file */
2474 modest_platform_activate_file (helper->file_path, tny_mime_part_get_content_type (mime_part));
2478 g_object_unref (helper->self);
2479 g_free (helper->file_path);
2480 g_slice_free (DecodeAsyncHelper, helper);
2484 modest_msg_view_window_view_attachment (ModestMsgViewWindow *window,
2485 TnyMimePart *mime_part)
2487 ModestMsgViewWindowPrivate *priv;
2488 const gchar *msg_uid;
2489 gchar *attachment_uid = NULL;
2490 gint attachment_index = 0;
2491 TnyList *attachments;
2493 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window));
2494 g_return_if_fail (TNY_IS_MIME_PART (mime_part) || (mime_part == NULL));
2495 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
2497 msg_uid = modest_msg_view_window_get_message_uid (MODEST_MSG_VIEW_WINDOW (window));
2498 attachments = modest_msg_view_get_attachments (MODEST_MSG_VIEW (priv->msg_view));
2499 attachment_index = modest_list_index (attachments, (GObject *) mime_part);
2500 g_object_unref (attachments);
2502 if (msg_uid && attachment_index >= 0) {
2503 attachment_uid = g_strdup_printf ("%s/%d", msg_uid, attachment_index);
2506 if (mime_part == NULL) {
2507 gboolean error = FALSE;
2508 TnyList *selected_attachments = modest_msg_view_get_selected_attachments (MODEST_MSG_VIEW (priv->msg_view));
2509 if (selected_attachments == NULL || tny_list_get_length (selected_attachments) == 0) {
2511 } else if (tny_list_get_length (selected_attachments) > 1) {
2512 hildon_banner_show_information (NULL, NULL, _("mcen_ib_unable_to_display_more"));
2516 iter = tny_list_create_iterator (selected_attachments);
2517 mime_part = (TnyMimePart *) tny_iterator_get_current (iter);
2518 g_object_unref (iter);
2520 if (selected_attachments)
2521 g_object_unref (selected_attachments);
2526 g_object_ref (mime_part);
2529 if (tny_mime_part_is_purged (mime_part))
2532 if (!modest_tny_mime_part_is_msg (mime_part) && tny_mime_part_get_filename (mime_part)) {
2533 gchar *filepath = NULL;
2534 const gchar *att_filename = tny_mime_part_get_filename (mime_part);
2535 gboolean show_error_banner = FALSE;
2536 TnyFsStream *temp_stream = NULL;
2537 temp_stream = modest_utils_create_temp_stream (att_filename, attachment_uid,
2540 if (temp_stream != NULL) {
2541 DecodeAsyncHelper *helper;
2543 /* Activate progress hint */
2544 set_progress_hint (window, TRUE);
2546 helper = g_slice_new0 (DecodeAsyncHelper);
2547 helper->self = g_object_ref (window);
2548 helper->file_path = g_strdup (filepath);
2550 tny_mime_part_decode_to_stream_async (mime_part, TNY_STREAM (temp_stream),
2551 on_decode_to_stream_async_handler,
2554 g_object_unref (temp_stream);
2555 /* NOTE: files in the temporary area will be automatically
2556 * cleaned after some time if they are no longer in use */
2559 const gchar *content_type;
2560 /* the file may already exist but it isn't writable,
2561 * let's try to open it anyway */
2562 content_type = tny_mime_part_get_content_type (mime_part);
2563 modest_platform_activate_file (filepath, content_type);
2565 g_warning ("%s: modest_utils_create_temp_stream failed", __FUNCTION__);
2566 show_error_banner = TRUE;
2571 if (show_error_banner)
2572 modest_platform_information_banner (NULL, NULL, _("mail_ib_file_operation_failed"));
2573 } else if (!modest_tny_mime_part_is_msg (mime_part)) {
2574 ModestWindowMgr *mgr;
2575 ModestWindow *msg_win = NULL;
2576 TnyMsg *current_msg;
2580 current_msg = modest_msg_view_window_get_message (MODEST_MSG_VIEW_WINDOW (window));
2581 mgr = modest_runtime_get_window_mgr ();
2582 header = tny_msg_get_header (TNY_MSG (current_msg));
2583 found = modest_window_mgr_find_registered_message_uid (mgr,
2588 g_warning ("window for this body is already being created");
2591 /* it's not found, so create a new window for it */
2592 modest_window_mgr_register_header (mgr, header, attachment_uid); /* register the uid before building the window */
2593 gchar *account = g_strdup (modest_window_get_active_account (MODEST_WINDOW (window)));
2594 const gchar *mailbox = modest_window_get_active_mailbox (MODEST_WINDOW (window));
2596 account = modest_account_mgr_get_default_account (modest_runtime_get_account_mgr ());
2598 msg_win = modest_msg_view_window_new_with_other_body (TNY_MSG (current_msg), TNY_MIME_PART (mime_part),
2599 account, mailbox, attachment_uid);
2601 modest_window_set_zoom (MODEST_WINDOW (msg_win),
2602 modest_window_get_zoom (MODEST_WINDOW (window)));
2603 if (modest_window_mgr_register_window (mgr, msg_win, MODEST_WINDOW (window)))
2604 gtk_widget_show_all (GTK_WIDGET (msg_win));
2606 gtk_widget_destroy (GTK_WIDGET (msg_win));
2608 g_object_unref (current_msg);
2610 /* message attachment */
2611 TnyHeader *header = NULL;
2612 ModestWindowMgr *mgr;
2613 ModestWindow *msg_win = NULL;
2616 header = tny_msg_get_header (TNY_MSG (mime_part));
2617 mgr = modest_runtime_get_window_mgr ();
2618 found = modest_window_mgr_find_registered_header (mgr, header, &msg_win);
2621 /* if it's found, but there is no msg_win, it's probably in the process of being created;
2622 * thus, we don't do anything */
2623 g_warning ("window for is already being created");
2625 /* it's not found, so create a new window for it */
2626 modest_window_mgr_register_header (mgr, header, attachment_uid); /* register the uid before building the window */
2627 gchar *account = g_strdup (modest_window_get_active_account (MODEST_WINDOW (window)));
2628 const gchar *mailbox = modest_window_get_active_mailbox (MODEST_WINDOW (window));
2630 account = modest_account_mgr_get_default_account (modest_runtime_get_account_mgr ());
2631 msg_win = modest_msg_view_window_new_for_attachment (TNY_MSG (mime_part), account,
2632 mailbox, attachment_uid);
2633 modest_window_set_zoom (MODEST_WINDOW (msg_win),
2634 modest_window_get_zoom (MODEST_WINDOW (window)));
2635 if (modest_window_mgr_register_window (mgr, msg_win, MODEST_WINDOW (window)))
2636 gtk_widget_show_all (GTK_WIDGET (msg_win));
2638 gtk_widget_destroy (GTK_WIDGET (msg_win));
2644 g_free (attachment_uid);
2646 g_object_unref (mime_part);
2658 GnomeVFSResult result;
2661 static void save_mime_part_info_free (SaveMimePartInfo *info, gboolean with_struct);
2662 static gboolean idle_save_mime_part_show_result (SaveMimePartInfo *info);
2663 static gpointer save_mime_part_to_file (SaveMimePartInfo *info);
2664 static void save_mime_parts_to_file_with_checks (SaveMimePartInfo *info);
2667 save_mime_part_info_free (SaveMimePartInfo *info, gboolean with_struct)
2671 for (node = info->pairs; node != NULL; node = g_list_next (node)) {
2672 SaveMimePartPair *pair = (SaveMimePartPair *) node->data;
2673 g_free (pair->filename);
2674 g_object_unref (pair->part);
2675 g_slice_free (SaveMimePartPair, pair);
2677 g_list_free (info->pairs);
2680 g_slice_free (SaveMimePartInfo, info);
2685 idle_save_mime_part_show_result (SaveMimePartInfo *info)
2687 if (info->pairs != NULL) {
2688 save_mime_part_to_file (info);
2690 /* This is a GDK lock because we are an idle callback and
2691 * hildon_banner_show_information is or does Gtk+ code */
2693 gdk_threads_enter (); /* CHECKED */
2694 save_mime_part_info_free (info, TRUE);
2695 if (info->result == GNOME_VFS_OK) {
2696 hildon_banner_show_information (NULL, NULL, _CS("sfil_ib_saved"));
2697 } else if (info->result == GNOME_VFS_ERROR_NO_SPACE) {
2698 gchar *msg = g_strdup_printf (_KR("cerm_device_memory_full"), "");
2699 modest_platform_information_banner (NULL, NULL, msg);
2702 hildon_banner_show_information (NULL, NULL, _("mail_ib_file_operation_failed"));
2704 gdk_threads_leave (); /* CHECKED */
2711 save_mime_part_to_file (SaveMimePartInfo *info)
2713 GnomeVFSHandle *handle;
2715 SaveMimePartPair *pair = (SaveMimePartPair *) info->pairs->data;
2717 info->result = gnome_vfs_create (&handle, pair->filename, GNOME_VFS_OPEN_WRITE, FALSE, 0644);
2718 if (info->result == GNOME_VFS_OK) {
2719 GError *error = NULL;
2720 stream = tny_vfs_stream_new (handle);
2721 if (tny_mime_part_decode_to_stream (pair->part, stream, &error) < 0) {
2722 g_warning ("modest: could not save attachment %s: %d (%s)\n", pair->filename, error?error->code:-1, error?error->message:"Unknown error");
2724 if ((error->domain == TNY_ERROR_DOMAIN) &&
2725 (error->code == TNY_IO_ERROR_WRITE) &&
2726 (errno == ENOSPC)) {
2727 info->result = GNOME_VFS_ERROR_NO_SPACE;
2729 info->result = GNOME_VFS_ERROR_IO;
2732 g_object_unref (G_OBJECT (stream));
2733 g_object_unref (pair->part);
2734 g_slice_free (SaveMimePartPair, pair);
2735 info->pairs = g_list_delete_link (info->pairs, info->pairs);
2737 g_warning ("modest: could not create save attachment %s: %s\n", pair->filename, gnome_vfs_result_to_string (info->result));
2738 save_mime_part_info_free (info, FALSE);
2741 g_idle_add ((GSourceFunc) idle_save_mime_part_show_result, info);
2746 save_mime_parts_to_file_with_checks (SaveMimePartInfo *info)
2748 gboolean is_ok = TRUE;
2749 gint replaced_files = 0;
2750 const GList *files = info->pairs;
2753 for (iter = files; (iter != NULL) && (replaced_files < 2); iter = g_list_next(iter)) {
2754 SaveMimePartPair *pair = iter->data;
2755 if (modest_utils_file_exists (pair->filename)) {
2759 if (replaced_files) {
2760 GtkWidget *confirm_overwrite_dialog;
2762 if (replaced_files == 1) {
2763 SaveMimePartPair *pair = files->data;
2764 const gchar *basename = strrchr (pair->filename, G_DIR_SEPARATOR) + 1;
2766 gchar *message = g_strdup_printf ("%s\n%s",
2767 _FM("docm_nc_replace_file"),
2768 (basename) ? basename : "");
2769 confirm_overwrite_dialog = hildon_note_new_confirmation (NULL, message);
2772 confirm_overwrite_dialog = hildon_note_new_confirmation (NULL,
2773 _FM("docm_nc_replace_multiple"));
2775 if (gtk_dialog_run (GTK_DIALOG (confirm_overwrite_dialog)) != GTK_RESPONSE_OK)
2778 gtk_widget_destroy (confirm_overwrite_dialog);
2782 save_mime_part_info_free (info, TRUE);
2784 g_thread_create ((GThreadFunc)save_mime_part_to_file, info, FALSE, NULL);
2790 save_attachments_response (GtkDialog *dialog,
2794 TnyList *mime_parts;
2796 GList *files_to_save = NULL;
2797 gchar *current_folder;
2799 mime_parts = TNY_LIST (user_data);
2801 if (arg1 != GTK_RESPONSE_OK)
2804 chooser_uri = gtk_file_chooser_get_uri (GTK_FILE_CHOOSER (dialog));
2805 current_folder = gtk_file_chooser_get_current_folder_uri (GTK_FILE_CHOOSER (dialog));
2806 if (current_folder && current_folder != '\0') {
2808 modest_conf_set_string (modest_runtime_get_conf (), MODEST_CONF_LATEST_SAVE_ATTACHMENT_PATH,
2809 current_folder,&err);
2811 g_debug ("Error storing latest used folder: %s", err->message);
2815 g_free (current_folder);
2817 if (!modest_utils_folder_writable (chooser_uri)) {
2818 hildon_banner_show_information
2819 (NULL, NULL, _FM("sfil_ib_readonly_location"));
2823 iter = tny_list_create_iterator (mime_parts);
2824 while (!tny_iterator_is_done (iter)) {
2825 TnyMimePart *mime_part = (TnyMimePart *) tny_iterator_get_current (iter);
2827 if ((modest_tny_mime_part_is_attachment_for_modest (mime_part)) &&
2828 !tny_mime_part_is_purged (mime_part) &&
2829 (tny_mime_part_get_filename (mime_part) != NULL)) {
2830 SaveMimePartPair *pair;
2832 pair = g_slice_new0 (SaveMimePartPair);
2834 if (tny_list_get_length (mime_parts) > 1) {
2836 gnome_vfs_escape_slashes (tny_mime_part_get_filename (mime_part));
2837 pair->filename = g_build_filename (chooser_uri, escaped, NULL);
2840 pair->filename = g_strdup (chooser_uri);
2842 pair->part = mime_part;
2843 files_to_save = g_list_prepend (files_to_save, pair);
2845 tny_iterator_next (iter);
2847 g_object_unref (iter);
2849 g_free (chooser_uri);
2851 if (files_to_save != NULL) {
2852 SaveMimePartInfo *info = g_slice_new0 (SaveMimePartInfo);
2853 info->pairs = files_to_save;
2854 info->result = TRUE;
2855 save_mime_parts_to_file_with_checks (info);
2859 /* Free and close the dialog */
2860 g_object_unref (mime_parts);
2861 gtk_widget_destroy (GTK_WIDGET (dialog));
2865 modest_msg_view_window_save_attachments (ModestMsgViewWindow *window,
2866 TnyList *mime_parts)
2868 ModestMsgViewWindowPrivate *priv;
2869 GtkWidget *save_dialog = NULL;
2870 gchar *conf_folder = NULL;
2871 gchar *filename = NULL;
2872 gchar *save_multiple_str = NULL;
2874 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window));
2875 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
2877 if (mime_parts == NULL) {
2878 /* In Hildon 2.2 save and delete operate over all the attachments as there's no
2879 * selection available */
2880 mime_parts = modest_msg_view_get_attachments (MODEST_MSG_VIEW (priv->msg_view));
2881 if (mime_parts && !modest_maemo_utils_select_attachments (GTK_WINDOW (window), mime_parts, FALSE)) {
2882 g_object_unref (mime_parts);
2885 if (mime_parts == NULL || tny_list_get_length (mime_parts) == 0) {
2887 g_object_unref (mime_parts);
2893 g_object_ref (mime_parts);
2896 /* prepare dialog */
2897 if (tny_list_get_length (mime_parts) == 1) {
2899 /* only one attachment selected */
2900 iter = tny_list_create_iterator (mime_parts);
2901 TnyMimePart *mime_part = (TnyMimePart *) tny_iterator_get_current (iter);
2902 g_object_unref (iter);
2903 if (!modest_tny_mime_part_is_msg (mime_part) &&
2904 modest_tny_mime_part_is_attachment_for_modest (mime_part) &&
2905 !tny_mime_part_is_purged (mime_part)) {
2906 filename = g_strdup (tny_mime_part_get_filename (mime_part));
2908 /* TODO: show any error? */
2909 g_warning ("Tried to save a non-file attachment");
2910 g_object_unref (mime_parts);
2913 g_object_unref (mime_part);
2915 save_multiple_str = g_strdup_printf (_FM("sfil_va_number_of_objects_attachments"),
2916 tny_list_get_length (mime_parts));
2919 save_dialog = hildon_file_chooser_dialog_new (GTK_WINDOW (window),
2920 GTK_FILE_CHOOSER_ACTION_SAVE);
2923 conf_folder = modest_conf_get_string (modest_runtime_get_conf (), MODEST_CONF_LATEST_SAVE_ATTACHMENT_PATH, NULL);
2924 if (conf_folder && conf_folder[0] != '\0') {
2925 gtk_file_chooser_set_current_folder_uri (GTK_FILE_CHOOSER (save_dialog), conf_folder);
2928 /* Set the default folder to images folder */
2929 docs_folder = g_build_filename (g_getenv (MYDOCS_ENV), DOCS_FOLDER, NULL);
2930 gtk_file_chooser_set_current_folder (GTK_FILE_CHOOSER (save_dialog), docs_folder);
2931 g_free (docs_folder);
2933 g_free (conf_folder);
2937 gtk_file_chooser_set_current_name (GTK_FILE_CHOOSER (save_dialog),
2942 /* if multiple, set multiple string */
2943 if (save_multiple_str) {
2944 g_object_set (G_OBJECT (save_dialog), "save-multiple", save_multiple_str, NULL);
2945 gtk_window_set_title (GTK_WINDOW (save_dialog), _FM("sfil_ti_save_objects_files"));
2948 /* We must run this asynchronously, because the hildon dialog
2949 performs a gtk_dialog_run by itself which leads to gdk
2951 g_signal_connect (save_dialog, "response",
2952 G_CALLBACK (save_attachments_response), mime_parts);
2954 gtk_widget_show_all (save_dialog);
2958 show_remove_attachment_information (gpointer userdata)
2960 ModestMsgViewWindow *window = (ModestMsgViewWindow *) userdata;
2961 ModestMsgViewWindowPrivate *priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
2963 /* We're outside the main lock */
2964 gdk_threads_enter ();
2966 if (priv->remove_attachment_banner != NULL) {
2967 gtk_widget_destroy (priv->remove_attachment_banner);
2968 g_object_unref (priv->remove_attachment_banner);
2971 priv->remove_attachment_banner = g_object_ref (
2972 hildon_banner_show_animation (NULL, NULL, _("mcen_me_inbox_remove_attachments")));
2974 gdk_threads_leave ();
2980 modest_msg_view_window_remove_attachments (ModestMsgViewWindow *window, gboolean get_all)
2982 ModestMsgViewWindowPrivate *priv;
2983 TnyList *mime_parts = NULL, *tmp;
2984 gchar *confirmation_message;
2990 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window));
2991 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
2993 /* In hildon 2.2 we ignore the get_all flag as we always get all attachments. This is
2994 * because we don't have selection
2996 mime_parts = modest_msg_view_get_attachments (MODEST_MSG_VIEW (priv->msg_view));
2998 /* Remove already purged messages from mime parts list. We use
2999 a copy of the list to remove items in the original one */
3000 tmp = tny_list_copy (mime_parts);
3001 iter = tny_list_create_iterator (tmp);
3002 while (!tny_iterator_is_done (iter)) {
3003 TnyMimePart *part = TNY_MIME_PART (tny_iterator_get_current (iter));
3004 if (tny_mime_part_is_purged (part))
3005 tny_list_remove (mime_parts, (GObject *) part);
3007 g_object_unref (part);
3008 tny_iterator_next (iter);
3010 g_object_unref (tmp);
3011 g_object_unref (iter);
3013 if (!modest_maemo_utils_select_attachments (GTK_WINDOW (window), mime_parts, TRUE) ||
3014 tny_list_get_length (mime_parts) == 0) {
3015 g_object_unref (mime_parts);
3019 n_attachments = tny_list_get_length (mime_parts);
3020 if (n_attachments == 1) {
3024 iter = tny_list_create_iterator (mime_parts);
3025 part = (TnyMimePart *) tny_iterator_get_current (iter);
3026 g_object_unref (iter);
3027 if (modest_tny_mime_part_is_msg (part)) {
3029 header = tny_msg_get_header (TNY_MSG (part));
3030 filename = tny_header_dup_subject (header);
3031 g_object_unref (header);
3032 if (filename == NULL)
3033 filename = g_strdup (_("mail_va_no_subject"));
3035 filename = g_strdup (tny_mime_part_get_filename (TNY_MIME_PART (part)));
3037 confirmation_message = g_strdup_printf (_("mcen_nc_purge_file_text"), filename);
3039 g_object_unref (part);
3041 confirmation_message = g_strdup_printf (ngettext("mcen_nc_purge_file_text",
3042 "mcen_nc_purge_files_text",
3043 n_attachments), n_attachments);
3045 response = modest_platform_run_confirmation_dialog (GTK_WINDOW (window),
3046 confirmation_message);
3047 g_free (confirmation_message);
3049 if (response != GTK_RESPONSE_OK) {
3050 g_object_unref (mime_parts);
3054 priv->purge_timeout = g_timeout_add (2000, show_remove_attachment_information, window);
3056 iter = tny_list_create_iterator (mime_parts);
3057 while (!tny_iterator_is_done (iter)) {
3060 part = (TnyMimePart *) tny_iterator_get_current (iter);
3061 tny_mime_part_set_purged (TNY_MIME_PART (part));
3062 g_object_unref (part);
3063 tny_iterator_next (iter);
3065 g_object_unref (iter);
3067 msg = tny_msg_view_get_msg (TNY_MSG_VIEW (priv->msg_view));
3068 tny_msg_view_clear (TNY_MSG_VIEW (priv->msg_view));
3069 tny_msg_rewrite_cache (msg);
3070 tny_msg_view_set_msg (TNY_MSG_VIEW (priv->msg_view), msg);
3071 g_object_unref (msg);
3073 g_object_unref (mime_parts);
3075 if (priv->purge_timeout > 0) {
3076 g_source_remove (priv->purge_timeout);
3077 priv->purge_timeout = 0;
3080 if (priv->remove_attachment_banner) {
3081 gtk_widget_destroy (priv->remove_attachment_banner);
3082 g_object_unref (priv->remove_attachment_banner);
3083 priv->remove_attachment_banner = NULL;
3089 update_window_title (ModestMsgViewWindow *window)
3091 ModestMsgViewWindowPrivate *priv;
3093 TnyHeader *header = NULL;
3094 gchar *subject = NULL;
3096 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
3098 /* Note that if the window is closed while we're retrieving
3099 the message, this widget could de deleted */
3100 if (!priv->msg_view)
3103 msg = tny_msg_view_get_msg (TNY_MSG_VIEW (priv->msg_view));
3105 if (priv->other_body) {
3108 description = modest_tny_mime_part_get_header_value (priv->other_body, "Content-Description");
3110 g_strstrip (description);
3111 subject = description;
3113 } else if (msg != NULL) {
3114 header = tny_msg_get_header (msg);
3115 subject = tny_header_dup_subject (header);
3116 g_object_unref (header);
3117 g_object_unref (msg);
3120 if ((subject == NULL)||(subject[0] == '\0')) {
3122 subject = g_strdup (_("mail_va_no_subject"));
3125 gtk_window_set_title (GTK_WINDOW (window), subject);
3130 on_move_focus (GtkWidget *widget,
3131 GtkDirectionType direction,
3134 g_signal_stop_emission_by_name (G_OBJECT (widget), "move-focus");
3138 fetch_image_open_stream (TnyStreamCache *self, gint64 *expected_size, gchar *uri)
3140 GnomeVFSResult result;
3141 GnomeVFSHandle *handle = NULL;
3142 GnomeVFSFileInfo *info = NULL;
3145 result = gnome_vfs_open (&handle, uri, GNOME_VFS_OPEN_READ);
3146 if (result != GNOME_VFS_OK) {
3151 info = gnome_vfs_file_info_new ();
3152 result = gnome_vfs_get_file_info_from_handle (handle, info, GNOME_VFS_FILE_INFO_DEFAULT);
3153 if (result != GNOME_VFS_OK || ! (info->valid_fields & GNOME_VFS_FILE_INFO_FIELDS_SIZE)) {
3154 /* We put a "safe" default size for going to cache */
3155 *expected_size = (300*1024);
3157 *expected_size = info->size;
3159 gnome_vfs_file_info_unref (info);
3161 stream = tny_vfs_stream_new (handle);
3170 TnyStream *output_stream;
3171 GtkWidget *msg_view;
3176 on_fetch_image_idle_refresh_view (gpointer userdata)
3179 FetchImageData *fidata = (FetchImageData *) userdata;
3181 gdk_threads_enter ();
3182 if (GTK_WIDGET_DRAWABLE (fidata->msg_view)) {
3183 ModestMsgViewWindowPrivate *priv;
3185 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (fidata->window);
3186 priv->fetching_images--;
3187 gtk_widget_queue_draw (fidata->msg_view);
3188 update_progress_hint (MODEST_MSG_VIEW_WINDOW (fidata->window));
3190 gdk_threads_leave ();
3192 g_object_unref (fidata->msg_view);
3193 g_object_unref (fidata->window);
3194 g_slice_free (FetchImageData, fidata);
3199 on_fetch_image_thread (gpointer userdata)
3201 FetchImageData *fidata = (FetchImageData *) userdata;
3202 TnyStreamCache *cache;
3203 TnyStream *cache_stream;
3205 cache = modest_runtime_get_images_cache ();
3207 tny_stream_cache_get_stream (cache,
3209 (TnyStreamCacheOpenStreamFetcher) fetch_image_open_stream,
3210 (gpointer) fidata->uri);
3211 g_free (fidata->cache_id);
3212 g_free (fidata->uri);
3214 if (cache_stream != NULL) {
3217 while (G_LIKELY (!tny_stream_is_eos (cache_stream))) {
3220 nb_read = tny_stream_read (cache_stream, buffer, sizeof (buffer));
3221 if (G_UNLIKELY (nb_read < 0)) {
3223 } else if (G_LIKELY (nb_read > 0)) {
3224 gssize nb_written = 0;
3226 while (G_UNLIKELY (nb_written < nb_read)) {
3229 len = tny_stream_write (fidata->output_stream, buffer + nb_written,
3230 nb_read - nb_written);
3231 if (G_UNLIKELY (len < 0))
3237 tny_stream_close (cache_stream);
3238 g_object_unref (cache_stream);
3241 tny_stream_close (fidata->output_stream);
3242 g_object_unref (fidata->output_stream);
3244 g_idle_add (on_fetch_image_idle_refresh_view, fidata);
3250 on_fetch_image (ModestMsgView *msgview,
3253 ModestMsgViewWindow *window)
3255 const gchar *current_account;
3256 ModestMsgViewWindowPrivate *priv;
3257 FetchImageData *fidata;
3259 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
3261 current_account = modest_window_get_active_account (MODEST_WINDOW (window));
3263 fidata = g_slice_new0 (FetchImageData);
3264 fidata->msg_view = g_object_ref (msgview);
3265 fidata->window = g_object_ref (window);
3266 fidata->uri = g_strdup (uri);
3267 fidata->cache_id = modest_images_cache_get_id (current_account, uri);
3268 fidata->output_stream = g_object_ref (stream);
3270 priv->fetching_images++;
3271 if (g_thread_create (on_fetch_image_thread, fidata, FALSE, NULL) == NULL) {
3272 g_object_unref (fidata->output_stream);
3273 g_free (fidata->cache_id);
3274 g_free (fidata->uri);
3275 g_object_unref (fidata->msg_view);
3276 g_slice_free (FetchImageData, fidata);
3277 tny_stream_close (stream);
3278 priv->fetching_images--;
3279 update_progress_hint (window);
3282 update_progress_hint (window);
3288 setup_menu (ModestMsgViewWindow *self)
3290 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW(self));
3292 /* Settings menu buttons */
3293 modest_hildon2_window_add_to_menu (MODEST_HILDON2_WINDOW (self), _("mcen_me_inbox_replytoall"), NULL,
3294 APP_MENU_CALLBACK (modest_ui_actions_on_reply_all),
3295 MODEST_DIMMING_CALLBACK (modest_ui_dimming_rules_on_reply_msg));
3296 modest_hildon2_window_add_to_menu (MODEST_HILDON2_WINDOW (self), _("mcen_me_inbox_forward"), "<Control>d",
3297 APP_MENU_CALLBACK (modest_ui_actions_on_forward),
3298 MODEST_DIMMING_CALLBACK (modest_ui_dimming_rules_on_reply_msg));
3300 modest_hildon2_window_add_to_menu (MODEST_HILDON2_WINDOW (self), _("mcen_me_inbox_mark_as_read"), NULL,
3301 APP_MENU_CALLBACK (modest_ui_actions_on_mark_as_read),
3302 MODEST_DIMMING_CALLBACK (modest_ui_dimming_rules_on_mark_as_read_msg_in_view));
3303 modest_hildon2_window_add_to_menu (MODEST_HILDON2_WINDOW (self), _("mcen_me_inbox_mark_as_unread"), NULL,
3304 APP_MENU_CALLBACK (modest_ui_actions_on_mark_as_unread),
3305 MODEST_DIMMING_CALLBACK (modest_ui_dimming_rules_on_mark_as_unread_msg_in_view));
3307 modest_hildon2_window_add_to_menu (MODEST_HILDON2_WINDOW (self), _("mcen_me_viewer_save_attachments"), NULL,
3308 APP_MENU_CALLBACK (modest_ui_actions_save_attachments),
3309 MODEST_DIMMING_CALLBACK (modest_ui_dimming_rules_on_save_attachments));
3310 modest_hildon2_window_add_to_menu (MODEST_HILDON2_WINDOW (self), _("mcen_me_inbox_remove_attachments"), NULL,
3311 APP_MENU_CALLBACK (modest_ui_actions_remove_attachments),
3312 MODEST_DIMMING_CALLBACK (modest_ui_dimming_rules_on_remove_attachments));
3314 modest_hildon2_window_add_to_menu (MODEST_HILDON2_WINDOW (self), _("mcen_me_new_message"), "<Control>n",
3315 APP_MENU_CALLBACK (modest_ui_actions_on_new_msg),
3316 MODEST_DIMMING_CALLBACK (modest_ui_dimming_rules_on_new_msg));
3317 modest_hildon2_window_add_to_menu (MODEST_HILDON2_WINDOW (self), _("mcen_me_viewer_addtocontacts"), NULL,
3318 APP_MENU_CALLBACK (modest_ui_actions_add_to_contacts),
3319 MODEST_DIMMING_CALLBACK (modest_ui_dimming_rules_on_add_to_contacts));
3321 modest_hildon2_window_add_to_menu (MODEST_HILDON2_WINDOW (self), _("mail_bd_external_images"), NULL,
3322 APP_MENU_CALLBACK (modest_ui_actions_on_fetch_images),
3323 MODEST_DIMMING_CALLBACK (modest_ui_dimming_rules_on_fetch_images));
3324 modest_hildon2_window_add_to_menu (MODEST_HILDON2_WINDOW (self), _("mcen_ti_message_properties"), NULL,
3325 APP_MENU_CALLBACK (modest_ui_actions_on_details),
3326 MODEST_DIMMING_CALLBACK (modest_ui_dimming_rules_on_details));
3330 modest_msg_view_window_add_to_contacts (ModestMsgViewWindow *self)
3332 ModestMsgViewWindowPrivate *priv;
3333 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
3334 GSList *recipients = NULL;
3336 gboolean contacts_to_add = FALSE;
3338 msg = tny_msg_view_get_msg (TNY_MSG_VIEW (priv->msg_view));
3342 header = modest_msg_view_window_get_header (self);
3345 recipients = modest_tny_msg_header_get_all_recipients_list (header);
3346 g_object_unref (header);
3348 recipients = modest_tny_msg_get_all_recipients_list (msg);
3349 g_object_unref (msg);
3352 if (recipients != NULL) {
3353 GtkWidget *picker_dialog;
3354 GtkWidget *selector;
3356 gchar *selected = NULL;
3358 selector = hildon_touch_selector_new_text ();
3359 g_object_ref (selector);
3361 for (node = recipients; node != NULL; node = g_slist_next (node)) {
3362 if (!modest_address_book_has_address ((const gchar *) node->data)) {
3363 hildon_touch_selector_append_text (HILDON_TOUCH_SELECTOR (selector),
3364 (const gchar *) node->data);
3365 contacts_to_add = TRUE;
3369 if (contacts_to_add) {
3372 picker_dialog = hildon_picker_dialog_new (GTK_WINDOW (self));
3373 gtk_window_set_title (GTK_WINDOW (picker_dialog), _("mcen_me_viewer_addtocontacts"));
3375 hildon_picker_dialog_set_selector (HILDON_PICKER_DIALOG (picker_dialog),
3376 HILDON_TOUCH_SELECTOR (selector));
3378 picker_result = gtk_dialog_run (GTK_DIALOG (picker_dialog));
3380 if (picker_result == GTK_RESPONSE_OK) {
3381 selected = hildon_touch_selector_get_current_text (HILDON_TOUCH_SELECTOR (selector));
3383 gtk_widget_destroy (picker_dialog);
3386 modest_address_book_add_address (selected);
3391 g_object_unref (selector);
3396 if (recipients) {g_slist_foreach (recipients, (GFunc) g_free, NULL); g_slist_free (recipients);}
3400 _modest_msg_view_window_map_event (GtkWidget *widget,
3404 ModestMsgViewWindow *self = (ModestMsgViewWindow *) userdata;
3406 update_progress_hint (self);
3412 modest_msg_view_window_fetch_images (ModestMsgViewWindow *self)
3414 ModestMsgViewWindowPrivate *priv;
3415 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
3417 modest_msg_view_request_fetch_images (MODEST_MSG_VIEW (priv->msg_view));
3421 modest_msg_view_window_has_blocked_external_images (ModestMsgViewWindow *self)
3423 ModestMsgViewWindowPrivate *priv;
3424 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
3426 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self), FALSE);
3428 return modest_msg_view_has_blocked_external_images (MODEST_MSG_VIEW (priv->msg_view));
3432 modest_msg_view_window_reload (ModestMsgViewWindow *self)
3434 ModestMsgViewWindowPrivate *priv;
3437 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self));
3439 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
3440 header = modest_msg_view_window_get_header (MODEST_MSG_VIEW_WINDOW (self));
3442 if (!message_reader (self, priv, header, priv->row_reference)) {
3443 g_warning ("Shouldn't happen, trying to reload a message failed");
3446 g_object_unref (header);