1 /* Copyright (c) 2006, Nokia Corporation
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions are
8 * * Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 * * Redistributions in binary form must reproduce the above copyright
11 * notice, this list of conditions and the following disclaimer in the
12 * documentation and/or other materials provided with the distribution.
13 * * Neither the name of the Nokia Corporation nor the names of its
14 * contributors may be used to endorse or promote products derived from
15 * this software without specific prior written permission.
17 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
18 * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
19 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
20 * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
21 * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
22 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
23 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
24 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
25 * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
26 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
27 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29 #include <glib/gi18n.h>
31 #include <tny-account-store.h>
32 #include <tny-simple-list.h>
34 #include <tny-mime-part.h>
35 #include <tny-vfs-stream.h>
36 #include <tny-error.h>
37 #include "modest-marshal.h"
38 #include "modest-platform.h"
39 #include <modest-utils.h>
40 #include <modest-maemo-utils.h>
41 #include <modest-tny-msg.h>
42 #include <modest-msg-view-window.h>
43 #include <modest-main-window-ui.h>
44 #include "modest-msg-view-window-ui-dimming.h"
45 #include <modest-widget-memory.h>
46 #include <modest-progress-object.h>
47 #include <modest-runtime.h>
48 #include <modest-window-priv.h>
49 #include <modest-tny-folder.h>
50 #include <modest-text-utils.h>
51 #include <modest-account-mgr-helpers.h>
52 #include <hildon/hildon-pannable-area.h>
53 #include <hildon/hildon-picker-dialog.h>
54 #include <hildon/hildon-app-menu.h>
55 #include "modest-defs.h"
56 #include "modest-hildon-includes.h"
57 #include "modest-ui-dimming-manager.h"
58 #include <gdk/gdkkeysyms.h>
59 #include <modest-tny-account.h>
60 #include <modest-mime-part-view.h>
61 #include <modest-isearch-view.h>
62 #include <modest-tny-mime-part.h>
63 #include <modest-address-book.h>
66 #include <glib/gstdio.h>
67 #include <modest-debug.h>
68 #include <modest-header-window.h>
69 #include <modest-account-protocol.h>
71 #define MYDOCS_ENV "MYDOCSDIR"
72 #define DOCS_FOLDER ".documents"
74 typedef struct _ModestMsgViewWindowPrivate ModestMsgViewWindowPrivate;
75 struct _ModestMsgViewWindowPrivate {
78 GtkWidget *main_scroll;
79 GtkWidget *find_toolbar;
82 /* Progress observers */
83 GSList *progress_widgets;
86 GtkWidget *prev_toolitem;
87 GtkWidget *next_toolitem;
88 gboolean progress_hint;
91 /* Optimized view enabled */
92 gboolean optimized_view;
94 /* Whether this was created via the *_new_for_search_result() function. */
95 gboolean is_search_result;
97 /* Whether the message is in outbox */
100 /* A reference to the @model of the header view
101 * to allow selecting previous/next messages,
102 * if the message is currently selected in the header view.
104 const gchar *header_folder_id;
105 GtkTreeModel *header_model;
106 GtkTreeRowReference *row_reference;
107 GtkTreeRowReference *next_row_reference;
109 gulong clipboard_change_handler;
110 gulong queue_change_handler;
111 gulong account_removed_handler;
112 gulong row_changed_handler;
113 gulong row_deleted_handler;
114 gulong row_inserted_handler;
115 gulong rows_reordered_handler;
118 GtkWidget *remove_attachment_banner;
121 TnyMimePart *other_body;
126 static void modest_msg_view_window_class_init (ModestMsgViewWindowClass *klass);
127 static void modest_msg_view_window_init (ModestMsgViewWindow *obj);
128 static void modest_header_view_observer_init(
129 ModestHeaderViewObserverIface *iface_class);
130 static void modest_msg_view_window_finalize (GObject *obj);
131 static void modest_msg_view_window_toggle_find_toolbar (GtkToggleAction *obj,
133 static void modest_msg_view_window_find_toolbar_close (GtkWidget *widget,
134 ModestMsgViewWindow *obj);
135 static void modest_msg_view_window_find_toolbar_search (GtkWidget *widget,
136 ModestMsgViewWindow *obj);
138 static void modest_msg_view_window_disconnect_signals (ModestWindow *self);
140 static gdouble modest_msg_view_window_get_zoom (ModestWindow *window);
141 static void modest_msg_view_window_set_zoom (ModestWindow *window,
143 static gboolean modest_msg_view_window_zoom_minus (ModestWindow *window);
144 static gboolean modest_msg_view_window_zoom_plus (ModestWindow *window);
145 static gboolean modest_msg_view_window_key_event (GtkWidget *window,
148 static void modest_msg_view_window_update_priority (ModestMsgViewWindow *window);
150 static void modest_msg_view_window_show_toolbar (ModestWindow *window,
151 gboolean show_toolbar);
153 static void modest_msg_view_window_clipboard_owner_change (GtkClipboard *clipboard,
155 ModestMsgViewWindow *window);
157 static void modest_msg_view_window_on_row_changed (GtkTreeModel *header_model,
160 ModestMsgViewWindow *window);
162 static void modest_msg_view_window_on_row_deleted (GtkTreeModel *header_model,
164 ModestMsgViewWindow *window);
166 static void modest_msg_view_window_on_row_inserted (GtkTreeModel *header_model,
167 GtkTreePath *tree_path,
168 GtkTreeIter *tree_iter,
169 ModestMsgViewWindow *window);
171 static void modest_msg_view_window_on_row_reordered (GtkTreeModel *header_model,
175 ModestMsgViewWindow *window);
177 static void modest_msg_view_window_update_model_replaced (ModestHeaderViewObserver *window,
179 const gchar *tny_folder_id);
181 static void on_queue_changed (ModestMailOperationQueue *queue,
182 ModestMailOperation *mail_op,
183 ModestMailOperationQueueNotification type,
184 ModestMsgViewWindow *self);
186 static void on_account_removed (TnyAccountStore *account_store,
190 static void on_move_focus (GtkWidget *widget,
191 GtkDirectionType direction,
194 static void view_msg_cb (ModestMailOperation *mail_op,
201 static void set_progress_hint (ModestMsgViewWindow *self,
204 static void update_window_title (ModestMsgViewWindow *window);
206 static gboolean set_toolbar_transfer_mode (ModestMsgViewWindow *self);
207 static void init_window (ModestMsgViewWindow *obj);
209 static gboolean msg_is_visible (TnyHeader *header, gboolean check_outbox);
211 static void check_dimming_rules_after_change (ModestMsgViewWindow *window);
213 static gboolean on_fetch_image (ModestMsgView *msgview,
216 ModestMsgViewWindow *window);
218 static gboolean modest_msg_view_window_scroll_child (ModestMsgViewWindow *self,
219 GtkScrollType scroll_type,
222 static gboolean message_reader (ModestMsgViewWindow *window,
223 ModestMsgViewWindowPrivate *priv,
225 GtkTreeRowReference *row_reference);
227 static void setup_menu (ModestMsgViewWindow *self);
228 static gboolean _modest_msg_view_window_map_event (GtkWidget *widget,
231 static void update_branding (ModestMsgViewWindow *self);
234 /* list my signals */
241 static const GtkToggleActionEntry msg_view_toggle_action_entries [] = {
242 { "FindInMessage", MODEST_TOOLBAR_ICON_FIND, N_("qgn_toolb_gene_find"), "<CTRL>F", NULL, G_CALLBACK (modest_msg_view_window_toggle_find_toolbar), FALSE },
245 #define MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE((o), \
246 MODEST_TYPE_MSG_VIEW_WINDOW, \
247 ModestMsgViewWindowPrivate))
249 static GtkWindowClass *parent_class = NULL;
251 /* uncomment the following if you have defined any signals */
252 static guint signals[LAST_SIGNAL] = {0};
255 modest_msg_view_window_get_type (void)
257 static GType my_type = 0;
259 static const GTypeInfo my_info = {
260 sizeof(ModestMsgViewWindowClass),
261 NULL, /* base init */
262 NULL, /* base finalize */
263 (GClassInitFunc) modest_msg_view_window_class_init,
264 NULL, /* class finalize */
265 NULL, /* class data */
266 sizeof(ModestMsgViewWindow),
268 (GInstanceInitFunc) modest_msg_view_window_init,
271 my_type = g_type_register_static (MODEST_TYPE_HILDON2_WINDOW,
272 "ModestMsgViewWindow",
275 static const GInterfaceInfo modest_header_view_observer_info =
277 (GInterfaceInitFunc) modest_header_view_observer_init,
278 NULL, /* interface_finalize */
279 NULL /* interface_data */
282 g_type_add_interface_static (my_type,
283 MODEST_TYPE_HEADER_VIEW_OBSERVER,
284 &modest_header_view_observer_info);
290 save_state (ModestWindow *self)
292 modest_widget_memory_save (modest_runtime_get_conf (),
294 MODEST_CONF_MSG_VIEW_WINDOW_KEY);
298 gboolean modest_msg_view_window_scroll_child (ModestMsgViewWindow *self,
299 GtkScrollType scroll_type,
303 ModestMsgViewWindowPrivate *priv;
304 gboolean return_value;
306 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(self);
307 g_signal_emit_by_name (priv->main_scroll, "scroll-child", scroll_type, horizontal, &return_value);
312 add_scroll_binding (GtkBindingSet *binding_set,
314 GtkScrollType scroll)
316 guint keypad_keyval = keyval - GDK_Left + GDK_KP_Left;
318 gtk_binding_entry_add_signal (binding_set, keyval, 0,
320 GTK_TYPE_SCROLL_TYPE, scroll,
321 G_TYPE_BOOLEAN, FALSE);
322 gtk_binding_entry_add_signal (binding_set, keypad_keyval, 0,
324 GTK_TYPE_SCROLL_TYPE, scroll,
325 G_TYPE_BOOLEAN, FALSE);
329 modest_msg_view_window_class_init (ModestMsgViewWindowClass *klass)
331 GObjectClass *gobject_class;
332 HildonWindowClass *hildon_window_class;
333 ModestWindowClass *modest_window_class;
334 GtkBindingSet *binding_set;
336 gobject_class = (GObjectClass*) klass;
337 hildon_window_class = (HildonWindowClass *) klass;
338 modest_window_class = (ModestWindowClass *) klass;
340 parent_class = g_type_class_peek_parent (klass);
341 gobject_class->finalize = modest_msg_view_window_finalize;
343 modest_window_class->set_zoom_func = modest_msg_view_window_set_zoom;
344 modest_window_class->get_zoom_func = modest_msg_view_window_get_zoom;
345 modest_window_class->zoom_plus_func = modest_msg_view_window_zoom_plus;
346 modest_window_class->zoom_minus_func = modest_msg_view_window_zoom_minus;
347 modest_window_class->show_toolbar_func = modest_msg_view_window_show_toolbar;
348 modest_window_class->disconnect_signals_func = modest_msg_view_window_disconnect_signals;
350 modest_window_class->save_state_func = save_state;
352 klass->scroll_child = modest_msg_view_window_scroll_child;
354 signals[MSG_CHANGED_SIGNAL] =
355 g_signal_new ("msg-changed",
356 G_TYPE_FROM_CLASS (gobject_class),
358 G_STRUCT_OFFSET (ModestMsgViewWindowClass, msg_changed),
360 modest_marshal_VOID__POINTER_POINTER,
361 G_TYPE_NONE, 2, G_TYPE_POINTER, G_TYPE_POINTER);
363 signals[SCROLL_CHILD_SIGNAL] =
364 g_signal_new ("scroll-child",
365 G_TYPE_FROM_CLASS (gobject_class),
366 G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
367 G_STRUCT_OFFSET (ModestMsgViewWindowClass, scroll_child),
369 modest_marshal_BOOLEAN__ENUM_BOOLEAN,
370 G_TYPE_BOOLEAN, 2, GTK_TYPE_SCROLL_TYPE, G_TYPE_BOOLEAN);
372 binding_set = gtk_binding_set_by_class (klass);
373 add_scroll_binding (binding_set, GDK_Up, GTK_SCROLL_STEP_UP);
374 add_scroll_binding (binding_set, GDK_Down, GTK_SCROLL_STEP_DOWN);
375 add_scroll_binding (binding_set, GDK_Page_Up, GTK_SCROLL_PAGE_UP);
376 add_scroll_binding (binding_set, GDK_Page_Down, GTK_SCROLL_PAGE_DOWN);
377 add_scroll_binding (binding_set, GDK_Home, GTK_SCROLL_START);
378 add_scroll_binding (binding_set, GDK_End, GTK_SCROLL_END);
380 g_type_class_add_private (gobject_class, sizeof(ModestMsgViewWindowPrivate));
384 static void modest_header_view_observer_init(
385 ModestHeaderViewObserverIface *iface_class)
387 iface_class->update_func = modest_msg_view_window_update_model_replaced;
391 modest_msg_view_window_init (ModestMsgViewWindow *obj)
393 ModestMsgViewWindowPrivate *priv;
394 ModestWindowPrivate *parent_priv = NULL;
395 GtkActionGroup *action_group = NULL;
396 GError *error = NULL;
397 GdkPixbuf *window_icon;
399 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(obj);
400 parent_priv = MODEST_WINDOW_GET_PRIVATE(obj);
401 parent_priv->ui_manager = gtk_ui_manager_new();
403 action_group = gtk_action_group_new ("ModestMsgViewWindowActions");
404 gtk_action_group_set_translation_domain (action_group, GETTEXT_PACKAGE);
406 /* Add common actions */
407 gtk_action_group_add_actions (action_group,
408 modest_action_entries,
409 G_N_ELEMENTS (modest_action_entries),
411 gtk_action_group_add_toggle_actions (action_group,
412 msg_view_toggle_action_entries,
413 G_N_ELEMENTS (msg_view_toggle_action_entries),
416 gtk_ui_manager_insert_action_group (parent_priv->ui_manager, action_group, 0);
417 g_object_unref (action_group);
419 /* Load the UI definition */
420 gtk_ui_manager_add_ui_from_file (parent_priv->ui_manager, MODEST_UIDIR "modest-msg-view-window-ui.xml",
423 g_printerr ("modest: could not merge modest-msg-view-window-ui.xml: %s\n", error->message);
424 g_error_free (error);
429 /* Add accelerators */
430 gtk_window_add_accel_group (GTK_WINDOW (obj),
431 gtk_ui_manager_get_accel_group (parent_priv->ui_manager));
433 priv->is_search_result = FALSE;
434 priv->is_outbox = FALSE;
436 priv->msg_view = NULL;
437 priv->header_model = NULL;
438 priv->header_folder_id = NULL;
439 priv->clipboard_change_handler = 0;
440 priv->queue_change_handler = 0;
441 priv->account_removed_handler = 0;
442 priv->row_changed_handler = 0;
443 priv->row_deleted_handler = 0;
444 priv->row_inserted_handler = 0;
445 priv->rows_reordered_handler = 0;
446 priv->progress_hint = FALSE;
447 priv->fetching_images = 0;
449 priv->optimized_view = FALSE;
450 priv->purge_timeout = 0;
451 priv->remove_attachment_banner = NULL;
452 priv->msg_uid = NULL;
453 priv->other_body = NULL;
455 priv->sighandlers = NULL;
458 init_window (MODEST_MSG_VIEW_WINDOW(obj));
460 /* Set window icon */
461 window_icon = modest_platform_get_icon (MODEST_APP_MSG_VIEW_ICON, MODEST_ICON_SIZE_BIG);
463 gtk_window_set_icon (GTK_WINDOW (obj), window_icon);
464 g_object_unref (window_icon);
467 hildon_program_add_window (hildon_program_get_instance(),
474 set_toolbar_transfer_mode (ModestMsgViewWindow *self)
476 ModestMsgViewWindowPrivate *priv = NULL;
478 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self), FALSE);
480 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(self);
482 set_progress_hint (self, TRUE);
488 update_progress_hint (ModestMsgViewWindow *self)
490 ModestMsgViewWindowPrivate *priv;
491 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(self);
493 if (GTK_WIDGET_VISIBLE (self)) {
494 hildon_gtk_window_set_progress_indicator (GTK_WINDOW (self),
495 (priv->progress_hint || (priv->fetching_images > 0))?1:0);
500 set_progress_hint (ModestMsgViewWindow *self,
503 ModestWindowPrivate *parent_priv;
504 ModestMsgViewWindowPrivate *priv;
506 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self));
508 parent_priv = MODEST_WINDOW_GET_PRIVATE(self);
509 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(self);
511 /* Sets current progress hint */
512 priv->progress_hint = enabled;
514 update_progress_hint (self);
520 init_window (ModestMsgViewWindow *obj)
522 GtkWidget *main_vbox;
523 ModestMsgViewWindowPrivate *priv;
525 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(obj);
527 priv->msg_view = GTK_WIDGET (tny_platform_factory_new_msg_view (modest_tny_platform_factory_get_instance ()));
528 modest_msg_view_set_shadow_type (MODEST_MSG_VIEW (priv->msg_view), GTK_SHADOW_NONE);
529 main_vbox = gtk_vbox_new (FALSE, 6);
530 priv->main_scroll = hildon_pannable_area_new ();
531 gtk_container_add (GTK_CONTAINER (priv->main_scroll), priv->msg_view);
532 gtk_box_pack_start (GTK_BOX(main_vbox), priv->main_scroll, TRUE, TRUE, 0);
533 gtk_container_add (GTK_CONTAINER(obj), main_vbox);
535 /* NULL-ize fields if the window is destroyed */
536 g_signal_connect (priv->msg_view, "destroy", G_CALLBACK (gtk_widget_destroyed), &(priv->msg_view));
538 gtk_widget_show_all (GTK_WIDGET(main_vbox));
542 modest_msg_view_window_disconnect_signals (ModestWindow *self)
544 ModestMsgViewWindowPrivate *priv;
545 GtkWidget *header_view = NULL;
546 GtkWindow *parent_window = NULL;
548 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
550 if (gtk_clipboard_get (GDK_SELECTION_PRIMARY) &&
551 g_signal_handler_is_connected (gtk_clipboard_get (GDK_SELECTION_PRIMARY),
552 priv->clipboard_change_handler))
553 g_signal_handler_disconnect (gtk_clipboard_get (GDK_SELECTION_PRIMARY),
554 priv->clipboard_change_handler);
556 if (g_signal_handler_is_connected (G_OBJECT (modest_runtime_get_mail_operation_queue ()),
557 priv->queue_change_handler))
558 g_signal_handler_disconnect (G_OBJECT (modest_runtime_get_mail_operation_queue ()),
559 priv->queue_change_handler);
561 if (g_signal_handler_is_connected (G_OBJECT (modest_runtime_get_account_store ()),
562 priv->account_removed_handler))
563 g_signal_handler_disconnect (G_OBJECT (modest_runtime_get_account_store ()),
564 priv->account_removed_handler);
566 if (priv->header_model) {
567 if (g_signal_handler_is_connected(G_OBJECT (priv->header_model),
568 priv->row_changed_handler))
569 g_signal_handler_disconnect(G_OBJECT (priv->header_model),
570 priv->row_changed_handler);
572 if (g_signal_handler_is_connected(G_OBJECT (priv->header_model),
573 priv->row_deleted_handler))
574 g_signal_handler_disconnect(G_OBJECT (priv->header_model),
575 priv->row_deleted_handler);
577 if (g_signal_handler_is_connected(G_OBJECT (priv->header_model),
578 priv->row_inserted_handler))
579 g_signal_handler_disconnect(G_OBJECT (priv->header_model),
580 priv->row_inserted_handler);
582 if (g_signal_handler_is_connected(G_OBJECT (priv->header_model),
583 priv->rows_reordered_handler))
584 g_signal_handler_disconnect(G_OBJECT (priv->header_model),
585 priv->rows_reordered_handler);
588 modest_signal_mgr_disconnect_all_and_destroy (priv->sighandlers);
589 priv->sighandlers = NULL;
591 parent_window = gtk_window_get_transient_for (GTK_WINDOW (self));
592 if (parent_window && MODEST_IS_HEADER_WINDOW (parent_window)) {
593 header_view = GTK_WIDGET (modest_header_window_get_header_view (MODEST_HEADER_WINDOW (parent_window)));
595 modest_header_view_remove_observer(MODEST_HEADER_VIEW (header_view),
596 MODEST_HEADER_VIEW_OBSERVER(self));
602 modest_msg_view_window_finalize (GObject *obj)
604 ModestMsgViewWindowPrivate *priv;
606 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (obj);
608 /* Sanity check: shouldn't be needed, the window mgr should
609 call this function before */
610 modest_msg_view_window_disconnect_signals (MODEST_WINDOW (obj));
612 if (priv->other_body != NULL) {
613 g_object_unref (priv->other_body);
614 priv->other_body = NULL;
617 if (priv->header_model != NULL) {
618 g_object_unref (priv->header_model);
619 priv->header_model = NULL;
622 if (priv->remove_attachment_banner) {
623 gtk_widget_destroy (priv->remove_attachment_banner);
624 g_object_unref (priv->remove_attachment_banner);
625 priv->remove_attachment_banner = NULL;
628 if (priv->purge_timeout > 0) {
629 g_source_remove (priv->purge_timeout);
630 priv->purge_timeout = 0;
633 if (priv->row_reference) {
634 gtk_tree_row_reference_free (priv->row_reference);
635 priv->row_reference = NULL;
638 if (priv->next_row_reference) {
639 gtk_tree_row_reference_free (priv->next_row_reference);
640 priv->next_row_reference = NULL;
644 g_free (priv->msg_uid);
645 priv->msg_uid = NULL;
648 G_OBJECT_CLASS(parent_class)->finalize (obj);
652 select_next_valid_row (GtkTreeModel *model,
653 GtkTreeRowReference **row_reference,
657 GtkTreeIter tmp_iter;
659 GtkTreePath *next = NULL;
660 gboolean retval = FALSE, finished;
662 g_return_val_if_fail (gtk_tree_row_reference_valid (*row_reference), FALSE);
664 path = gtk_tree_row_reference_get_path (*row_reference);
665 gtk_tree_model_get_iter (model, &tmp_iter, path);
666 gtk_tree_row_reference_free (*row_reference);
667 *row_reference = NULL;
671 TnyHeader *header = NULL;
673 if (gtk_tree_model_iter_next (model, &tmp_iter)) {
674 gtk_tree_model_get (model, &tmp_iter,
675 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
679 if (msg_is_visible (header, is_outbox)) {
680 next = gtk_tree_model_get_path (model, &tmp_iter);
681 *row_reference = gtk_tree_row_reference_new (model, next);
682 gtk_tree_path_free (next);
686 g_object_unref (header);
689 } else if (cycle && gtk_tree_model_get_iter_first (model, &tmp_iter)) {
690 next = gtk_tree_model_get_path (model, &tmp_iter);
692 /* Ensure that we are not selecting the same */
693 if (gtk_tree_path_compare (path, next) != 0) {
694 gtk_tree_model_get (model, &tmp_iter,
695 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
698 if (msg_is_visible (header, is_outbox)) {
699 *row_reference = gtk_tree_row_reference_new (model, next);
703 g_object_unref (header);
707 /* If we ended up in the same message
708 then there is no valid next
712 gtk_tree_path_free (next);
714 /* If there are no more messages and we don't
715 want to start again in the first one then
716 there is no valid next message */
722 gtk_tree_path_free (path);
727 /* TODO: This should be in _init(), with the parameters as properties. */
729 modest_msg_view_window_construct (ModestMsgViewWindow *self,
730 const gchar *modest_account_name,
731 const gchar *mailbox,
732 const gchar *msg_uid)
735 ModestMsgViewWindowPrivate *priv = NULL;
736 ModestWindowPrivate *parent_priv = NULL;
737 ModestDimmingRulesGroup *toolbar_rules_group = NULL;
738 ModestDimmingRulesGroup *clipboard_rules_group = NULL;
740 obj = G_OBJECT (self);
741 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(obj);
742 parent_priv = MODEST_WINDOW_GET_PRIVATE(obj);
744 priv->msg_uid = g_strdup (msg_uid);
747 parent_priv->menubar = NULL;
749 toolbar_rules_group = modest_dimming_rules_group_new (MODEST_DIMMING_RULES_TOOLBAR, TRUE);
750 clipboard_rules_group = modest_dimming_rules_group_new (MODEST_DIMMING_RULES_CLIPBOARD, FALSE);
753 /* Add common dimming rules */
754 modest_dimming_rules_group_add_rules (toolbar_rules_group,
755 modest_msg_view_toolbar_dimming_entries,
756 G_N_ELEMENTS (modest_msg_view_toolbar_dimming_entries),
757 MODEST_WINDOW (self));
758 modest_dimming_rules_group_add_rules (clipboard_rules_group,
759 modest_msg_view_clipboard_dimming_entries,
760 G_N_ELEMENTS (modest_msg_view_clipboard_dimming_entries),
761 MODEST_WINDOW (self));
763 /* Insert dimming rules group for this window */
764 modest_ui_dimming_manager_insert_rules_group (parent_priv->ui_dimming_manager, toolbar_rules_group);
765 modest_ui_dimming_manager_insert_rules_group (parent_priv->ui_dimming_manager, clipboard_rules_group);
766 g_object_unref (toolbar_rules_group);
767 g_object_unref (clipboard_rules_group);
769 /* g_signal_connect (G_OBJECT(obj), "delete-event", G_CALLBACK(on_delete_event), obj); */
771 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);
772 g_signal_connect (G_OBJECT(priv->msg_view), "activate_link",
773 G_CALLBACK (modest_ui_actions_on_msg_link_clicked), obj);
774 g_signal_connect (G_OBJECT(priv->msg_view), "link_hover",
775 G_CALLBACK (modest_ui_actions_on_msg_link_hover), obj);
776 g_signal_connect (G_OBJECT(priv->msg_view), "attachment_clicked",
777 G_CALLBACK (modest_ui_actions_on_msg_attachment_clicked), obj);
778 g_signal_connect (G_OBJECT(priv->msg_view), "recpt_activated",
779 G_CALLBACK (modest_ui_actions_on_msg_recpt_activated), obj);
780 g_signal_connect (G_OBJECT(priv->msg_view), "show_details",
781 G_CALLBACK (modest_ui_actions_on_details), obj);
782 g_signal_connect (G_OBJECT(priv->msg_view), "link_contextual",
783 G_CALLBACK (modest_ui_actions_on_msg_link_contextual), obj);
784 g_signal_connect (G_OBJECT (priv->msg_view), "fetch_image",
785 G_CALLBACK (on_fetch_image), obj);
787 g_signal_connect (G_OBJECT (obj), "key-release-event",
788 G_CALLBACK (modest_msg_view_window_key_event),
791 g_signal_connect (G_OBJECT (obj), "key-press-event",
792 G_CALLBACK (modest_msg_view_window_key_event),
795 g_signal_connect (G_OBJECT (obj), "move-focus",
796 G_CALLBACK (on_move_focus), obj);
798 g_signal_connect (G_OBJECT (obj), "map-event",
799 G_CALLBACK (_modest_msg_view_window_map_event),
802 /* Mail Operation Queue */
803 priv->queue_change_handler = g_signal_connect (G_OBJECT (modest_runtime_get_mail_operation_queue ()),
805 G_CALLBACK (on_queue_changed),
808 /* Account manager */
809 priv->account_removed_handler = g_signal_connect (G_OBJECT (modest_runtime_get_account_store ()),
811 G_CALLBACK(on_account_removed),
814 modest_window_set_active_account (MODEST_WINDOW(obj), modest_account_name);
815 modest_window_set_active_mailbox (MODEST_WINDOW(obj), mailbox);
817 /* First add out toolbar ... */
818 modest_msg_view_window_show_toolbar (MODEST_WINDOW (obj), TRUE);
820 /* ... and later the find toolbar. This way find toolbar will
821 be shown over the other */
822 priv->find_toolbar = hildon_find_toolbar_new (NULL);
823 hildon_window_add_toolbar (HILDON_WINDOW (obj), GTK_TOOLBAR (priv->find_toolbar));
824 gtk_widget_set_no_show_all (priv->find_toolbar, TRUE);
825 g_signal_connect (G_OBJECT (priv->find_toolbar), "close",
826 G_CALLBACK (modest_msg_view_window_find_toolbar_close), obj);
827 g_signal_connect (G_OBJECT (priv->find_toolbar), "search",
828 G_CALLBACK (modest_msg_view_window_find_toolbar_search), obj);
829 priv->last_search = NULL;
831 /* Init the clipboard actions dim status */
832 modest_msg_view_grab_focus(MODEST_MSG_VIEW (priv->msg_view));
834 update_window_title (MODEST_MSG_VIEW_WINDOW (obj));
839 /* FIXME: parameter checks */
841 modest_msg_view_window_new_with_header_model (TnyMsg *msg,
842 const gchar *modest_account_name,
843 const gchar *mailbox,
844 const gchar *msg_uid,
846 GtkTreeRowReference *row_reference)
848 ModestMsgViewWindow *window = NULL;
849 ModestMsgViewWindowPrivate *priv = NULL;
850 TnyFolder *header_folder = NULL;
851 ModestHeaderView *header_view = NULL;
852 ModestWindowMgr *mgr = NULL;
855 modest_tny_mime_part_to_string (TNY_MIME_PART (msg), 0);
858 mgr = modest_runtime_get_window_mgr ();
859 window = MODEST_MSG_VIEW_WINDOW (modest_window_mgr_get_msg_view_window (mgr));
860 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), NULL);
862 modest_msg_view_window_construct (window, modest_account_name, mailbox, msg_uid);
864 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
866 /* Remember the message list's TreeModel so we can detect changes
867 * and change the list selection when necessary: */
868 header_folder = modest_header_view_get_folder (header_view);
870 priv->is_outbox = (modest_tny_folder_guess_folder_type (header_folder) ==
871 TNY_FOLDER_TYPE_OUTBOX);
872 priv->header_folder_id = tny_folder_get_id (header_folder);
873 g_object_unref(header_folder);
876 /* Setup row references and connect signals */
877 priv->header_model = g_object_ref (model);
880 priv->row_reference = gtk_tree_row_reference_copy (row_reference);
881 priv->next_row_reference = gtk_tree_row_reference_copy (row_reference);
882 select_next_valid_row (model, &(priv->next_row_reference), TRUE, priv->is_outbox);
884 priv->row_reference = NULL;
885 priv->next_row_reference = NULL;
888 /* Connect signals */
889 priv->row_changed_handler =
890 g_signal_connect (GTK_TREE_MODEL(model), "row-changed",
891 G_CALLBACK(modest_msg_view_window_on_row_changed),
893 priv->row_deleted_handler =
894 g_signal_connect (GTK_TREE_MODEL(model), "row-deleted",
895 G_CALLBACK(modest_msg_view_window_on_row_deleted),
897 priv->row_inserted_handler =
898 g_signal_connect (GTK_TREE_MODEL(model), "row-inserted",
899 G_CALLBACK(modest_msg_view_window_on_row_inserted),
901 priv->rows_reordered_handler =
902 g_signal_connect(GTK_TREE_MODEL(model), "rows-reordered",
903 G_CALLBACK(modest_msg_view_window_on_row_reordered),
906 if (header_view != NULL){
907 modest_header_view_add_observer(header_view,
908 MODEST_HEADER_VIEW_OBSERVER(window));
911 tny_msg_view_set_msg (TNY_MSG_VIEW (priv->msg_view), msg);
912 update_window_title (MODEST_MSG_VIEW_WINDOW (window));
913 update_branding (MODEST_MSG_VIEW_WINDOW (window));
915 /* gtk_widget_show_all (GTK_WIDGET (window)); */
916 modest_msg_view_window_update_priority (window);
917 /* Check dimming rules */
918 modest_ui_actions_check_toolbar_dimming_rules (MODEST_WINDOW (window));
919 modest_ui_actions_check_menu_dimming_rules (MODEST_WINDOW (window));
920 modest_window_check_dimming_rules_group (MODEST_WINDOW (window), MODEST_DIMMING_RULES_CLIPBOARD);
922 return MODEST_WINDOW(window);
926 modest_msg_view_window_new_from_header_view (ModestHeaderView *header_view,
927 const gchar *modest_account_name,
928 const gchar *mailbox,
929 const gchar *msg_uid,
930 GtkTreeRowReference *row_reference)
932 ModestMsgViewWindow *window = NULL;
933 ModestMsgViewWindowPrivate *priv = NULL;
934 TnyFolder *header_folder = NULL;
935 ModestWindowMgr *mgr = NULL;
939 mgr = modest_runtime_get_window_mgr ();
940 window = MODEST_MSG_VIEW_WINDOW (modest_window_mgr_get_msg_view_window (mgr));
941 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), NULL);
943 modest_msg_view_window_construct (window, modest_account_name, mailbox, msg_uid);
945 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
947 /* Remember the message list's TreeModel so we can detect changes
948 * and change the list selection when necessary: */
950 if (header_view != NULL){
951 header_folder = modest_header_view_get_folder(header_view);
952 /* This could happen if the header folder was
953 unseleted before opening this msg window (for
954 example if the user selects an account in the
955 folder view of the main window */
957 priv->is_outbox = (modest_tny_folder_guess_folder_type (header_folder) ==
958 TNY_FOLDER_TYPE_OUTBOX);
959 priv->header_folder_id = tny_folder_get_id(header_folder);
960 g_object_unref(header_folder);
964 /* Setup row references and connect signals */
965 priv->header_model = gtk_tree_view_get_model (GTK_TREE_VIEW (header_view));
966 g_object_ref (priv->header_model);
969 priv->row_reference = gtk_tree_row_reference_copy (row_reference);
970 priv->next_row_reference = gtk_tree_row_reference_copy (row_reference);
971 select_next_valid_row (priv->header_model, &(priv->next_row_reference), TRUE, priv->is_outbox);
973 priv->row_reference = NULL;
974 priv->next_row_reference = NULL;
977 /* Connect signals */
978 priv->row_changed_handler =
979 g_signal_connect (GTK_TREE_MODEL(priv->header_model), "row-changed",
980 G_CALLBACK(modest_msg_view_window_on_row_changed),
982 priv->row_deleted_handler =
983 g_signal_connect (GTK_TREE_MODEL(priv->header_model), "row-deleted",
984 G_CALLBACK(modest_msg_view_window_on_row_deleted),
986 priv->row_inserted_handler =
987 g_signal_connect (GTK_TREE_MODEL(priv->header_model), "row-inserted",
988 G_CALLBACK(modest_msg_view_window_on_row_inserted),
990 priv->rows_reordered_handler =
991 g_signal_connect(GTK_TREE_MODEL(priv->header_model), "rows-reordered",
992 G_CALLBACK(modest_msg_view_window_on_row_reordered),
995 if (header_view != NULL){
996 modest_header_view_add_observer(header_view,
997 MODEST_HEADER_VIEW_OBSERVER(window));
1000 tny_msg_view_set_msg (TNY_MSG_VIEW (priv->msg_view), NULL);
1001 update_branding (MODEST_MSG_VIEW_WINDOW (window));
1003 path = gtk_tree_row_reference_get_path (row_reference);
1004 if (gtk_tree_model_get_iter (priv->header_model, &iter, path)) {
1006 gtk_tree_model_get (priv->header_model, &iter,
1007 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
1009 message_reader (window, priv, header, row_reference);
1010 g_object_unref (header);
1012 gtk_tree_path_free (path);
1014 /* Check dimming rules */
1015 modest_ui_actions_check_toolbar_dimming_rules (MODEST_WINDOW (window));
1016 modest_ui_actions_check_menu_dimming_rules (MODEST_WINDOW (window));
1017 modest_window_check_dimming_rules_group (MODEST_WINDOW (window), MODEST_DIMMING_RULES_CLIPBOARD);
1019 return MODEST_WINDOW(window);
1023 modest_msg_view_window_new_for_search_result (TnyMsg *msg,
1024 const gchar *modest_account_name,
1025 const gchar *mailbox,
1026 const gchar *msg_uid)
1028 ModestMsgViewWindow *window = NULL;
1029 ModestMsgViewWindowPrivate *priv = NULL;
1030 ModestWindowMgr *mgr = NULL;
1032 mgr = modest_runtime_get_window_mgr ();
1033 window = MODEST_MSG_VIEW_WINDOW (modest_window_mgr_get_msg_view_window (mgr));
1034 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), NULL);
1035 modest_msg_view_window_construct (window, modest_account_name, mailbox, msg_uid);
1037 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1039 /* Remember that this is a search result,
1040 * so we can disable some UI appropriately: */
1041 priv->is_search_result = TRUE;
1043 tny_msg_view_set_msg (TNY_MSG_VIEW (priv->msg_view), msg);
1044 update_branding (MODEST_MSG_VIEW_WINDOW (window));
1046 update_window_title (window);
1047 /* gtk_widget_show_all (GTK_WIDGET (window));*/
1048 modest_msg_view_window_update_priority (window);
1050 /* Check dimming rules */
1051 modest_ui_actions_check_toolbar_dimming_rules (MODEST_WINDOW (window));
1052 modest_ui_actions_check_menu_dimming_rules (MODEST_WINDOW (window));
1053 modest_window_check_dimming_rules_group (MODEST_WINDOW (window), MODEST_DIMMING_RULES_CLIPBOARD);
1055 return MODEST_WINDOW(window);
1059 modest_msg_view_window_is_other_body (ModestMsgViewWindow *self)
1061 ModestMsgViewWindowPrivate *priv = NULL;
1063 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self), FALSE);
1064 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
1066 return (priv->other_body != NULL);
1070 modest_msg_view_window_new_with_other_body (TnyMsg *msg,
1071 TnyMimePart *other_body,
1072 const gchar *modest_account_name,
1073 const gchar *mailbox,
1074 const gchar *msg_uid)
1076 GObject *obj = NULL;
1077 ModestMsgViewWindowPrivate *priv;
1078 ModestWindowMgr *mgr = NULL;
1080 g_return_val_if_fail (msg, NULL);
1081 mgr = modest_runtime_get_window_mgr ();
1082 obj = G_OBJECT (modest_window_mgr_get_msg_view_window (mgr));
1083 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (obj);
1084 modest_msg_view_window_construct (MODEST_MSG_VIEW_WINDOW (obj),
1085 modest_account_name, mailbox, msg_uid);
1088 priv->other_body = g_object_ref (other_body);
1089 modest_msg_view_set_msg_with_other_body (MODEST_MSG_VIEW (priv->msg_view), msg, other_body);
1091 tny_msg_view_set_msg (TNY_MSG_VIEW (priv->msg_view), msg);
1093 update_window_title (MODEST_MSG_VIEW_WINDOW (obj));
1094 update_branding (MODEST_MSG_VIEW_WINDOW (obj));
1096 /* gtk_widget_show_all (GTK_WIDGET (obj)); */
1098 /* Check dimming rules */
1099 modest_ui_actions_check_toolbar_dimming_rules (MODEST_WINDOW (obj));
1100 modest_ui_actions_check_menu_dimming_rules (MODEST_WINDOW (obj));
1101 modest_window_check_dimming_rules_group (MODEST_WINDOW (obj), MODEST_DIMMING_RULES_CLIPBOARD);
1103 return MODEST_WINDOW(obj);
1107 modest_msg_view_window_new_for_attachment (TnyMsg *msg,
1108 const gchar *modest_account_name,
1109 const gchar *mailbox,
1110 const gchar *msg_uid)
1112 return modest_msg_view_window_new_with_other_body (msg, NULL, modest_account_name, mailbox, msg_uid);
1116 modest_msg_view_window_on_row_changed (GtkTreeModel *header_model,
1119 ModestMsgViewWindow *window)
1121 check_dimming_rules_after_change (window);
1125 modest_msg_view_window_on_row_deleted(GtkTreeModel *header_model,
1127 ModestMsgViewWindow *window)
1129 check_dimming_rules_after_change (window);
1131 /* The window could have dissapeared */
1134 check_dimming_rules_after_change (ModestMsgViewWindow *window)
1136 modest_ui_actions_check_menu_dimming_rules (MODEST_WINDOW (window));
1137 modest_ui_actions_check_toolbar_dimming_rules (MODEST_WINDOW (window));
1141 /* On insertions we check if the folder still has the message we are
1142 * showing or do not. If do not, we do nothing. Which means we are still
1143 * not attached to any header folder and thus next/prev buttons are
1144 * still dimmed. Once the message that is shown by msg-view is found, the
1145 * new model of header-view will be attached and the references will be set.
1146 * On each further insertions dimming rules will be checked. However
1147 * this requires extra CPU time at least works.
1148 * (An message might be deleted from TnyFolder and thus will not be
1149 * inserted into the model again for example if it is removed by the
1150 * imap server and the header view is refreshed.)
1153 modest_msg_view_window_on_row_inserted (GtkTreeModel *model,
1154 GtkTreePath *tree_path,
1155 GtkTreeIter *tree_iter,
1156 ModestMsgViewWindow *window)
1158 ModestMsgViewWindowPrivate *priv = NULL;
1159 TnyHeader *header = NULL;
1161 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window));
1162 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1164 g_assert (model == priv->header_model);
1166 /* Check if the newly inserted message is the same we are actually
1167 * showing. IF not, we should remain detached from the header model
1168 * and thus prev and next toolbar buttons should remain dimmed. */
1169 gtk_tree_model_get (model, tree_iter,
1170 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
1173 if (TNY_IS_HEADER (header)) {
1176 uid = modest_tny_folder_get_header_unique_id (header);
1177 if (!g_str_equal(priv->msg_uid, uid)) {
1178 check_dimming_rules_after_change (window);
1180 g_object_unref (G_OBJECT(header));
1184 g_object_unref(G_OBJECT(header));
1187 if (priv->row_reference) {
1188 gtk_tree_row_reference_free (priv->row_reference);
1191 /* Setup row_reference for the actual msg. */
1192 priv->row_reference = gtk_tree_row_reference_new (priv->header_model, tree_path);
1193 if (priv->row_reference == NULL) {
1194 g_warning("%s: No reference for msg header item.", __FUNCTION__);
1198 /* Now set up next_row_reference. */
1199 if (priv->next_row_reference) {
1200 gtk_tree_row_reference_free (priv->next_row_reference);
1203 priv->next_row_reference = gtk_tree_row_reference_copy (priv->row_reference);
1204 select_next_valid_row (priv->header_model,
1205 &(priv->next_row_reference), FALSE, priv->is_outbox);
1207 /* Connect the remaining callbacks to become able to detect
1208 * changes in header-view. */
1209 priv->row_changed_handler =
1210 g_signal_connect (priv->header_model, "row-changed",
1211 G_CALLBACK (modest_msg_view_window_on_row_changed),
1213 priv->row_deleted_handler =
1214 g_signal_connect (priv->header_model, "row-deleted",
1215 G_CALLBACK (modest_msg_view_window_on_row_deleted),
1217 priv->rows_reordered_handler =
1218 g_signal_connect (priv->header_model, "rows-reordered",
1219 G_CALLBACK (modest_msg_view_window_on_row_reordered),
1222 check_dimming_rules_after_change (window);
1226 modest_msg_view_window_on_row_reordered (GtkTreeModel *header_model,
1230 ModestMsgViewWindow *window)
1232 ModestMsgViewWindowPrivate *priv = NULL;
1233 gboolean already_changed = FALSE;
1235 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(window);
1237 /* If the current row was reordered select the proper next
1238 valid row. The same if the next row reference changes */
1239 if (priv->row_reference &&
1240 gtk_tree_row_reference_valid (priv->row_reference)) {
1242 path = gtk_tree_row_reference_get_path (priv->row_reference);
1243 if (gtk_tree_path_compare (path, arg1) == 0) {
1244 if (priv->next_row_reference) {
1245 gtk_tree_row_reference_free (priv->next_row_reference);
1247 priv->next_row_reference = gtk_tree_row_reference_copy (priv->row_reference);
1248 select_next_valid_row (header_model, &(priv->next_row_reference), FALSE, priv->is_outbox);
1249 already_changed = TRUE;
1251 gtk_tree_path_free (path);
1253 if (!already_changed &&
1254 priv->next_row_reference &&
1255 gtk_tree_row_reference_valid (priv->next_row_reference)) {
1257 path = gtk_tree_row_reference_get_path (priv->next_row_reference);
1258 if (gtk_tree_path_compare (path, arg1) == 0) {
1259 if (priv->next_row_reference) {
1260 gtk_tree_row_reference_free (priv->next_row_reference);
1262 priv->next_row_reference = gtk_tree_row_reference_copy (priv->row_reference);
1263 select_next_valid_row (header_model, &(priv->next_row_reference), FALSE, priv->is_outbox);
1265 gtk_tree_path_free (path);
1267 check_dimming_rules_after_change (window);
1270 /* The modest_msg_view_window_update_model_replaced implements update
1271 * function for ModestHeaderViewObserver. Checks whether the TnyFolder
1272 * actually belongs to the header-view is the same as the TnyFolder of
1273 * the message of msg-view or not. If they are different, there is
1274 * nothing to do. If they are the same, then the model has replaced and
1275 * the reference in msg-view shall be replaced from the old model to
1276 * the new model. In this case the view will be detached from it's
1277 * header folder. From this point the next/prev buttons are dimmed.
1280 modest_msg_view_window_update_model_replaced (ModestHeaderViewObserver *observer,
1281 GtkTreeModel *model,
1282 const gchar *tny_folder_id)
1284 ModestMsgViewWindowPrivate *priv = NULL;
1285 ModestMsgViewWindow *window = NULL;
1287 g_assert(MODEST_IS_HEADER_VIEW_OBSERVER(observer));
1288 g_assert(MODEST_IS_MSG_VIEW_WINDOW(observer));
1290 window = MODEST_MSG_VIEW_WINDOW(observer);
1291 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(window);
1293 /* If there is an other folder in the header-view then we do
1294 * not care about it's model (msg list). Else if the
1295 * header-view shows the folder the msg shown by us is in, we
1296 * shall replace our model reference and make some check. */
1297 if(model == NULL || tny_folder_id == NULL ||
1298 (priv->header_folder_id && !g_str_equal(tny_folder_id, priv->header_folder_id)))
1301 /* Model is changed(replaced), so we should forget the old
1302 * one. Because there might be other references and there
1303 * might be some change on the model even if we unreferenced
1304 * it, we need to disconnect our signals here. */
1305 if (priv->header_model) {
1306 if (g_signal_handler_is_connected(G_OBJECT (priv->header_model),
1307 priv->row_changed_handler))
1308 g_signal_handler_disconnect(G_OBJECT (priv->header_model),
1309 priv->row_changed_handler);
1310 if (g_signal_handler_is_connected(G_OBJECT (priv->header_model),
1311 priv->row_deleted_handler))
1312 g_signal_handler_disconnect(G_OBJECT (priv->header_model),
1313 priv->row_deleted_handler);
1314 if (g_signal_handler_is_connected(G_OBJECT (priv->header_model),
1315 priv->row_inserted_handler))
1316 g_signal_handler_disconnect(G_OBJECT (priv->header_model),
1317 priv->row_inserted_handler);
1318 if (g_signal_handler_is_connected(G_OBJECT (priv->header_model),
1319 priv->rows_reordered_handler))
1320 g_signal_handler_disconnect(G_OBJECT (priv->header_model),
1321 priv->rows_reordered_handler);
1324 if (priv->row_reference)
1325 gtk_tree_row_reference_free (priv->row_reference);
1326 if (priv->next_row_reference)
1327 gtk_tree_row_reference_free (priv->next_row_reference);
1328 g_object_unref(priv->header_model);
1331 priv->row_changed_handler = 0;
1332 priv->row_deleted_handler = 0;
1333 priv->row_inserted_handler = 0;
1334 priv->rows_reordered_handler = 0;
1335 priv->next_row_reference = NULL;
1336 priv->row_reference = NULL;
1337 priv->header_model = NULL;
1340 priv->header_model = g_object_ref (model);
1342 /* Also we must connect to the new model for row insertions.
1343 * Only for insertions now. We will need other ones only after
1344 * the msg is show by msg-view is added to the new model. */
1345 priv->row_inserted_handler =
1346 g_signal_connect (priv->header_model, "row-inserted",
1347 G_CALLBACK(modest_msg_view_window_on_row_inserted),
1350 modest_ui_actions_check_menu_dimming_rules(MODEST_WINDOW(window));
1351 modest_ui_actions_check_toolbar_dimming_rules(MODEST_WINDOW(window));
1355 modest_msg_view_window_toolbar_on_transfer_mode (ModestMsgViewWindow *self)
1357 ModestMsgViewWindowPrivate *priv= NULL;
1359 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self), FALSE);
1360 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
1362 return priv->progress_hint;
1366 modest_msg_view_window_get_header (ModestMsgViewWindow *self)
1368 ModestMsgViewWindowPrivate *priv= NULL;
1370 TnyHeader *header = NULL;
1371 GtkTreePath *path = NULL;
1374 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self), NULL);
1375 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
1377 /* If the message was not obtained from a treemodel,
1378 * for instance if it was opened directly by the search UI:
1380 if (priv->header_model == NULL ||
1381 priv->row_reference == NULL ||
1382 !gtk_tree_row_reference_valid (priv->row_reference)) {
1383 msg = modest_msg_view_window_get_message (self);
1385 header = tny_msg_get_header (msg);
1386 g_object_unref (msg);
1391 /* Get iter of the currently selected message in the header view: */
1392 path = gtk_tree_row_reference_get_path (priv->row_reference);
1393 g_return_val_if_fail (path != NULL, NULL);
1394 gtk_tree_model_get_iter (priv->header_model,
1398 /* Get current message header */
1399 gtk_tree_model_get (priv->header_model, &iter,
1400 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
1403 gtk_tree_path_free (path);
1408 modest_msg_view_window_get_message (ModestMsgViewWindow *self)
1410 ModestMsgViewWindowPrivate *priv;
1412 g_return_val_if_fail (self, NULL);
1414 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(self);
1416 return tny_msg_view_get_msg (TNY_MSG_VIEW (priv->msg_view));
1420 modest_msg_view_window_get_message_uid (ModestMsgViewWindow *self)
1422 ModestMsgViewWindowPrivate *priv;
1424 g_return_val_if_fail (self, NULL);
1426 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
1428 return (const gchar*) priv->msg_uid;
1432 modest_msg_view_window_toggle_find_toolbar (GtkToggleAction *toggle,
1435 ModestMsgViewWindow *window = MODEST_MSG_VIEW_WINDOW (data);
1436 ModestMsgViewWindowPrivate *priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1437 ModestWindowPrivate *parent_priv = MODEST_WINDOW_GET_PRIVATE (window);
1441 is_active = gtk_toggle_action_get_active (toggle);
1444 gtk_widget_show (priv->find_toolbar);
1445 hildon_find_toolbar_highlight_entry (HILDON_FIND_TOOLBAR (priv->find_toolbar), TRUE);
1447 gtk_widget_hide (priv->find_toolbar);
1448 modest_msg_view_grab_focus (MODEST_MSG_VIEW (priv->msg_view));
1451 /* update the toggle buttons status */
1452 action = gtk_ui_manager_get_action (parent_priv->ui_manager, "/ToolBar/FindInMessage");
1454 modest_utils_toggle_action_set_active_block_notify (GTK_TOGGLE_ACTION (action), is_active);
1459 modest_msg_view_window_find_toolbar_close (GtkWidget *widget,
1460 ModestMsgViewWindow *obj)
1462 GtkToggleAction *toggle;
1463 ModestWindowPrivate *parent_priv;
1464 ModestMsgViewWindowPrivate *priv;
1466 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (obj);
1467 parent_priv = MODEST_WINDOW_GET_PRIVATE (obj);
1469 toggle = GTK_TOGGLE_ACTION (gtk_ui_manager_get_action (parent_priv->ui_manager, "/ToolBar/FindInMessage"));
1470 gtk_toggle_action_set_active (toggle, FALSE);
1471 modest_msg_view_grab_focus (MODEST_MSG_VIEW (priv->msg_view));
1475 modest_msg_view_window_find_toolbar_search (GtkWidget *widget,
1476 ModestMsgViewWindow *obj)
1478 gchar *current_search;
1479 ModestMsgViewWindowPrivate *priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (obj);
1481 if (modest_mime_part_view_is_empty (MODEST_MIME_PART_VIEW (priv->msg_view))) {
1482 hildon_banner_show_information (NULL, NULL, _("mail_ib_nothing_to_find"));
1486 g_object_get (G_OBJECT (widget), "prefix", ¤t_search, NULL);
1488 if ((current_search == NULL) || (strcmp (current_search, "") == 0)) {
1489 g_free (current_search);
1490 hildon_banner_show_information (NULL, NULL, _CS("ecdg_ib_find_rep_enter_text"));
1494 if ((priv->last_search == NULL) || (strcmp (priv->last_search, current_search) != 0)) {
1496 g_free (priv->last_search);
1497 priv->last_search = g_strdup (current_search);
1498 result = modest_isearch_view_search (MODEST_ISEARCH_VIEW (priv->msg_view),
1501 hildon_banner_show_information (NULL, NULL,
1502 _HL("ckct_ib_find_no_matches"));
1503 g_free (priv->last_search);
1504 priv->last_search = NULL;
1506 hildon_find_toolbar_highlight_entry (HILDON_FIND_TOOLBAR (priv->find_toolbar), TRUE);
1509 if (!modest_isearch_view_search_next (MODEST_ISEARCH_VIEW (priv->msg_view))) {
1510 hildon_banner_show_information (NULL, NULL,
1511 _HL("ckct_ib_find_search_complete"));
1512 g_free (priv->last_search);
1513 priv->last_search = NULL;
1515 hildon_find_toolbar_highlight_entry (HILDON_FIND_TOOLBAR (priv->find_toolbar), TRUE);
1519 g_free (current_search);
1524 modest_msg_view_window_set_zoom (ModestWindow *window,
1527 ModestMsgViewWindowPrivate *priv;
1528 ModestWindowPrivate *parent_priv;
1530 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window));
1532 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1533 parent_priv = MODEST_WINDOW_GET_PRIVATE (window);
1534 modest_zoomable_set_zoom (MODEST_ZOOMABLE (priv->msg_view), zoom);
1539 modest_msg_view_window_get_zoom (ModestWindow *window)
1541 ModestMsgViewWindowPrivate *priv;
1543 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), 1.0);
1545 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1546 return modest_zoomable_get_zoom (MODEST_ZOOMABLE (priv->msg_view));
1550 modest_msg_view_window_zoom_plus (ModestWindow *window)
1553 ModestMsgViewWindowPrivate *priv;
1557 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), 1.0);
1558 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1560 zoom_level = modest_zoomable_get_zoom (MODEST_ZOOMABLE (priv->msg_view));
1562 if (zoom_level >= 2.0) {
1563 hildon_banner_show_information (NULL, NULL,
1564 _CS("ckct_ib_max_zoom_level_reached"));
1566 } else if (zoom_level >= 1.5) {
1568 } else if (zoom_level >= 1.2) {
1570 } else if (zoom_level >= 1.0) {
1572 } else if (zoom_level >= 0.8) {
1574 } else if (zoom_level >= 0.5) {
1580 /* set zoom level */
1581 int_zoom = (gint) rint (zoom_level*100.0+0.1);
1582 banner_text = g_strdup_printf (_HL("wdgt_ib_zoom"), int_zoom);
1583 modest_platform_information_banner (GTK_WIDGET (window), NULL, banner_text);
1584 g_free (banner_text);
1585 modest_zoomable_set_zoom (MODEST_ZOOMABLE (priv->msg_view), zoom_level);
1591 modest_msg_view_window_zoom_minus (ModestWindow *window)
1594 ModestMsgViewWindowPrivate *priv;
1598 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), 1.0);
1599 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1601 zoom_level = modest_zoomable_get_zoom (MODEST_ZOOMABLE (priv->msg_view));
1603 if (zoom_level <= 0.5) {
1604 hildon_banner_show_information (NULL, NULL,
1605 _CS("ckct_ib_min_zoom_level_reached"));
1607 } else if (zoom_level <= 0.8) {
1609 } else if (zoom_level <= 1.0) {
1611 } else if (zoom_level <= 1.2) {
1613 } else if (zoom_level <= 1.5) {
1615 } else if (zoom_level <= 2.0) {
1621 /* set zoom level */
1622 int_zoom = (gint) rint (zoom_level*100.0+0.1);
1623 banner_text = g_strdup_printf (_HL("wdgt_ib_zoom"), int_zoom);
1624 modest_platform_information_banner (GTK_WIDGET (window), NULL, banner_text);
1625 g_free (banner_text);
1626 modest_zoomable_set_zoom (MODEST_ZOOMABLE (priv->msg_view), zoom_level);
1633 modest_msg_view_window_key_event (GtkWidget *window,
1639 focus = gtk_window_get_focus (GTK_WINDOW (window));
1641 /* for the find toolbar case */
1642 if (focus && GTK_IS_ENTRY (focus)) {
1643 if (event->keyval == GDK_BackSpace) {
1645 copy = gdk_event_copy ((GdkEvent *) event);
1646 gtk_widget_event (focus, copy);
1647 gdk_event_free (copy);
1652 if (event->keyval == GDK_Up || event->keyval == GDK_KP_Up ||
1653 event->keyval == GDK_Down || event->keyval == GDK_KP_Down ||
1654 event->keyval == GDK_Page_Up || event->keyval == GDK_KP_Page_Up ||
1655 event->keyval == GDK_Page_Down || event->keyval == GDK_KP_Page_Down ||
1656 event->keyval == GDK_Home || event->keyval == GDK_KP_Home ||
1657 event->keyval == GDK_End || event->keyval == GDK_KP_End) {
1658 /* ModestMsgViewWindowPrivate *priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window); */
1659 /* gboolean return_value; */
1661 if (event->type == GDK_KEY_PRESS) {
1662 GtkScrollType scroll_type;
1664 switch (event->keyval) {
1667 scroll_type = GTK_SCROLL_STEP_UP; break;
1670 scroll_type = GTK_SCROLL_STEP_DOWN; break;
1672 case GDK_KP_Page_Up:
1673 scroll_type = GTK_SCROLL_PAGE_UP; break;
1675 case GDK_KP_Page_Down:
1676 scroll_type = GTK_SCROLL_PAGE_DOWN; break;
1679 scroll_type = GTK_SCROLL_START; break;
1682 scroll_type = GTK_SCROLL_END; break;
1683 default: scroll_type = GTK_SCROLL_NONE;
1686 /* g_signal_emit_by_name (G_OBJECT (priv->main_scroll), "scroll-child", */
1687 /* scroll_type, FALSE, &return_value); */
1698 modest_msg_view_window_last_message_selected (ModestMsgViewWindow *window)
1701 ModestMsgViewWindowPrivate *priv;
1702 GtkTreeIter tmp_iter;
1703 gboolean is_last_selected;
1705 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), TRUE);
1706 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1708 /*if no model (so no rows at all), then virtually we are the last*/
1709 if (!priv->header_model || !priv->row_reference)
1712 if (!gtk_tree_row_reference_valid (priv->row_reference))
1715 path = gtk_tree_row_reference_get_path (priv->row_reference);
1719 is_last_selected = TRUE;
1720 while (is_last_selected) {
1722 gtk_tree_path_next (path);
1723 if (!gtk_tree_model_get_iter (priv->header_model, &tmp_iter, path))
1725 gtk_tree_model_get (priv->header_model, &tmp_iter,
1726 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
1729 if (msg_is_visible (header, priv->is_outbox))
1730 is_last_selected = FALSE;
1731 g_object_unref(G_OBJECT(header));
1734 gtk_tree_path_free (path);
1735 return is_last_selected;
1739 modest_msg_view_window_has_headers_model (ModestMsgViewWindow *window)
1741 ModestMsgViewWindowPrivate *priv;
1743 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), TRUE);
1744 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1746 return priv->header_model != NULL;
1750 modest_msg_view_window_is_search_result (ModestMsgViewWindow *window)
1752 ModestMsgViewWindowPrivate *priv;
1754 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), TRUE);
1755 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1757 return priv->is_search_result;
1761 msg_is_visible (TnyHeader *header, gboolean check_outbox)
1763 if ((tny_header_get_flags(header) & TNY_HEADER_FLAG_DELETED))
1765 if (!check_outbox) {
1768 ModestTnySendQueueStatus status;
1769 status = modest_tny_all_send_queues_get_msg_status (header);
1770 return ((status != MODEST_TNY_SEND_QUEUE_FAILED) &&
1771 (status != MODEST_TNY_SEND_QUEUE_SENDING));
1776 modest_msg_view_window_first_message_selected (ModestMsgViewWindow *window)
1779 ModestMsgViewWindowPrivate *priv;
1780 gboolean is_first_selected;
1781 GtkTreeIter tmp_iter;
1783 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), TRUE);
1784 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1786 /*if no model (so no rows at all), then virtually we are the first*/
1787 if (!priv->header_model || !priv->row_reference)
1790 if (!gtk_tree_row_reference_valid (priv->row_reference))
1793 path = gtk_tree_row_reference_get_path (priv->row_reference);
1797 is_first_selected = TRUE;
1798 while (is_first_selected) {
1800 if(!gtk_tree_path_prev (path))
1802 /* Here the 'if' is needless for logic, but let make sure
1803 * iter is valid for gtk_tree_model_get. */
1804 if (!gtk_tree_model_get_iter (priv->header_model, &tmp_iter, path))
1806 gtk_tree_model_get (priv->header_model, &tmp_iter,
1807 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
1810 if (msg_is_visible (header, priv->is_outbox))
1811 is_first_selected = FALSE;
1812 g_object_unref(G_OBJECT(header));
1815 gtk_tree_path_free (path);
1816 return is_first_selected;
1821 GtkTreeRowReference *row_reference;
1825 message_reader_performer (gboolean canceled,
1827 GtkWindow *parent_window,
1828 TnyAccount *account,
1831 ModestMailOperation *mail_op = NULL;
1832 MsgReaderInfo *info;
1834 info = (MsgReaderInfo *) user_data;
1835 if (canceled || err) {
1836 update_window_title (MODEST_MSG_VIEW_WINDOW (parent_window));
1840 /* Register the header - it'll be unregistered in the callback */
1841 modest_window_mgr_register_header (modest_runtime_get_window_mgr (), info->header, NULL);
1843 /* New mail operation */
1844 mail_op = modest_mail_operation_new_with_error_handling (G_OBJECT(parent_window),
1845 modest_ui_actions_disk_operations_error_handler,
1848 modest_mail_operation_queue_add (modest_runtime_get_mail_operation_queue (), mail_op);
1849 modest_mail_operation_get_msg (mail_op, info->header, TRUE, view_msg_cb, info->row_reference);
1850 g_object_unref (mail_op);
1852 /* Update dimming rules */
1853 modest_ui_actions_check_toolbar_dimming_rules (MODEST_WINDOW (parent_window));
1854 modest_ui_actions_check_menu_dimming_rules (MODEST_WINDOW (parent_window));
1857 /* Frees. The row_reference will be freed by the view_msg_cb callback */
1858 g_object_unref (info->header);
1859 g_slice_free (MsgReaderInfo, info);
1864 * Reads the message whose summary item is @header. It takes care of
1865 * several things, among others:
1867 * If the message was not previously downloaded then ask the user
1868 * before downloading. If there is no connection launch the connection
1869 * dialog. Update toolbar dimming rules.
1871 * Returns: TRUE if the mail operation was started, otherwise if the
1872 * user do not want to download the message, or if the user do not
1873 * want to connect, then the operation is not issued
1876 message_reader (ModestMsgViewWindow *window,
1877 ModestMsgViewWindowPrivate *priv,
1879 GtkTreeRowReference *row_reference)
1881 ModestWindowMgr *mgr;
1882 TnyAccount *account;
1884 MsgReaderInfo *info;
1886 g_return_val_if_fail (row_reference != NULL, FALSE);
1888 /* We set the header from model while we're loading */
1889 tny_header_view_set_header (TNY_HEADER_VIEW (priv->msg_view), header);
1890 gtk_window_set_title (GTK_WINDOW (window), _CS("ckdg_pb_updating"));
1892 mgr = modest_runtime_get_window_mgr ();
1893 /* Msg download completed */
1894 if (!(tny_header_get_flags (header) & TNY_HEADER_FLAG_CACHED)) {
1896 /* Ask the user if he wants to download the message if
1898 if (!tny_device_is_online (modest_runtime_get_device())) {
1899 GtkResponseType response;
1901 response = modest_platform_run_confirmation_dialog (GTK_WINDOW (window),
1902 _("mcen_nc_get_msg"));
1903 if (response == GTK_RESPONSE_CANCEL) {
1904 update_window_title (window);
1908 folder = tny_header_get_folder (header);
1909 info = g_slice_new (MsgReaderInfo);
1910 info->header = g_object_ref (header);
1911 info->row_reference = gtk_tree_row_reference_copy (row_reference);
1913 /* Offer the connection dialog if necessary */
1914 modest_platform_connect_if_remote_and_perform ((GtkWindow *) window,
1916 TNY_FOLDER_STORE (folder),
1917 message_reader_performer,
1919 g_object_unref (folder);
1924 folder = tny_header_get_folder (header);
1925 account = tny_folder_get_account (folder);
1926 info = g_slice_new (MsgReaderInfo);
1927 info->header = g_object_ref (header);
1928 info->row_reference = gtk_tree_row_reference_copy (row_reference);
1930 message_reader_performer (FALSE, NULL, (GtkWindow *) window, account, info);
1931 g_object_unref (account);
1932 g_object_unref (folder);
1938 modest_msg_view_window_select_next_message (ModestMsgViewWindow *window)
1940 ModestMsgViewWindowPrivate *priv;
1941 GtkTreePath *path= NULL;
1942 GtkTreeIter tmp_iter;
1944 gboolean retval = TRUE;
1945 GtkTreeRowReference *row_reference = NULL;
1947 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), FALSE);
1948 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1950 if (!priv->row_reference)
1953 /* Update the next row reference if it's not valid. This could
1954 happen if for example the header which it was pointing to,
1955 was deleted. The best place to do it is in the row-deleted
1956 handler but the tinymail model do not work like the glib
1957 tree models and reports the deletion when the row is still
1959 if (!gtk_tree_row_reference_valid (priv->next_row_reference)) {
1960 if (gtk_tree_row_reference_valid (priv->row_reference)) {
1961 priv->next_row_reference = gtk_tree_row_reference_copy (priv->row_reference);
1962 select_next_valid_row (priv->header_model, &(priv->next_row_reference), FALSE, priv->is_outbox);
1965 if (priv->next_row_reference)
1966 path = gtk_tree_row_reference_get_path (priv->next_row_reference);
1970 row_reference = gtk_tree_row_reference_copy (priv->next_row_reference);
1972 gtk_tree_model_get_iter (priv->header_model,
1975 gtk_tree_path_free (path);
1977 gtk_tree_model_get (priv->header_model, &tmp_iter,
1978 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
1981 /* Read the message & show it */
1982 if (!message_reader (window, priv, header, row_reference)) {
1985 gtk_tree_row_reference_free (row_reference);
1988 g_object_unref (header);
1994 modest_msg_view_window_select_previous_message (ModestMsgViewWindow *window)
1996 ModestMsgViewWindowPrivate *priv = NULL;
1998 gboolean finished = FALSE;
1999 gboolean retval = FALSE;
2001 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), FALSE);
2002 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
2004 /* Return inmediatly if there is no header model */
2005 if (!priv->header_model || !priv->row_reference)
2008 path = gtk_tree_row_reference_get_path (priv->row_reference);
2009 while (!finished && gtk_tree_path_prev (path)) {
2013 gtk_tree_model_get_iter (priv->header_model, &iter, path);
2014 gtk_tree_model_get (priv->header_model, &iter,
2015 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
2019 if (msg_is_visible (header, priv->is_outbox)) {
2020 GtkTreeRowReference *row_reference;
2021 row_reference = gtk_tree_row_reference_new (priv->header_model, path);
2022 /* Read the message & show it */
2023 retval = message_reader (window, priv, header, row_reference);
2024 gtk_tree_row_reference_free (row_reference);
2028 g_object_unref (header);
2032 gtk_tree_path_free (path);
2037 view_msg_cb (ModestMailOperation *mail_op,
2044 ModestMsgViewWindow *self = NULL;
2045 ModestMsgViewWindowPrivate *priv = NULL;
2046 GtkTreeRowReference *row_reference = NULL;
2048 /* Unregister the header (it was registered before creating the mail operation) */
2049 modest_window_mgr_unregister_header (modest_runtime_get_window_mgr (), header);
2051 row_reference = (GtkTreeRowReference *) user_data;
2053 gtk_tree_row_reference_free (row_reference);
2054 self = (ModestMsgViewWindow *) modest_mail_operation_get_source (mail_op);
2056 /* Restore window title */
2057 update_window_title (self);
2058 g_object_unref (self);
2063 /* If there was any error */
2064 if (!modest_ui_actions_msg_retrieval_check (mail_op, header, msg)) {
2065 gtk_tree_row_reference_free (row_reference);
2066 self = (ModestMsgViewWindow *) modest_mail_operation_get_source (mail_op);
2068 /* Restore window title */
2069 update_window_title (self);
2070 g_object_unref (self);
2075 /* Get the window */
2076 self = (ModestMsgViewWindow *) modest_mail_operation_get_source (mail_op);
2077 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self));
2078 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
2080 /* Update the row reference */
2081 if (priv->row_reference != NULL) {
2082 gtk_tree_row_reference_free (priv->row_reference);
2083 priv->row_reference = gtk_tree_row_reference_copy (row_reference);
2084 if (priv->next_row_reference != NULL) {
2085 gtk_tree_row_reference_free (priv->next_row_reference);
2087 priv->next_row_reference = gtk_tree_row_reference_copy (priv->row_reference);
2088 select_next_valid_row (priv->header_model, &(priv->next_row_reference), TRUE, priv->is_outbox);
2091 /* Mark header as read */
2092 if (!(tny_header_get_flags (header) & TNY_HEADER_FLAG_SEEN))
2093 tny_header_set_flag (header, TNY_HEADER_FLAG_SEEN);
2095 /* Set new message */
2096 if (priv->msg_view != NULL && TNY_IS_MSG_VIEW (priv->msg_view)) {
2097 tny_msg_view_set_msg (TNY_MSG_VIEW (priv->msg_view), msg);
2098 modest_msg_view_window_update_priority (self);
2099 update_window_title (MODEST_MSG_VIEW_WINDOW (self));
2100 update_branding (MODEST_MSG_VIEW_WINDOW (self));
2101 modest_msg_view_grab_focus (MODEST_MSG_VIEW (priv->msg_view));
2104 /* Set the new message uid of the window */
2105 if (priv->msg_uid) {
2106 g_free (priv->msg_uid);
2107 priv->msg_uid = modest_tny_folder_get_header_unique_id (header);
2110 /* Notify the observers */
2111 g_signal_emit (G_OBJECT (self), signals[MSG_CHANGED_SIGNAL],
2112 0, priv->header_model, priv->row_reference);
2115 g_object_unref (self);
2116 gtk_tree_row_reference_free (row_reference);
2120 modest_msg_view_window_get_folder_type (ModestMsgViewWindow *window)
2122 ModestMsgViewWindowPrivate *priv;
2124 TnyFolderType folder_type;
2126 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
2128 folder_type = TNY_FOLDER_TYPE_UNKNOWN;
2130 msg = tny_msg_view_get_msg (TNY_MSG_VIEW (priv->msg_view));
2134 folder = tny_msg_get_folder (msg);
2136 folder_type = modest_tny_folder_guess_folder_type (folder);
2137 g_object_unref (folder);
2139 g_object_unref (msg);
2147 modest_msg_view_window_update_priority (ModestMsgViewWindow *window)
2149 ModestMsgViewWindowPrivate *priv;
2150 TnyHeader *header = NULL;
2151 TnyHeaderFlags flags = 0;
2153 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
2155 if (priv->header_model && priv->row_reference) {
2157 GtkTreePath *path = NULL;
2159 path = gtk_tree_row_reference_get_path (priv->row_reference);
2160 g_return_if_fail (path != NULL);
2161 gtk_tree_model_get_iter (priv->header_model,
2163 gtk_tree_row_reference_get_path (priv->row_reference));
2165 gtk_tree_model_get (priv->header_model, &iter, TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
2167 gtk_tree_path_free (path);
2170 msg = tny_msg_view_get_msg (TNY_MSG_VIEW (priv->msg_view));
2172 header = tny_msg_get_header (msg);
2173 g_object_unref (msg);
2178 flags = tny_header_get_flags (header);
2179 g_object_unref(G_OBJECT(header));
2182 modest_msg_view_set_priority (MODEST_MSG_VIEW(priv->msg_view), flags);
2187 toolbar_resize (ModestMsgViewWindow *self)
2189 ModestMsgViewWindowPrivate *priv = NULL;
2190 ModestWindowPrivate *parent_priv = NULL;
2192 gint static_button_size;
2193 ModestWindowMgr *mgr;
2195 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self));
2196 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
2197 parent_priv = MODEST_WINDOW_GET_PRIVATE(self);
2199 mgr = modest_runtime_get_window_mgr ();
2200 static_button_size = modest_window_mgr_get_fullscreen_mode (mgr)?120:120;
2202 if (parent_priv->toolbar) {
2203 /* left size buttons */
2204 widget = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/ToolbarMessageReply");
2205 gtk_tool_item_set_expand (GTK_TOOL_ITEM (widget), FALSE);
2206 gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (widget), FALSE);
2207 gtk_widget_set_size_request (GTK_WIDGET (widget), static_button_size, -1);
2208 widget = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/ToolbarMessageMoveTo");
2209 gtk_tool_item_set_expand (GTK_TOOL_ITEM (widget), FALSE);
2210 gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (widget), FALSE);
2211 gtk_widget_set_size_request (GTK_WIDGET (widget), static_button_size, -1);
2212 widget = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/ToolbarDeleteMessage");
2213 gtk_tool_item_set_expand (GTK_TOOL_ITEM (widget), FALSE);
2214 gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (widget), FALSE);
2215 gtk_widget_set_size_request (GTK_WIDGET (widget), static_button_size, -1);
2216 widget = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/FindInMessage");
2217 gtk_tool_item_set_expand (GTK_TOOL_ITEM (widget), FALSE);
2218 gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (widget), FALSE);
2219 gtk_widget_set_size_request (GTK_WIDGET (widget), static_button_size, -1);
2221 gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (priv->next_toolitem), TRUE);
2222 gtk_tool_item_set_expand (GTK_TOOL_ITEM (priv->next_toolitem), TRUE);
2223 gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (priv->prev_toolitem), TRUE);
2224 gtk_tool_item_set_expand (GTK_TOOL_ITEM (priv->prev_toolitem), TRUE);
2229 modest_msg_view_window_show_toolbar (ModestWindow *self,
2230 gboolean show_toolbar)
2232 ModestMsgViewWindowPrivate *priv = NULL;
2233 ModestWindowPrivate *parent_priv;
2235 parent_priv = MODEST_WINDOW_GET_PRIVATE(self);
2236 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(self);
2238 /* Set optimized view status */
2239 priv->optimized_view = !show_toolbar;
2241 if (!parent_priv->toolbar) {
2242 parent_priv->toolbar = gtk_ui_manager_get_widget (parent_priv->ui_manager,
2244 gtk_toolbar_set_icon_size (GTK_TOOLBAR (parent_priv->toolbar), HILDON_ICON_SIZE_FINGER);
2245 gtk_widget_set_no_show_all (parent_priv->toolbar, TRUE);
2247 priv->next_toolitem = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/ToolbarMessageNext");
2248 priv->prev_toolitem = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/ToolbarMessageBack");
2249 toolbar_resize (MODEST_MSG_VIEW_WINDOW (self));
2252 hildon_window_add_toolbar (HILDON_WINDOW (self),
2253 GTK_TOOLBAR (parent_priv->toolbar));
2258 /* Quick hack: this prevents toolbar icons "dance" when progress bar show status is changed */
2259 /* TODO: resize mode migth be GTK_RESIZE_QUEUE, in order to avoid unneccesary shows */
2260 gtk_container_set_resize_mode (GTK_CONTAINER(parent_priv->toolbar), GTK_RESIZE_IMMEDIATE);
2262 gtk_widget_show (GTK_WIDGET (parent_priv->toolbar));
2263 if (modest_msg_view_window_transfer_mode_enabled (MODEST_MSG_VIEW_WINDOW (self)))
2264 set_progress_hint (MODEST_MSG_VIEW_WINDOW (self), TRUE);
2266 set_progress_hint (MODEST_MSG_VIEW_WINDOW (self), FALSE);
2269 gtk_widget_set_no_show_all (parent_priv->toolbar, TRUE);
2270 gtk_widget_hide (GTK_WIDGET (parent_priv->toolbar));
2275 modest_msg_view_window_clipboard_owner_change (GtkClipboard *clipboard,
2277 ModestMsgViewWindow *window)
2279 if (!GTK_WIDGET_VISIBLE (window))
2282 modest_window_check_dimming_rules_group (MODEST_WINDOW (window), MODEST_DIMMING_RULES_CLIPBOARD);
2286 modest_msg_view_window_transfer_mode_enabled (ModestMsgViewWindow *self)
2288 ModestMsgViewWindowPrivate *priv;
2290 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self), FALSE);
2291 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(self);
2293 return priv->progress_hint;
2297 observers_empty (ModestMsgViewWindow *self)
2300 ModestMsgViewWindowPrivate *priv;
2301 gboolean is_empty = TRUE;
2302 guint pending_ops = 0;
2304 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(self);
2305 tmp = priv->progress_widgets;
2307 /* Check all observers */
2308 while (tmp && is_empty) {
2309 pending_ops = modest_progress_object_num_pending_operations (MODEST_PROGRESS_OBJECT(tmp->data));
2310 is_empty = pending_ops == 0;
2312 tmp = g_slist_next(tmp);
2319 on_account_removed (TnyAccountStore *account_store,
2320 TnyAccount *account,
2323 /* Do nothing if it's a transport account, because we only
2324 show the messages of a store account */
2325 if (tny_account_get_account_type(account) == TNY_ACCOUNT_TYPE_STORE) {
2326 const gchar *parent_acc = NULL;
2327 const gchar *our_acc = NULL;
2329 our_acc = modest_window_get_active_account (MODEST_WINDOW (user_data));
2330 parent_acc = modest_tny_account_get_parent_modest_account_name_for_server_account (account);
2332 /* Close this window if I'm showing a message of the removed account */
2333 if (our_acc && parent_acc && strcmp (parent_acc, our_acc) == 0)
2334 modest_ui_actions_on_close_window (NULL, MODEST_WINDOW (user_data));
2339 on_mail_operation_started (ModestMailOperation *mail_op,
2342 ModestMsgViewWindow *self;
2343 ModestMailOperationTypeOperation op_type;
2345 ModestMsgViewWindowPrivate *priv;
2346 GObject *source = NULL;
2348 self = MODEST_MSG_VIEW_WINDOW (user_data);
2349 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
2350 op_type = modest_mail_operation_get_type_operation (mail_op);
2351 tmp = priv->progress_widgets;
2352 source = modest_mail_operation_get_source(mail_op);
2353 if (G_OBJECT (self) == source) {
2354 if (op_type == MODEST_MAIL_OPERATION_TYPE_RECEIVE) {
2355 set_toolbar_transfer_mode(self);
2357 modest_progress_object_add_operation (
2358 MODEST_PROGRESS_OBJECT (tmp->data),
2360 tmp = g_slist_next (tmp);
2364 g_object_unref (source);
2368 on_mail_operation_finished (ModestMailOperation *mail_op,
2371 ModestMsgViewWindow *self;
2372 ModestMailOperationTypeOperation op_type;
2374 ModestMsgViewWindowPrivate *priv;
2376 self = MODEST_MSG_VIEW_WINDOW (user_data);
2377 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
2378 op_type = modest_mail_operation_get_type_operation (mail_op);
2379 tmp = priv->progress_widgets;
2381 if (op_type == MODEST_MAIL_OPERATION_TYPE_RECEIVE) {
2383 modest_progress_object_remove_operation (MODEST_PROGRESS_OBJECT (tmp->data),
2385 tmp = g_slist_next (tmp);
2388 /* If no more operations are being observed, NORMAL mode is enabled again */
2389 if (observers_empty (self)) {
2390 set_progress_hint (self, FALSE);
2394 /* Update dimming rules. We have to do this right here
2395 and not in view_msg_cb because at that point the
2396 transfer mode is still enabled so the dimming rule
2397 won't let the user delete the message that has been
2398 readed for example */
2399 check_dimming_rules_after_change (self);
2404 on_queue_changed (ModestMailOperationQueue *queue,
2405 ModestMailOperation *mail_op,
2406 ModestMailOperationQueueNotification type,
2407 ModestMsgViewWindow *self)
2409 ModestMsgViewWindowPrivate *priv;
2411 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
2413 /* If this operations was created by another window, do nothing */
2414 if (!modest_mail_operation_is_mine (mail_op, G_OBJECT(self)))
2417 if (type == MODEST_MAIL_OPERATION_QUEUE_OPERATION_ADDED) {
2418 priv->sighandlers = modest_signal_mgr_connect (priv->sighandlers,
2420 "operation-started",
2421 G_CALLBACK (on_mail_operation_started),
2423 priv->sighandlers = modest_signal_mgr_connect (priv->sighandlers,
2425 "operation-finished",
2426 G_CALLBACK (on_mail_operation_finished),
2428 } else if (type == MODEST_MAIL_OPERATION_QUEUE_OPERATION_REMOVED) {
2429 priv->sighandlers = modest_signal_mgr_disconnect (priv->sighandlers,
2431 "operation-started");
2432 priv->sighandlers = modest_signal_mgr_disconnect (priv->sighandlers,
2434 "operation-finished");
2439 modest_msg_view_window_get_attachments (ModestMsgViewWindow *win)
2441 ModestMsgViewWindowPrivate *priv;
2442 TnyList *selected_attachments = NULL;
2444 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (win), NULL);
2445 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (win);
2447 /* In Hildon 2.2 as there's no selection we assume we have all attachments selected */
2448 selected_attachments = modest_msg_view_get_attachments (MODEST_MSG_VIEW (priv->msg_view));
2450 return selected_attachments;
2454 ModestMsgViewWindow *self;
2456 } DecodeAsyncHelper;
2459 on_decode_to_stream_async_handler (TnyMimePart *mime_part,
2465 DecodeAsyncHelper *helper = (DecodeAsyncHelper *) user_data;
2467 /* It could happen that the window was closed */
2468 if (GTK_WIDGET_VISIBLE (helper->self))
2469 set_progress_hint (helper->self, FALSE);
2471 if (cancelled || err) {
2473 gchar *msg = g_strdup_printf (_KR("cerm_device_memory_full"), "");
2474 modest_platform_information_banner (NULL, NULL, msg);
2480 /* make the file read-only */
2481 g_chmod(helper->file_path, 0444);
2483 /* Activate the file */
2484 modest_platform_activate_file (helper->file_path, tny_mime_part_get_content_type (mime_part));
2488 g_object_unref (helper->self);
2489 g_free (helper->file_path);
2490 g_slice_free (DecodeAsyncHelper, helper);
2494 modest_msg_view_window_view_attachment (ModestMsgViewWindow *window,
2495 TnyMimePart *mime_part)
2497 ModestMsgViewWindowPrivate *priv;
2498 const gchar *msg_uid;
2499 gchar *attachment_uid = NULL;
2500 gint attachment_index = 0;
2501 TnyList *attachments;
2503 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window));
2504 g_return_if_fail (TNY_IS_MIME_PART (mime_part) || (mime_part == NULL));
2505 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
2507 msg_uid = modest_msg_view_window_get_message_uid (MODEST_MSG_VIEW_WINDOW (window));
2508 attachments = modest_msg_view_get_attachments (MODEST_MSG_VIEW (priv->msg_view));
2509 attachment_index = modest_list_index (attachments, (GObject *) mime_part);
2510 g_object_unref (attachments);
2512 if (msg_uid && attachment_index >= 0) {
2513 attachment_uid = g_strdup_printf ("%s/%d", msg_uid, attachment_index);
2516 if (mime_part == NULL) {
2517 gboolean error = FALSE;
2518 TnyList *selected_attachments = modest_msg_view_get_selected_attachments (MODEST_MSG_VIEW (priv->msg_view));
2519 if (selected_attachments == NULL || tny_list_get_length (selected_attachments) == 0) {
2521 } else if (tny_list_get_length (selected_attachments) > 1) {
2522 hildon_banner_show_information (NULL, NULL, _("mcen_ib_unable_to_display_more"));
2526 iter = tny_list_create_iterator (selected_attachments);
2527 mime_part = (TnyMimePart *) tny_iterator_get_current (iter);
2528 g_object_unref (iter);
2530 if (selected_attachments)
2531 g_object_unref (selected_attachments);
2536 g_object_ref (mime_part);
2539 if (tny_mime_part_is_purged (mime_part))
2542 if (!modest_tny_mime_part_is_msg (mime_part) && tny_mime_part_get_filename (mime_part)) {
2543 gchar *filepath = NULL;
2544 const gchar *att_filename = tny_mime_part_get_filename (mime_part);
2545 gboolean show_error_banner = FALSE;
2546 TnyFsStream *temp_stream = NULL;
2547 temp_stream = modest_utils_create_temp_stream (att_filename, attachment_uid,
2550 if (temp_stream != NULL) {
2551 DecodeAsyncHelper *helper;
2553 /* Activate progress hint */
2554 set_progress_hint (window, TRUE);
2556 helper = g_slice_new0 (DecodeAsyncHelper);
2557 helper->self = g_object_ref (window);
2558 helper->file_path = g_strdup (filepath);
2560 tny_mime_part_decode_to_stream_async (mime_part, TNY_STREAM (temp_stream),
2561 on_decode_to_stream_async_handler,
2564 g_object_unref (temp_stream);
2565 /* NOTE: files in the temporary area will be automatically
2566 * cleaned after some time if they are no longer in use */
2569 const gchar *content_type;
2570 /* the file may already exist but it isn't writable,
2571 * let's try to open it anyway */
2572 content_type = tny_mime_part_get_content_type (mime_part);
2573 modest_platform_activate_file (filepath, content_type);
2575 g_warning ("%s: modest_utils_create_temp_stream failed", __FUNCTION__);
2576 show_error_banner = TRUE;
2581 if (show_error_banner)
2582 modest_platform_information_banner (NULL, NULL, _("mail_ib_file_operation_failed"));
2583 } else if (!modest_tny_mime_part_is_msg (mime_part)) {
2584 ModestWindowMgr *mgr;
2585 ModestWindow *msg_win = NULL;
2586 TnyMsg *current_msg;
2590 current_msg = modest_msg_view_window_get_message (MODEST_MSG_VIEW_WINDOW (window));
2591 mgr = modest_runtime_get_window_mgr ();
2592 header = tny_msg_get_header (TNY_MSG (current_msg));
2593 found = modest_window_mgr_find_registered_message_uid (mgr,
2598 g_debug ("window for this body is already being created");
2601 /* it's not found, so create a new window for it */
2602 modest_window_mgr_register_header (mgr, header, attachment_uid); /* register the uid before building the window */
2603 gchar *account = g_strdup (modest_window_get_active_account (MODEST_WINDOW (window)));
2604 const gchar *mailbox = modest_window_get_active_mailbox (MODEST_WINDOW (window));
2606 account = modest_account_mgr_get_default_account (modest_runtime_get_account_mgr ());
2608 msg_win = modest_msg_view_window_new_with_other_body (TNY_MSG (current_msg), TNY_MIME_PART (mime_part),
2609 account, mailbox, attachment_uid);
2611 modest_window_set_zoom (MODEST_WINDOW (msg_win),
2612 modest_window_get_zoom (MODEST_WINDOW (window)));
2613 if (modest_window_mgr_register_window (mgr, msg_win, MODEST_WINDOW (window)))
2614 gtk_widget_show_all (GTK_WIDGET (msg_win));
2616 gtk_widget_destroy (GTK_WIDGET (msg_win));
2618 g_object_unref (current_msg);
2620 /* message attachment */
2621 TnyHeader *header = NULL;
2622 ModestWindowMgr *mgr;
2623 ModestWindow *msg_win = NULL;
2626 header = tny_msg_get_header (TNY_MSG (mime_part));
2627 mgr = modest_runtime_get_window_mgr ();
2628 found = modest_window_mgr_find_registered_header (mgr, header, &msg_win);
2631 /* if it's found, but there is no msg_win, it's probably in the process of being created;
2632 * thus, we don't do anything */
2633 g_debug ("window for is already being created");
2635 /* it's not found, so create a new window for it */
2636 modest_window_mgr_register_header (mgr, header, attachment_uid); /* register the uid before building the window */
2637 gchar *account = g_strdup (modest_window_get_active_account (MODEST_WINDOW (window)));
2638 const gchar *mailbox = modest_window_get_active_mailbox (MODEST_WINDOW (window));
2640 account = modest_account_mgr_get_default_account (modest_runtime_get_account_mgr ());
2641 msg_win = modest_msg_view_window_new_for_attachment (TNY_MSG (mime_part), account,
2642 mailbox, attachment_uid);
2643 modest_window_set_zoom (MODEST_WINDOW (msg_win),
2644 modest_window_get_zoom (MODEST_WINDOW (window)));
2645 if (modest_window_mgr_register_window (mgr, msg_win, MODEST_WINDOW (window)))
2646 gtk_widget_show_all (GTK_WIDGET (msg_win));
2648 gtk_widget_destroy (GTK_WIDGET (msg_win));
2654 g_free (attachment_uid);
2656 g_object_unref (mime_part);
2668 GnomeVFSResult result;
2671 static void save_mime_part_info_free (SaveMimePartInfo *info, gboolean with_struct);
2672 static gboolean idle_save_mime_part_show_result (SaveMimePartInfo *info);
2673 static gpointer save_mime_part_to_file (SaveMimePartInfo *info);
2674 static void save_mime_parts_to_file_with_checks (GtkWindow *parent, SaveMimePartInfo *info);
2677 save_mime_part_info_free (SaveMimePartInfo *info, gboolean with_struct)
2681 for (node = info->pairs; node != NULL; node = g_list_next (node)) {
2682 SaveMimePartPair *pair = (SaveMimePartPair *) node->data;
2683 g_free (pair->filename);
2684 g_object_unref (pair->part);
2685 g_slice_free (SaveMimePartPair, pair);
2687 g_list_free (info->pairs);
2690 g_slice_free (SaveMimePartInfo, info);
2695 idle_save_mime_part_show_result (SaveMimePartInfo *info)
2697 /* This is a GDK lock because we are an idle callback and
2698 * hildon_banner_show_information is or does Gtk+ code */
2700 gdk_threads_enter (); /* CHECKED */
2701 if (info->result == GNOME_VFS_OK) {
2702 hildon_banner_show_information (NULL, NULL, _CS("sfil_ib_saved"));
2703 } else if (info->result == GNOME_VFS_ERROR_NO_SPACE) {
2704 gchar *msg = g_strdup_printf (_KR("cerm_device_memory_full"), "");
2705 modest_platform_information_banner (NULL, NULL, msg);
2708 hildon_banner_show_information (NULL, NULL, _("mail_ib_file_operation_failed"));
2710 save_mime_part_info_free (info, FALSE);
2711 gdk_threads_leave (); /* CHECKED */
2717 save_mime_part_to_file (SaveMimePartInfo *info)
2719 GnomeVFSHandle *handle;
2721 SaveMimePartPair *pair = (SaveMimePartPair *) info->pairs->data;
2723 info->result = gnome_vfs_create (&handle, pair->filename, GNOME_VFS_OPEN_WRITE, FALSE, 0644);
2724 if (info->result == GNOME_VFS_OK) {
2725 GError *error = NULL;
2726 stream = tny_vfs_stream_new (handle);
2727 if (tny_mime_part_decode_to_stream (pair->part, stream, &error) < 0) {
2728 g_warning ("modest: could not save attachment %s: %d (%s)\n", pair->filename, error?error->code:-1, error?error->message:"Unknown error");
2730 if ((error->domain == TNY_ERROR_DOMAIN) &&
2731 (error->code == TNY_IO_ERROR_WRITE) &&
2732 (errno == ENOSPC)) {
2733 info->result = GNOME_VFS_ERROR_NO_SPACE;
2735 info->result = GNOME_VFS_ERROR_IO;
2738 g_object_unref (G_OBJECT (stream));
2740 g_warning ("Could not create save attachment %s: %s\n",
2741 pair->filename, gnome_vfs_result_to_string (info->result));
2744 /* Go on saving remaining files */
2745 info->pairs = g_list_remove_link (info->pairs, info->pairs);
2746 if (info->pairs != NULL) {
2747 save_mime_part_to_file (info);
2749 g_idle_add ((GSourceFunc) idle_save_mime_part_show_result, info);
2756 save_mime_parts_to_file_with_checks (GtkWindow *parent,
2757 SaveMimePartInfo *info)
2759 gboolean is_ok = TRUE;
2760 gint replaced_files = 0;
2761 const GList *files = info->pairs;
2762 const GList *iter, *to_replace = NULL;
2764 for (iter = files; (iter != NULL) && (replaced_files < 2); iter = g_list_next(iter)) {
2765 SaveMimePartPair *pair = iter->data;
2766 if (modest_utils_file_exists (pair->filename)) {
2768 if (replaced_files == 1)
2772 if (replaced_files) {
2775 if (replaced_files == 1) {
2776 SaveMimePartPair *pair = to_replace->data;
2777 const gchar *basename = strrchr (pair->filename, G_DIR_SEPARATOR) + 1;
2778 gchar *escaped_basename, *message;
2780 escaped_basename = g_uri_unescape_string (basename, NULL);
2781 message = g_strdup_printf ("%s\n%s",
2782 _FM("docm_nc_replace_file"),
2783 (escaped_basename) ? escaped_basename : "");
2784 response = modest_platform_run_confirmation_dialog (parent, message);
2786 g_free (escaped_basename);
2788 response = modest_platform_run_confirmation_dialog (parent,
2789 _FM("docm_nc_replace_multiple"));
2791 if (response != GTK_RESPONSE_OK)
2796 save_mime_part_info_free (info, TRUE);
2798 g_thread_create ((GThreadFunc)save_mime_part_to_file, info, FALSE, NULL);
2804 save_attachments_response (GtkDialog *dialog,
2808 TnyList *mime_parts;
2810 GList *files_to_save = NULL;
2811 gchar *current_folder;
2813 mime_parts = TNY_LIST (user_data);
2815 if (arg1 != GTK_RESPONSE_OK)
2818 chooser_uri = gtk_file_chooser_get_uri (GTK_FILE_CHOOSER (dialog));
2819 current_folder = gtk_file_chooser_get_current_folder_uri (GTK_FILE_CHOOSER (dialog));
2820 if (current_folder && current_folder != '\0') {
2822 modest_conf_set_string (modest_runtime_get_conf (), MODEST_CONF_LATEST_SAVE_ATTACHMENT_PATH,
2823 current_folder,&err);
2825 g_debug ("Error storing latest used folder: %s", err->message);
2829 g_free (current_folder);
2831 if (!modest_utils_folder_writable (chooser_uri)) {
2832 hildon_banner_show_information
2833 (NULL, NULL, _FM("sfil_ib_readonly_location"));
2837 iter = tny_list_create_iterator (mime_parts);
2838 while (!tny_iterator_is_done (iter)) {
2839 TnyMimePart *mime_part = (TnyMimePart *) tny_iterator_get_current (iter);
2841 if ((modest_tny_mime_part_is_attachment_for_modest (mime_part)) &&
2842 !tny_mime_part_is_purged (mime_part) &&
2843 (tny_mime_part_get_filename (mime_part) != NULL)) {
2844 SaveMimePartPair *pair;
2846 pair = g_slice_new0 (SaveMimePartPair);
2848 if (tny_list_get_length (mime_parts) > 1) {
2850 gnome_vfs_escape_slashes (tny_mime_part_get_filename (mime_part));
2851 pair->filename = g_build_filename (chooser_uri, escaped, NULL);
2854 pair->filename = g_strdup (chooser_uri);
2856 pair->part = mime_part;
2857 files_to_save = g_list_prepend (files_to_save, pair);
2859 tny_iterator_next (iter);
2861 g_object_unref (iter);
2863 g_free (chooser_uri);
2865 if (files_to_save != NULL) {
2866 SaveMimePartInfo *info = g_slice_new0 (SaveMimePartInfo);
2867 info->pairs = files_to_save;
2868 info->result = TRUE;
2869 save_mime_parts_to_file_with_checks ((GtkWindow *) dialog, info);
2873 /* Free and close the dialog */
2874 g_object_unref (mime_parts);
2875 gtk_widget_destroy (GTK_WIDGET (dialog));
2879 modest_msg_view_window_save_attachments (ModestMsgViewWindow *window,
2880 TnyList *mime_parts)
2882 ModestMsgViewWindowPrivate *priv;
2883 GtkWidget *save_dialog = NULL;
2884 gchar *conf_folder = NULL;
2885 gchar *filename = NULL;
2886 gchar *save_multiple_str = NULL;
2888 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window));
2889 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
2891 if (mime_parts == NULL) {
2892 /* In Hildon 2.2 save and delete operate over all the attachments as there's no
2893 * selection available */
2894 mime_parts = modest_msg_view_get_attachments (MODEST_MSG_VIEW (priv->msg_view));
2895 if (mime_parts && !modest_maemo_utils_select_attachments (GTK_WINDOW (window), mime_parts, FALSE)) {
2896 g_object_unref (mime_parts);
2899 if (mime_parts == NULL || tny_list_get_length (mime_parts) == 0) {
2901 g_object_unref (mime_parts);
2907 g_object_ref (mime_parts);
2910 /* prepare dialog */
2911 if (tny_list_get_length (mime_parts) == 1) {
2913 /* only one attachment selected */
2914 iter = tny_list_create_iterator (mime_parts);
2915 TnyMimePart *mime_part = (TnyMimePart *) tny_iterator_get_current (iter);
2916 g_object_unref (iter);
2917 if (!modest_tny_mime_part_is_msg (mime_part) &&
2918 modest_tny_mime_part_is_attachment_for_modest (mime_part) &&
2919 !tny_mime_part_is_purged (mime_part)) {
2920 filename = g_strdup (tny_mime_part_get_filename (mime_part));
2922 /* TODO: show any error? */
2923 g_warning ("%s: Tried to save a non-file attachment", __FUNCTION__);
2924 g_object_unref (mime_parts);
2927 g_object_unref (mime_part);
2929 gint num = tny_list_get_length (mime_parts);
2930 save_multiple_str = g_strdup_printf (dngettext("hildon-fm",
2931 "sfil_va_number_of_objects_attachment",
2932 "sfil_va_number_of_objects_attachments",
2936 save_dialog = hildon_file_chooser_dialog_new (GTK_WINDOW (window),
2937 GTK_FILE_CHOOSER_ACTION_SAVE);
2940 conf_folder = modest_conf_get_string (modest_runtime_get_conf (), MODEST_CONF_LATEST_SAVE_ATTACHMENT_PATH, NULL);
2941 if (conf_folder && conf_folder[0] != '\0') {
2942 gtk_file_chooser_set_current_folder_uri (GTK_FILE_CHOOSER (save_dialog), conf_folder);
2945 /* Set the default folder to images folder */
2946 docs_folder = g_build_filename (g_getenv (MYDOCS_ENV), DOCS_FOLDER, NULL);
2947 gtk_file_chooser_set_current_folder (GTK_FILE_CHOOSER (save_dialog), docs_folder);
2948 g_free (docs_folder);
2950 g_free (conf_folder);
2954 gtk_file_chooser_set_current_name (GTK_FILE_CHOOSER (save_dialog),
2959 /* if multiple, set multiple string */
2960 if (save_multiple_str) {
2961 g_object_set (G_OBJECT (save_dialog), "save-multiple", save_multiple_str, NULL);
2962 gtk_window_set_title (GTK_WINDOW (save_dialog), _FM("sfil_ti_save_objects_files"));
2965 /* We must run this asynchronously, because the hildon dialog
2966 performs a gtk_dialog_run by itself which leads to gdk
2968 g_signal_connect (save_dialog, "response",
2969 G_CALLBACK (save_attachments_response), mime_parts);
2971 gtk_widget_show_all (save_dialog);
2975 show_remove_attachment_information (gpointer userdata)
2977 ModestMsgViewWindow *window = (ModestMsgViewWindow *) userdata;
2978 ModestMsgViewWindowPrivate *priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
2980 /* We're outside the main lock */
2981 gdk_threads_enter ();
2983 if (priv->remove_attachment_banner != NULL) {
2984 gtk_widget_destroy (priv->remove_attachment_banner);
2985 g_object_unref (priv->remove_attachment_banner);
2988 priv->remove_attachment_banner = g_object_ref (
2989 hildon_banner_show_animation (NULL, NULL, _("mcen_me_inbox_remove_attachments")));
2991 gdk_threads_leave ();
2997 modest_msg_view_window_remove_attachments (ModestMsgViewWindow *window, gboolean get_all)
2999 ModestMsgViewWindowPrivate *priv;
3000 TnyList *mime_parts = NULL, *tmp;
3001 gchar *confirmation_message;
3007 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window));
3008 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
3010 /* In hildon 2.2 we ignore the get_all flag as we always get all attachments. This is
3011 * because we don't have selection
3013 mime_parts = modest_msg_view_get_attachments (MODEST_MSG_VIEW (priv->msg_view));
3015 /* Remove already purged messages from mime parts list. We use
3016 a copy of the list to remove items in the original one */
3017 tmp = tny_list_copy (mime_parts);
3018 iter = tny_list_create_iterator (tmp);
3019 while (!tny_iterator_is_done (iter)) {
3020 TnyMimePart *part = TNY_MIME_PART (tny_iterator_get_current (iter));
3021 if (tny_mime_part_is_purged (part))
3022 tny_list_remove (mime_parts, (GObject *) part);
3024 g_object_unref (part);
3025 tny_iterator_next (iter);
3027 g_object_unref (tmp);
3028 g_object_unref (iter);
3030 if (!modest_maemo_utils_select_attachments (GTK_WINDOW (window), mime_parts, TRUE) ||
3031 tny_list_get_length (mime_parts) == 0) {
3032 g_object_unref (mime_parts);
3036 n_attachments = tny_list_get_length (mime_parts);
3037 if (n_attachments == 1) {
3041 iter = tny_list_create_iterator (mime_parts);
3042 part = (TnyMimePart *) tny_iterator_get_current (iter);
3043 g_object_unref (iter);
3044 if (modest_tny_mime_part_is_msg (part)) {
3046 header = tny_msg_get_header (TNY_MSG (part));
3047 filename = tny_header_dup_subject (header);
3048 g_object_unref (header);
3049 if (filename == NULL)
3050 filename = g_strdup (_("mail_va_no_subject"));
3052 filename = g_strdup (tny_mime_part_get_filename (TNY_MIME_PART (part)));
3054 confirmation_message = g_strdup_printf (_("mcen_nc_purge_file_text"), filename);
3056 g_object_unref (part);
3058 confirmation_message = g_strdup_printf (ngettext("mcen_nc_purge_file_text",
3059 "mcen_nc_purge_files_text",
3060 n_attachments), n_attachments);
3062 response = modest_platform_run_confirmation_dialog (GTK_WINDOW (window),
3063 confirmation_message);
3064 g_free (confirmation_message);
3066 if (response != GTK_RESPONSE_OK) {
3067 g_object_unref (mime_parts);
3071 priv->purge_timeout = g_timeout_add (2000, show_remove_attachment_information, window);
3073 iter = tny_list_create_iterator (mime_parts);
3074 while (!tny_iterator_is_done (iter)) {
3077 part = (TnyMimePart *) tny_iterator_get_current (iter);
3078 tny_mime_part_set_purged (TNY_MIME_PART (part));
3079 g_object_unref (part);
3080 tny_iterator_next (iter);
3082 g_object_unref (iter);
3084 msg = tny_msg_view_get_msg (TNY_MSG_VIEW (priv->msg_view));
3085 tny_msg_view_clear (TNY_MSG_VIEW (priv->msg_view));
3086 tny_msg_rewrite_cache (msg);
3087 tny_msg_view_set_msg (TNY_MSG_VIEW (priv->msg_view), msg);
3088 g_object_unref (msg);
3089 update_branding (MODEST_MSG_VIEW_WINDOW (window));
3091 g_object_unref (mime_parts);
3093 if (priv->purge_timeout > 0) {
3094 g_source_remove (priv->purge_timeout);
3095 priv->purge_timeout = 0;
3098 if (priv->remove_attachment_banner) {
3099 gtk_widget_destroy (priv->remove_attachment_banner);
3100 g_object_unref (priv->remove_attachment_banner);
3101 priv->remove_attachment_banner = NULL;
3107 update_window_title (ModestMsgViewWindow *window)
3109 ModestMsgViewWindowPrivate *priv;
3111 TnyHeader *header = NULL;
3112 gchar *subject = NULL;
3114 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
3116 /* Note that if the window is closed while we're retrieving
3117 the message, this widget could de deleted */
3118 if (!priv->msg_view)
3121 msg = tny_msg_view_get_msg (TNY_MSG_VIEW (priv->msg_view));
3123 if (priv->other_body) {
3126 description = modest_tny_mime_part_get_header_value (priv->other_body, "Content-Description");
3128 g_strstrip (description);
3129 subject = description;
3131 } else if (msg != NULL) {
3132 header = tny_msg_get_header (msg);
3133 subject = tny_header_dup_subject (header);
3134 g_object_unref (header);
3135 g_object_unref (msg);
3138 if ((subject == NULL)||(subject[0] == '\0')) {
3140 subject = g_strdup (_("mail_va_no_subject"));
3143 gtk_window_set_title (GTK_WINDOW (window), subject);
3148 on_move_focus (GtkWidget *widget,
3149 GtkDirectionType direction,
3152 g_signal_stop_emission_by_name (G_OBJECT (widget), "move-focus");
3156 fetch_image_open_stream (TnyStreamCache *self, gint64 *expected_size, gchar *uri)
3158 GnomeVFSResult result;
3159 GnomeVFSHandle *handle = NULL;
3160 GnomeVFSFileInfo *info = NULL;
3163 result = gnome_vfs_open (&handle, uri, GNOME_VFS_OPEN_READ);
3164 if (result != GNOME_VFS_OK) {
3169 info = gnome_vfs_file_info_new ();
3170 result = gnome_vfs_get_file_info_from_handle (handle, info, GNOME_VFS_FILE_INFO_DEFAULT);
3171 if (result != GNOME_VFS_OK || ! (info->valid_fields & GNOME_VFS_FILE_INFO_FIELDS_SIZE)) {
3172 /* We put a "safe" default size for going to cache */
3173 *expected_size = (300*1024);
3175 *expected_size = info->size;
3177 gnome_vfs_file_info_unref (info);
3179 stream = tny_vfs_stream_new (handle);
3188 TnyStream *output_stream;
3189 GtkWidget *msg_view;
3194 on_fetch_image_idle_refresh_view (gpointer userdata)
3197 FetchImageData *fidata = (FetchImageData *) userdata;
3199 gdk_threads_enter ();
3200 if (GTK_WIDGET_DRAWABLE (fidata->msg_view)) {
3201 ModestMsgViewWindowPrivate *priv;
3203 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (fidata->window);
3204 priv->fetching_images--;
3205 gtk_widget_queue_draw (fidata->msg_view);
3206 update_progress_hint (MODEST_MSG_VIEW_WINDOW (fidata->window));
3208 gdk_threads_leave ();
3210 g_object_unref (fidata->msg_view);
3211 g_object_unref (fidata->window);
3212 g_slice_free (FetchImageData, fidata);
3217 on_fetch_image_thread (gpointer userdata)
3219 FetchImageData *fidata = (FetchImageData *) userdata;
3220 TnyStreamCache *cache;
3221 TnyStream *cache_stream;
3223 cache = modest_runtime_get_images_cache ();
3225 tny_stream_cache_get_stream (cache,
3227 (TnyStreamCacheOpenStreamFetcher) fetch_image_open_stream,
3228 (gpointer) fidata->uri);
3229 g_free (fidata->cache_id);
3230 g_free (fidata->uri);
3232 if (cache_stream != NULL) {
3235 while (G_LIKELY (!tny_stream_is_eos (cache_stream))) {
3238 nb_read = tny_stream_read (cache_stream, buffer, sizeof (buffer));
3239 if (G_UNLIKELY (nb_read < 0)) {
3241 } else if (G_LIKELY (nb_read > 0)) {
3242 gssize nb_written = 0;
3244 while (G_UNLIKELY (nb_written < nb_read)) {
3247 len = tny_stream_write (fidata->output_stream, buffer + nb_written,
3248 nb_read - nb_written);
3249 if (G_UNLIKELY (len < 0))
3255 tny_stream_close (cache_stream);
3256 g_object_unref (cache_stream);
3259 tny_stream_close (fidata->output_stream);
3260 g_object_unref (fidata->output_stream);
3262 g_idle_add (on_fetch_image_idle_refresh_view, fidata);
3268 on_fetch_image (ModestMsgView *msgview,
3271 ModestMsgViewWindow *window)
3273 const gchar *current_account;
3274 ModestMsgViewWindowPrivate *priv;
3275 FetchImageData *fidata;
3277 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
3279 current_account = modest_window_get_active_account (MODEST_WINDOW (window));
3281 fidata = g_slice_new0 (FetchImageData);
3282 fidata->msg_view = g_object_ref (msgview);
3283 fidata->window = g_object_ref (window);
3284 fidata->uri = g_strdup (uri);
3285 fidata->cache_id = modest_images_cache_get_id (current_account, uri);
3286 fidata->output_stream = g_object_ref (stream);
3288 priv->fetching_images++;
3289 if (g_thread_create (on_fetch_image_thread, fidata, FALSE, NULL) == NULL) {
3290 g_object_unref (fidata->output_stream);
3291 g_free (fidata->cache_id);
3292 g_free (fidata->uri);
3293 g_object_unref (fidata->msg_view);
3294 g_slice_free (FetchImageData, fidata);
3295 tny_stream_close (stream);
3296 priv->fetching_images--;
3297 update_progress_hint (window);
3300 update_progress_hint (window);
3306 setup_menu (ModestMsgViewWindow *self)
3308 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW(self));
3310 /* Settings menu buttons */
3311 modest_hildon2_window_add_to_menu (MODEST_HILDON2_WINDOW (self), _("mcen_me_inbox_replytoall"), NULL,
3312 APP_MENU_CALLBACK (modest_ui_actions_on_reply_all),
3313 MODEST_DIMMING_CALLBACK (modest_ui_dimming_rules_on_reply_msg));
3314 modest_hildon2_window_add_to_menu (MODEST_HILDON2_WINDOW (self), _("mcen_me_inbox_forward"), "<Control>d",
3315 APP_MENU_CALLBACK (modest_ui_actions_on_forward),
3316 MODEST_DIMMING_CALLBACK (modest_ui_dimming_rules_on_reply_msg));
3318 modest_hildon2_window_add_to_menu (MODEST_HILDON2_WINDOW (self), _("mcen_me_inbox_mark_as_read"), NULL,
3319 APP_MENU_CALLBACK (modest_ui_actions_on_mark_as_read),
3320 MODEST_DIMMING_CALLBACK (modest_ui_dimming_rules_on_mark_as_read_msg_in_view));
3321 modest_hildon2_window_add_to_menu (MODEST_HILDON2_WINDOW (self), _("mcen_me_inbox_mark_as_unread"), NULL,
3322 APP_MENU_CALLBACK (modest_ui_actions_on_mark_as_unread),
3323 MODEST_DIMMING_CALLBACK (modest_ui_dimming_rules_on_mark_as_unread_msg_in_view));
3325 modest_hildon2_window_add_to_menu (MODEST_HILDON2_WINDOW (self), _("mcen_me_viewer_save_attachments"), NULL,
3326 APP_MENU_CALLBACK (modest_ui_actions_save_attachments),
3327 MODEST_DIMMING_CALLBACK (modest_ui_dimming_rules_on_save_attachments));
3328 modest_hildon2_window_add_to_menu (MODEST_HILDON2_WINDOW (self), _("mcen_me_inbox_remove_attachments"), NULL,
3329 APP_MENU_CALLBACK (modest_ui_actions_remove_attachments),
3330 MODEST_DIMMING_CALLBACK (modest_ui_dimming_rules_on_remove_attachments));
3332 modest_hildon2_window_add_to_menu (MODEST_HILDON2_WINDOW (self), _("mcen_me_new_message"), "<Control>n",
3333 APP_MENU_CALLBACK (modest_ui_actions_on_new_msg),
3334 MODEST_DIMMING_CALLBACK (modest_ui_dimming_rules_on_new_msg));
3335 modest_hildon2_window_add_to_menu (MODEST_HILDON2_WINDOW (self), _("mcen_me_viewer_addtocontacts"), NULL,
3336 APP_MENU_CALLBACK (modest_ui_actions_add_to_contacts),
3337 MODEST_DIMMING_CALLBACK (modest_ui_dimming_rules_on_add_to_contacts));
3339 modest_hildon2_window_add_to_menu (MODEST_HILDON2_WINDOW (self), _("mail_bd_external_images"), NULL,
3340 APP_MENU_CALLBACK (modest_ui_actions_on_fetch_images),
3341 MODEST_DIMMING_CALLBACK (modest_ui_dimming_rules_on_fetch_images));
3342 modest_hildon2_window_add_to_menu (MODEST_HILDON2_WINDOW (self), _("mcen_ti_message_properties"), NULL,
3343 APP_MENU_CALLBACK (modest_ui_actions_on_details),
3344 MODEST_DIMMING_CALLBACK (modest_ui_dimming_rules_on_details));
3348 modest_msg_view_window_add_to_contacts (ModestMsgViewWindow *self)
3350 ModestMsgViewWindowPrivate *priv;
3351 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
3352 GSList *recipients = NULL;
3354 gboolean contacts_to_add = FALSE;
3356 msg = tny_msg_view_get_msg (TNY_MSG_VIEW (priv->msg_view));
3360 header = modest_msg_view_window_get_header (self);
3363 recipients = modest_tny_msg_header_get_all_recipients_list (header);
3364 g_object_unref (header);
3366 recipients = modest_tny_msg_get_all_recipients_list (msg);
3367 g_object_unref (msg);
3370 if (recipients != NULL) {
3371 GtkWidget *picker_dialog;
3372 GtkWidget *selector;
3374 gchar *selected = NULL;
3376 selector = hildon_touch_selector_new_text ();
3377 g_object_ref (selector);
3379 for (node = recipients; node != NULL; node = g_slist_next (node)) {
3380 if (!modest_address_book_has_address ((const gchar *) node->data)) {
3381 hildon_touch_selector_append_text (HILDON_TOUCH_SELECTOR (selector),
3382 (const gchar *) node->data);
3383 contacts_to_add = TRUE;
3387 if (contacts_to_add) {
3390 picker_dialog = hildon_picker_dialog_new (GTK_WINDOW (self));
3391 gtk_window_set_title (GTK_WINDOW (picker_dialog), _("mcen_me_viewer_addtocontacts"));
3393 hildon_picker_dialog_set_selector (HILDON_PICKER_DIALOG (picker_dialog),
3394 HILDON_TOUCH_SELECTOR (selector));
3396 picker_result = gtk_dialog_run (GTK_DIALOG (picker_dialog));
3398 if (picker_result == GTK_RESPONSE_OK) {
3399 selected = hildon_touch_selector_get_current_text (HILDON_TOUCH_SELECTOR (selector));
3401 gtk_widget_destroy (picker_dialog);
3404 modest_address_book_add_address (selected);
3409 g_object_unref (selector);
3414 if (recipients) {g_slist_foreach (recipients, (GFunc) g_free, NULL); g_slist_free (recipients);}
3418 _modest_msg_view_window_map_event (GtkWidget *widget,
3422 ModestMsgViewWindow *self = (ModestMsgViewWindow *) userdata;
3424 update_progress_hint (self);
3430 modest_msg_view_window_fetch_images (ModestMsgViewWindow *self)
3432 ModestMsgViewWindowPrivate *priv;
3433 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
3435 modest_msg_view_request_fetch_images (MODEST_MSG_VIEW (priv->msg_view));
3439 modest_msg_view_window_has_blocked_external_images (ModestMsgViewWindow *self)
3441 ModestMsgViewWindowPrivate *priv;
3442 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
3444 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self), FALSE);
3446 return modest_msg_view_has_blocked_external_images (MODEST_MSG_VIEW (priv->msg_view));
3450 modest_msg_view_window_reload (ModestMsgViewWindow *self)
3452 ModestMsgViewWindowPrivate *priv;
3455 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self));
3457 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
3458 header = modest_msg_view_window_get_header (MODEST_MSG_VIEW_WINDOW (self));
3460 if (!message_reader (self, priv, header, priv->row_reference)) {
3461 g_warning ("Shouldn't happen, trying to reload a message failed");
3464 g_object_unref (header);
3468 update_branding (ModestMsgViewWindow *self)
3470 const gchar *account;
3471 const gchar *mailbox;
3472 ModestAccountMgr *mgr;
3473 ModestProtocol *protocol = NULL;
3474 gchar *service_name = NULL;
3475 const GdkPixbuf *service_icon = NULL;
3476 ModestMsgViewWindowPrivate *priv;
3478 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
3480 account = modest_window_get_active_account (MODEST_WINDOW (self));
3481 mailbox = modest_window_get_active_mailbox (MODEST_WINDOW (self));
3483 mgr = modest_runtime_get_account_mgr ();
3485 if (modest_account_mgr_account_is_multimailbox (mgr, account, &protocol)) {
3486 if (MODEST_IS_ACCOUNT_PROTOCOL (protocol)) {
3487 service_name = modest_account_protocol_get_service_name (MODEST_ACCOUNT_PROTOCOL (protocol),
3489 service_icon = modest_account_protocol_get_service_icon (MODEST_ACCOUNT_PROTOCOL (protocol),
3490 account, mailbox, MODEST_ICON_SIZE_SMALL);
3494 modest_msg_view_set_branding (MODEST_MSG_VIEW (priv->msg_view), service_name, service_icon);
3495 g_free (service_name);