1 /* Copyright (c) 2006, Nokia Corporation
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions are
8 * * Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 * * Redistributions in binary form must reproduce the above copyright
11 * notice, this list of conditions and the following disclaimer in the
12 * documentation and/or other materials provided with the distribution.
13 * * Neither the name of the Nokia Corporation nor the names of its
14 * contributors may be used to endorse or promote products derived from
15 * this software without specific prior written permission.
17 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
18 * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
19 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
20 * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
21 * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
22 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
23 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
24 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
25 * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
26 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
27 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29 #include <glib/gi18n.h>
31 #include <tny-account-store.h>
32 #include <tny-simple-list.h>
34 #include <tny-mime-part.h>
35 #include <tny-vfs-stream.h>
36 #include <tny-error.h>
37 #include "modest-marshal.h"
38 #include "modest-platform.h"
39 #include <modest-utils.h>
40 #include <modest-maemo-utils.h>
41 #include <modest-tny-msg.h>
42 #include <modest-msg-view-window.h>
43 #include <modest-main-window-ui.h>
44 #include "modest-msg-view-window-ui-dimming.h"
45 #include <modest-widget-memory.h>
46 #include <modest-progress-object.h>
47 #include <modest-runtime.h>
48 #include <modest-window-priv.h>
49 #include <modest-tny-folder.h>
50 #include <modest-text-utils.h>
51 #include <modest-account-mgr-helpers.h>
52 #include <hildon/hildon-pannable-area.h>
53 #include <hildon/hildon-picker-dialog.h>
54 #include <hildon/hildon-app-menu.h>
55 #include "modest-defs.h"
56 #include "modest-hildon-includes.h"
57 #include "modest-ui-dimming-manager.h"
58 #include <gdk/gdkkeysyms.h>
59 #include <modest-tny-account.h>
60 #include <modest-mime-part-view.h>
61 #include <modest-isearch-view.h>
62 #include <modest-tny-mime-part.h>
63 #include <modest-address-book.h>
66 #include <glib/gstdio.h>
67 #include <modest-debug.h>
68 #include <modest-header-window.h>
69 #include <modest-account-protocol.h>
71 #define MYDOCS_ENV "MYDOCSDIR"
72 #define DOCS_FOLDER ".documents"
74 typedef struct _ModestMsgViewWindowPrivate ModestMsgViewWindowPrivate;
75 struct _ModestMsgViewWindowPrivate {
78 GtkWidget *main_scroll;
79 GtkWidget *find_toolbar;
82 /* Progress observers */
83 GSList *progress_widgets;
86 GtkWidget *prev_toolitem;
87 GtkWidget *next_toolitem;
88 gboolean progress_hint;
91 /* Optimized view enabled */
92 gboolean optimized_view;
94 /* Whether this was created via the *_new_for_search_result() function. */
95 gboolean is_search_result;
97 /* Whether the message is in outbox */
100 /* A reference to the @model of the header view
101 * to allow selecting previous/next messages,
102 * if the message is currently selected in the header view.
104 const gchar *header_folder_id;
105 GtkTreeModel *header_model;
106 GtkTreeRowReference *row_reference;
107 GtkTreeRowReference *next_row_reference;
109 gulong clipboard_change_handler;
110 gulong queue_change_handler;
111 gulong account_removed_handler;
112 gulong row_changed_handler;
113 gulong row_deleted_handler;
114 gulong row_inserted_handler;
115 gulong rows_reordered_handler;
118 GtkWidget *remove_attachment_banner;
121 TnyMimePart *other_body;
126 static void modest_msg_view_window_class_init (ModestMsgViewWindowClass *klass);
127 static void modest_msg_view_window_init (ModestMsgViewWindow *obj);
128 static void modest_header_view_observer_init (ModestHeaderViewObserverIface *iface_class);
129 static void modest_msg_view_window_finalize (GObject *obj);
130 static void modest_msg_view_window_show_find_toolbar (GtkWidget *obj, gpointer data);
131 static void modest_msg_view_window_find_toolbar_close (GtkWidget *widget,
132 ModestMsgViewWindow *obj);
133 static void modest_msg_view_window_find_toolbar_search (GtkWidget *widget,
134 ModestMsgViewWindow *obj);
135 static void modest_msg_view_window_toggle_find_toolbar (GtkWidget *obj,
137 static void modest_msg_view_window_disconnect_signals (ModestWindow *self);
139 static gdouble modest_msg_view_window_get_zoom (ModestWindow *window);
140 static void modest_msg_view_window_set_zoom (ModestWindow *window,
142 static gboolean modest_msg_view_window_zoom_minus (ModestWindow *window);
143 static gboolean modest_msg_view_window_zoom_plus (ModestWindow *window);
144 static gboolean modest_msg_view_window_key_event (GtkWidget *window,
147 static void modest_msg_view_window_update_priority (ModestMsgViewWindow *window);
149 static void modest_msg_view_window_show_toolbar (ModestWindow *window,
150 gboolean show_toolbar);
152 static void modest_msg_view_window_clipboard_owner_change (GtkClipboard *clipboard,
154 ModestMsgViewWindow *window);
156 static void modest_msg_view_window_on_row_changed (GtkTreeModel *header_model,
159 ModestMsgViewWindow *window);
161 static void modest_msg_view_window_on_row_deleted (GtkTreeModel *header_model,
163 ModestMsgViewWindow *window);
165 static void modest_msg_view_window_on_row_inserted (GtkTreeModel *header_model,
166 GtkTreePath *tree_path,
167 GtkTreeIter *tree_iter,
168 ModestMsgViewWindow *window);
170 static void modest_msg_view_window_on_row_reordered (GtkTreeModel *header_model,
174 ModestMsgViewWindow *window);
176 static void modest_msg_view_window_update_model_replaced (ModestHeaderViewObserver *window,
178 const gchar *tny_folder_id);
180 static void on_queue_changed (ModestMailOperationQueue *queue,
181 ModestMailOperation *mail_op,
182 ModestMailOperationQueueNotification type,
183 ModestMsgViewWindow *self);
185 static void on_account_removed (TnyAccountStore *account_store,
189 static void on_move_focus (GtkWidget *widget,
190 GtkDirectionType direction,
193 static void view_msg_cb (ModestMailOperation *mail_op,
200 static void set_progress_hint (ModestMsgViewWindow *self,
203 static void update_window_title (ModestMsgViewWindow *window);
205 static void init_window (ModestMsgViewWindow *obj);
207 static gboolean msg_is_visible (TnyHeader *header, gboolean check_outbox);
209 static void check_dimming_rules_after_change (ModestMsgViewWindow *window);
211 static gboolean on_fetch_image (ModestMsgView *msgview,
214 ModestMsgViewWindow *window);
216 static gboolean modest_msg_view_window_scroll_child (ModestMsgViewWindow *self,
217 GtkScrollType scroll_type,
220 static gboolean message_reader (ModestMsgViewWindow *window,
221 ModestMsgViewWindowPrivate *priv,
223 const gchar *msg_uid,
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(),
473 update_progress_hint (ModestMsgViewWindow *self)
475 ModestMsgViewWindowPrivate *priv;
476 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(self);
478 if (GTK_WIDGET_VISIBLE (self)) {
479 hildon_gtk_window_set_progress_indicator (GTK_WINDOW (self),
480 (priv->progress_hint || (priv->fetching_images > 0))?1:0);
485 set_progress_hint (ModestMsgViewWindow *self,
488 ModestWindowPrivate *parent_priv;
489 ModestMsgViewWindowPrivate *priv;
491 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self));
493 parent_priv = MODEST_WINDOW_GET_PRIVATE(self);
494 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(self);
496 /* Sets current progress hint */
497 priv->progress_hint = enabled;
499 update_progress_hint (self);
505 init_window (ModestMsgViewWindow *obj)
507 GtkWidget *main_vbox;
508 ModestMsgViewWindowPrivate *priv;
510 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(obj);
512 priv->msg_view = GTK_WIDGET (tny_platform_factory_new_msg_view (modest_tny_platform_factory_get_instance ()));
513 modest_msg_view_set_shadow_type (MODEST_MSG_VIEW (priv->msg_view), GTK_SHADOW_NONE);
514 main_vbox = gtk_vbox_new (FALSE, 6);
515 priv->main_scroll = hildon_pannable_area_new ();
516 gtk_container_add (GTK_CONTAINER (priv->main_scroll), priv->msg_view);
517 gtk_box_pack_start (GTK_BOX(main_vbox), priv->main_scroll, TRUE, TRUE, 0);
518 gtk_container_add (GTK_CONTAINER(obj), main_vbox);
520 /* NULL-ize fields if the window is destroyed */
521 g_signal_connect (priv->msg_view, "destroy", G_CALLBACK (gtk_widget_destroyed), &(priv->msg_view));
523 gtk_widget_show_all (GTK_WIDGET(main_vbox));
527 modest_msg_view_window_disconnect_signals (ModestWindow *self)
529 ModestMsgViewWindowPrivate *priv;
530 GtkWidget *header_view = NULL;
531 GtkWindow *parent_window = NULL;
533 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
535 if (gtk_clipboard_get (GDK_SELECTION_PRIMARY) &&
536 g_signal_handler_is_connected (gtk_clipboard_get (GDK_SELECTION_PRIMARY),
537 priv->clipboard_change_handler))
538 g_signal_handler_disconnect (gtk_clipboard_get (GDK_SELECTION_PRIMARY),
539 priv->clipboard_change_handler);
541 if (g_signal_handler_is_connected (G_OBJECT (modest_runtime_get_mail_operation_queue ()),
542 priv->queue_change_handler))
543 g_signal_handler_disconnect (G_OBJECT (modest_runtime_get_mail_operation_queue ()),
544 priv->queue_change_handler);
546 if (g_signal_handler_is_connected (G_OBJECT (modest_runtime_get_account_store ()),
547 priv->account_removed_handler))
548 g_signal_handler_disconnect (G_OBJECT (modest_runtime_get_account_store ()),
549 priv->account_removed_handler);
551 if (priv->header_model) {
552 if (g_signal_handler_is_connected(G_OBJECT (priv->header_model),
553 priv->row_changed_handler))
554 g_signal_handler_disconnect(G_OBJECT (priv->header_model),
555 priv->row_changed_handler);
557 if (g_signal_handler_is_connected(G_OBJECT (priv->header_model),
558 priv->row_deleted_handler))
559 g_signal_handler_disconnect(G_OBJECT (priv->header_model),
560 priv->row_deleted_handler);
562 if (g_signal_handler_is_connected(G_OBJECT (priv->header_model),
563 priv->row_inserted_handler))
564 g_signal_handler_disconnect(G_OBJECT (priv->header_model),
565 priv->row_inserted_handler);
567 if (g_signal_handler_is_connected(G_OBJECT (priv->header_model),
568 priv->rows_reordered_handler))
569 g_signal_handler_disconnect(G_OBJECT (priv->header_model),
570 priv->rows_reordered_handler);
573 modest_signal_mgr_disconnect_all_and_destroy (priv->sighandlers);
574 priv->sighandlers = NULL;
576 parent_window = gtk_window_get_transient_for (GTK_WINDOW (self));
577 if (parent_window && MODEST_IS_HEADER_WINDOW (parent_window)) {
578 header_view = GTK_WIDGET (modest_header_window_get_header_view (MODEST_HEADER_WINDOW (parent_window)));
580 modest_header_view_remove_observer(MODEST_HEADER_VIEW (header_view),
581 MODEST_HEADER_VIEW_OBSERVER(self));
587 modest_msg_view_window_finalize (GObject *obj)
589 ModestMsgViewWindowPrivate *priv;
591 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (obj);
593 /* Sanity check: shouldn't be needed, the window mgr should
594 call this function before */
595 modest_msg_view_window_disconnect_signals (MODEST_WINDOW (obj));
597 if (priv->other_body != NULL) {
598 g_object_unref (priv->other_body);
599 priv->other_body = NULL;
602 if (priv->header_model != NULL) {
603 g_object_unref (priv->header_model);
604 priv->header_model = NULL;
607 if (priv->remove_attachment_banner) {
608 gtk_widget_destroy (priv->remove_attachment_banner);
609 g_object_unref (priv->remove_attachment_banner);
610 priv->remove_attachment_banner = NULL;
613 if (priv->purge_timeout > 0) {
614 g_source_remove (priv->purge_timeout);
615 priv->purge_timeout = 0;
618 if (priv->row_reference) {
619 gtk_tree_row_reference_free (priv->row_reference);
620 priv->row_reference = NULL;
623 if (priv->next_row_reference) {
624 gtk_tree_row_reference_free (priv->next_row_reference);
625 priv->next_row_reference = NULL;
629 g_free (priv->msg_uid);
630 priv->msg_uid = NULL;
633 G_OBJECT_CLASS(parent_class)->finalize (obj);
637 select_next_valid_row (GtkTreeModel *model,
638 GtkTreeRowReference **row_reference,
642 GtkTreeIter tmp_iter;
644 GtkTreePath *next = NULL;
645 gboolean retval = FALSE, finished;
647 g_return_val_if_fail (gtk_tree_row_reference_valid (*row_reference), FALSE);
649 path = gtk_tree_row_reference_get_path (*row_reference);
650 gtk_tree_model_get_iter (model, &tmp_iter, path);
651 gtk_tree_row_reference_free (*row_reference);
652 *row_reference = NULL;
656 TnyHeader *header = NULL;
658 if (gtk_tree_model_iter_next (model, &tmp_iter)) {
659 gtk_tree_model_get (model, &tmp_iter,
660 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
664 if (msg_is_visible (header, is_outbox)) {
665 next = gtk_tree_model_get_path (model, &tmp_iter);
666 *row_reference = gtk_tree_row_reference_new (model, next);
667 gtk_tree_path_free (next);
671 g_object_unref (header);
674 } else if (cycle && gtk_tree_model_get_iter_first (model, &tmp_iter)) {
675 next = gtk_tree_model_get_path (model, &tmp_iter);
677 /* Ensure that we are not selecting the same */
678 if (gtk_tree_path_compare (path, next) != 0) {
679 gtk_tree_model_get (model, &tmp_iter,
680 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
683 if (msg_is_visible (header, is_outbox)) {
684 *row_reference = gtk_tree_row_reference_new (model, next);
688 g_object_unref (header);
692 /* If we ended up in the same message
693 then there is no valid next
697 gtk_tree_path_free (next);
699 /* If there are no more messages and we don't
700 want to start again in the first one then
701 there is no valid next message */
707 gtk_tree_path_free (path);
712 /* TODO: This should be in _init(), with the parameters as properties. */
714 modest_msg_view_window_construct (ModestMsgViewWindow *self,
715 const gchar *modest_account_name,
716 const gchar *mailbox,
717 const gchar *msg_uid)
720 ModestMsgViewWindowPrivate *priv = NULL;
721 ModestWindowPrivate *parent_priv = NULL;
722 ModestDimmingRulesGroup *toolbar_rules_group = NULL;
723 ModestDimmingRulesGroup *clipboard_rules_group = NULL;
725 obj = G_OBJECT (self);
726 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(obj);
727 parent_priv = MODEST_WINDOW_GET_PRIVATE(obj);
729 priv->msg_uid = g_strdup (msg_uid);
732 parent_priv->menubar = NULL;
734 toolbar_rules_group = modest_dimming_rules_group_new (MODEST_DIMMING_RULES_TOOLBAR, TRUE);
735 clipboard_rules_group = modest_dimming_rules_group_new (MODEST_DIMMING_RULES_CLIPBOARD, FALSE);
738 /* Add common dimming rules */
739 modest_dimming_rules_group_add_rules (toolbar_rules_group,
740 modest_msg_view_toolbar_dimming_entries,
741 G_N_ELEMENTS (modest_msg_view_toolbar_dimming_entries),
742 MODEST_WINDOW (self));
743 modest_dimming_rules_group_add_rules (clipboard_rules_group,
744 modest_msg_view_clipboard_dimming_entries,
745 G_N_ELEMENTS (modest_msg_view_clipboard_dimming_entries),
746 MODEST_WINDOW (self));
748 /* Insert dimming rules group for this window */
749 modest_ui_dimming_manager_insert_rules_group (parent_priv->ui_dimming_manager, toolbar_rules_group);
750 modest_ui_dimming_manager_insert_rules_group (parent_priv->ui_dimming_manager, clipboard_rules_group);
751 g_object_unref (toolbar_rules_group);
752 g_object_unref (clipboard_rules_group);
754 /* g_signal_connect (G_OBJECT(obj), "delete-event", G_CALLBACK(on_delete_event), obj); */
756 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);
757 g_signal_connect (G_OBJECT(priv->msg_view), "activate_link",
758 G_CALLBACK (modest_ui_actions_on_msg_link_clicked), obj);
759 g_signal_connect (G_OBJECT(priv->msg_view), "link_hover",
760 G_CALLBACK (modest_ui_actions_on_msg_link_hover), obj);
761 g_signal_connect (G_OBJECT(priv->msg_view), "attachment_clicked",
762 G_CALLBACK (modest_ui_actions_on_msg_attachment_clicked), obj);
763 g_signal_connect (G_OBJECT(priv->msg_view), "recpt_activated",
764 G_CALLBACK (modest_ui_actions_on_msg_recpt_activated), obj);
765 g_signal_connect (G_OBJECT(priv->msg_view), "show_details",
766 G_CALLBACK (modest_ui_actions_on_details), obj);
767 g_signal_connect (G_OBJECT(priv->msg_view), "link_contextual",
768 G_CALLBACK (modest_ui_actions_on_msg_link_contextual), obj);
769 g_signal_connect (G_OBJECT (priv->msg_view), "fetch_image",
770 G_CALLBACK (on_fetch_image), obj);
772 g_signal_connect (G_OBJECT (obj), "key-release-event",
773 G_CALLBACK (modest_msg_view_window_key_event),
776 g_signal_connect (G_OBJECT (obj), "key-press-event",
777 G_CALLBACK (modest_msg_view_window_key_event),
780 g_signal_connect (G_OBJECT (obj), "move-focus",
781 G_CALLBACK (on_move_focus), obj);
783 g_signal_connect (G_OBJECT (obj), "map-event",
784 G_CALLBACK (_modest_msg_view_window_map_event),
787 /* Mail Operation Queue */
788 priv->queue_change_handler = g_signal_connect (G_OBJECT (modest_runtime_get_mail_operation_queue ()),
790 G_CALLBACK (on_queue_changed),
793 /* Account manager */
794 priv->account_removed_handler = g_signal_connect (G_OBJECT (modest_runtime_get_account_store ()),
796 G_CALLBACK(on_account_removed),
799 modest_window_set_active_account (MODEST_WINDOW(obj), modest_account_name);
800 modest_window_set_active_mailbox (MODEST_WINDOW(obj), mailbox);
802 /* First add out toolbar ... */
803 modest_msg_view_window_show_toolbar (MODEST_WINDOW (obj), TRUE);
805 /* ... and later the find toolbar. This way find toolbar will
806 be shown over the other */
807 priv->find_toolbar = hildon_find_toolbar_new (NULL);
808 hildon_window_add_toolbar (HILDON_WINDOW (obj), GTK_TOOLBAR (priv->find_toolbar));
809 gtk_widget_set_no_show_all (priv->find_toolbar, TRUE);
810 g_signal_connect (G_OBJECT (priv->find_toolbar), "close",
811 G_CALLBACK (modest_msg_view_window_find_toolbar_close), obj);
812 g_signal_connect (G_OBJECT (priv->find_toolbar), "search",
813 G_CALLBACK (modest_msg_view_window_find_toolbar_search), obj);
814 priv->last_search = NULL;
816 /* Init the clipboard actions dim status */
817 modest_msg_view_grab_focus(MODEST_MSG_VIEW (priv->msg_view));
819 update_window_title (MODEST_MSG_VIEW_WINDOW (obj));
824 /* FIXME: parameter checks */
826 modest_msg_view_window_new_with_header_model (TnyMsg *msg,
827 const gchar *modest_account_name,
828 const gchar *mailbox,
829 const gchar *msg_uid,
831 GtkTreeRowReference *row_reference)
833 ModestMsgViewWindow *window = NULL;
834 ModestMsgViewWindowPrivate *priv = NULL;
835 TnyFolder *header_folder = NULL;
836 ModestHeaderView *header_view = NULL;
837 ModestWindowMgr *mgr = NULL;
840 modest_tny_mime_part_to_string (TNY_MIME_PART (msg), 0);
843 mgr = modest_runtime_get_window_mgr ();
844 window = MODEST_MSG_VIEW_WINDOW (modest_window_mgr_get_msg_view_window (mgr));
845 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), NULL);
847 modest_msg_view_window_construct (window, modest_account_name, mailbox, msg_uid);
849 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
851 /* Remember the message list's TreeModel so we can detect changes
852 * and change the list selection when necessary: */
853 header_folder = modest_header_view_get_folder (header_view);
855 priv->is_outbox = (modest_tny_folder_guess_folder_type (header_folder) ==
856 TNY_FOLDER_TYPE_OUTBOX);
857 priv->header_folder_id = tny_folder_get_id (header_folder);
858 g_object_unref(header_folder);
861 /* Setup row references and connect signals */
862 priv->header_model = g_object_ref (model);
865 priv->row_reference = gtk_tree_row_reference_copy (row_reference);
866 priv->next_row_reference = gtk_tree_row_reference_copy (row_reference);
867 select_next_valid_row (model, &(priv->next_row_reference), TRUE, priv->is_outbox);
869 priv->row_reference = NULL;
870 priv->next_row_reference = NULL;
873 /* Connect signals */
874 priv->row_changed_handler =
875 g_signal_connect (GTK_TREE_MODEL(model), "row-changed",
876 G_CALLBACK(modest_msg_view_window_on_row_changed),
878 priv->row_deleted_handler =
879 g_signal_connect (GTK_TREE_MODEL(model), "row-deleted",
880 G_CALLBACK(modest_msg_view_window_on_row_deleted),
882 priv->row_inserted_handler =
883 g_signal_connect (GTK_TREE_MODEL(model), "row-inserted",
884 G_CALLBACK(modest_msg_view_window_on_row_inserted),
886 priv->rows_reordered_handler =
887 g_signal_connect(GTK_TREE_MODEL(model), "rows-reordered",
888 G_CALLBACK(modest_msg_view_window_on_row_reordered),
891 if (header_view != NULL){
892 modest_header_view_add_observer(header_view,
893 MODEST_HEADER_VIEW_OBSERVER(window));
896 tny_msg_view_set_msg (TNY_MSG_VIEW (priv->msg_view), msg);
897 update_window_title (MODEST_MSG_VIEW_WINDOW (window));
898 update_branding (MODEST_MSG_VIEW_WINDOW (window));
900 /* gtk_widget_show_all (GTK_WIDGET (window)); */
901 modest_msg_view_window_update_priority (window);
902 /* Check dimming rules */
903 modest_ui_actions_check_toolbar_dimming_rules (MODEST_WINDOW (window));
904 modest_ui_actions_check_menu_dimming_rules (MODEST_WINDOW (window));
905 modest_window_check_dimming_rules_group (MODEST_WINDOW (window), MODEST_DIMMING_RULES_CLIPBOARD);
907 return MODEST_WINDOW(window);
911 modest_msg_view_window_new_from_uid (const gchar *modest_account_name,
912 const gchar *mailbox,
913 const gchar *msg_uid)
915 ModestMsgViewWindow *window = NULL;
916 ModestMsgViewWindowPrivate *priv = NULL;
917 ModestWindowMgr *mgr = NULL;
919 TnyAccount *account = NULL;
921 mgr = modest_runtime_get_window_mgr ();
922 window = MODEST_MSG_VIEW_WINDOW (modest_window_mgr_get_msg_view_window (mgr));
923 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), NULL);
925 modest_msg_view_window_construct (window, modest_account_name, mailbox, msg_uid);
927 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
931 is_merge = g_str_has_prefix (msg_uid, "merge:");
933 /* Get the account */
935 account = tny_account_store_find_account (TNY_ACCOUNT_STORE (modest_runtime_get_account_store ()),
939 if (is_merge || account) {
940 TnyFolder *folder = NULL;
942 /* Try to get the message, if it's already downloaded
943 we don't need to connect */
945 folder = modest_tny_folder_store_find_folder_from_uri (TNY_FOLDER_STORE (account), msg_uid);
947 ModestTnyAccountStore *account_store;
948 ModestTnyLocalFoldersAccount *local_folders_account;
950 account_store = modest_runtime_get_account_store ();
951 local_folders_account = MODEST_TNY_LOCAL_FOLDERS_ACCOUNT (
952 modest_tny_account_store_get_local_folders_account (account_store));
953 folder = modest_tny_local_folders_account_get_merged_outbox (local_folders_account);
954 g_object_unref (local_folders_account);
958 gboolean device_online;
960 device = modest_runtime_get_device();
961 device_online = tny_device_is_online (device);
963 message_reader (window, priv, NULL, msg_uid, folder, NULL);
965 TnyMsg *msg = tny_folder_find_msg (folder, msg_uid, NULL);
967 tny_msg_view_set_msg (TNY_MSG_VIEW (priv->msg_view), msg);
968 update_window_title (MODEST_MSG_VIEW_WINDOW (window));
969 update_branding (MODEST_MSG_VIEW_WINDOW (window));
970 g_object_unref (msg);
972 message_reader (window, priv, NULL, msg_uid, folder, NULL);
975 g_object_unref (folder);
980 /* Check dimming rules */
981 modest_ui_actions_check_toolbar_dimming_rules (MODEST_WINDOW (window));
982 modest_ui_actions_check_menu_dimming_rules (MODEST_WINDOW (window));
983 modest_window_check_dimming_rules_group (MODEST_WINDOW (window), MODEST_DIMMING_RULES_CLIPBOARD);
985 return MODEST_WINDOW(window);
989 modest_msg_view_window_new_from_header_view (ModestHeaderView *header_view,
990 const gchar *modest_account_name,
991 const gchar *mailbox,
992 const gchar *msg_uid,
993 GtkTreeRowReference *row_reference)
995 ModestMsgViewWindow *window = NULL;
996 ModestMsgViewWindowPrivate *priv = NULL;
997 TnyFolder *header_folder = NULL;
998 ModestWindowMgr *mgr = NULL;
1002 mgr = modest_runtime_get_window_mgr ();
1003 window = MODEST_MSG_VIEW_WINDOW (modest_window_mgr_get_msg_view_window (mgr));
1004 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), NULL);
1006 modest_msg_view_window_construct (window, modest_account_name, mailbox, msg_uid);
1008 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1010 /* Remember the message list's TreeModel so we can detect changes
1011 * and change the list selection when necessary: */
1013 if (header_view != NULL){
1014 header_folder = modest_header_view_get_folder(header_view);
1015 /* This could happen if the header folder was
1016 unseleted before opening this msg window (for
1017 example if the user selects an account in the
1018 folder view of the main window */
1019 if (header_folder) {
1020 priv->is_outbox = (modest_tny_folder_guess_folder_type (header_folder) ==
1021 TNY_FOLDER_TYPE_OUTBOX);
1022 priv->header_folder_id = tny_folder_get_id(header_folder);
1023 g_object_unref(header_folder);
1027 /* Setup row references and connect signals */
1028 priv->header_model = gtk_tree_view_get_model (GTK_TREE_VIEW (header_view));
1029 g_object_ref (priv->header_model);
1031 if (row_reference) {
1032 priv->row_reference = gtk_tree_row_reference_copy (row_reference);
1033 priv->next_row_reference = gtk_tree_row_reference_copy (row_reference);
1034 select_next_valid_row (priv->header_model, &(priv->next_row_reference), TRUE, priv->is_outbox);
1036 priv->row_reference = NULL;
1037 priv->next_row_reference = NULL;
1040 /* Connect signals */
1041 priv->row_changed_handler =
1042 g_signal_connect (GTK_TREE_MODEL(priv->header_model), "row-changed",
1043 G_CALLBACK(modest_msg_view_window_on_row_changed),
1045 priv->row_deleted_handler =
1046 g_signal_connect (GTK_TREE_MODEL(priv->header_model), "row-deleted",
1047 G_CALLBACK(modest_msg_view_window_on_row_deleted),
1049 priv->row_inserted_handler =
1050 g_signal_connect (GTK_TREE_MODEL(priv->header_model), "row-inserted",
1051 G_CALLBACK(modest_msg_view_window_on_row_inserted),
1053 priv->rows_reordered_handler =
1054 g_signal_connect(GTK_TREE_MODEL(priv->header_model), "rows-reordered",
1055 G_CALLBACK(modest_msg_view_window_on_row_reordered),
1058 if (header_view != NULL){
1059 modest_header_view_add_observer(header_view,
1060 MODEST_HEADER_VIEW_OBSERVER(window));
1063 tny_msg_view_set_msg (TNY_MSG_VIEW (priv->msg_view), NULL);
1064 update_branding (MODEST_MSG_VIEW_WINDOW (window));
1066 path = gtk_tree_row_reference_get_path (row_reference);
1067 if (gtk_tree_model_get_iter (priv->header_model, &iter, path)) {
1069 gtk_tree_model_get (priv->header_model, &iter,
1070 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
1072 message_reader (window, priv, header, NULL, NULL, row_reference);
1073 g_object_unref (header);
1075 gtk_tree_path_free (path);
1077 /* Check dimming rules */
1078 modest_ui_actions_check_toolbar_dimming_rules (MODEST_WINDOW (window));
1079 modest_ui_actions_check_menu_dimming_rules (MODEST_WINDOW (window));
1080 modest_window_check_dimming_rules_group (MODEST_WINDOW (window), MODEST_DIMMING_RULES_CLIPBOARD);
1082 return MODEST_WINDOW(window);
1086 modest_msg_view_window_new_for_search_result (TnyMsg *msg,
1087 const gchar *modest_account_name,
1088 const gchar *mailbox,
1089 const gchar *msg_uid)
1091 ModestMsgViewWindow *window = NULL;
1092 ModestMsgViewWindowPrivate *priv = NULL;
1093 ModestWindowMgr *mgr = NULL;
1095 mgr = modest_runtime_get_window_mgr ();
1096 window = MODEST_MSG_VIEW_WINDOW (modest_window_mgr_get_msg_view_window (mgr));
1097 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), NULL);
1098 modest_msg_view_window_construct (window, modest_account_name, mailbox, msg_uid);
1100 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1102 /* Remember that this is a search result,
1103 * so we can disable some UI appropriately: */
1104 priv->is_search_result = TRUE;
1106 tny_msg_view_set_msg (TNY_MSG_VIEW (priv->msg_view), msg);
1107 update_branding (MODEST_MSG_VIEW_WINDOW (window));
1109 update_window_title (window);
1110 /* gtk_widget_show_all (GTK_WIDGET (window));*/
1111 modest_msg_view_window_update_priority (window);
1113 /* Check dimming rules */
1114 modest_ui_actions_check_toolbar_dimming_rules (MODEST_WINDOW (window));
1115 modest_ui_actions_check_menu_dimming_rules (MODEST_WINDOW (window));
1116 modest_window_check_dimming_rules_group (MODEST_WINDOW (window), MODEST_DIMMING_RULES_CLIPBOARD);
1118 return MODEST_WINDOW(window);
1122 modest_msg_view_window_is_other_body (ModestMsgViewWindow *self)
1124 ModestMsgViewWindowPrivate *priv = NULL;
1126 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self), FALSE);
1127 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
1129 return (priv->other_body != NULL);
1133 modest_msg_view_window_new_with_other_body (TnyMsg *msg,
1134 TnyMimePart *other_body,
1135 const gchar *modest_account_name,
1136 const gchar *mailbox,
1137 const gchar *msg_uid)
1139 GObject *obj = NULL;
1140 ModestMsgViewWindowPrivate *priv;
1141 ModestWindowMgr *mgr = NULL;
1143 g_return_val_if_fail (msg, NULL);
1144 mgr = modest_runtime_get_window_mgr ();
1145 obj = G_OBJECT (modest_window_mgr_get_msg_view_window (mgr));
1146 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (obj);
1147 modest_msg_view_window_construct (MODEST_MSG_VIEW_WINDOW (obj),
1148 modest_account_name, mailbox, msg_uid);
1151 priv->other_body = g_object_ref (other_body);
1152 modest_msg_view_set_msg_with_other_body (MODEST_MSG_VIEW (priv->msg_view), msg, other_body);
1154 tny_msg_view_set_msg (TNY_MSG_VIEW (priv->msg_view), msg);
1156 update_window_title (MODEST_MSG_VIEW_WINDOW (obj));
1157 update_branding (MODEST_MSG_VIEW_WINDOW (obj));
1159 /* gtk_widget_show_all (GTK_WIDGET (obj)); */
1161 /* Check dimming rules */
1162 modest_ui_actions_check_toolbar_dimming_rules (MODEST_WINDOW (obj));
1163 modest_ui_actions_check_menu_dimming_rules (MODEST_WINDOW (obj));
1164 modest_window_check_dimming_rules_group (MODEST_WINDOW (obj), MODEST_DIMMING_RULES_CLIPBOARD);
1166 return MODEST_WINDOW(obj);
1170 modest_msg_view_window_new_for_attachment (TnyMsg *msg,
1171 const gchar *modest_account_name,
1172 const gchar *mailbox,
1173 const gchar *msg_uid)
1175 return modest_msg_view_window_new_with_other_body (msg, NULL, modest_account_name, mailbox, msg_uid);
1179 modest_msg_view_window_on_row_changed (GtkTreeModel *header_model,
1182 ModestMsgViewWindow *window)
1184 check_dimming_rules_after_change (window);
1188 modest_msg_view_window_on_row_deleted(GtkTreeModel *header_model,
1190 ModestMsgViewWindow *window)
1192 check_dimming_rules_after_change (window);
1194 /* The window could have dissapeared */
1197 check_dimming_rules_after_change (ModestMsgViewWindow *window)
1199 modest_ui_actions_check_menu_dimming_rules (MODEST_WINDOW (window));
1200 modest_ui_actions_check_toolbar_dimming_rules (MODEST_WINDOW (window));
1204 /* On insertions we check if the folder still has the message we are
1205 * showing or do not. If do not, we do nothing. Which means we are still
1206 * not attached to any header folder and thus next/prev buttons are
1207 * still dimmed. Once the message that is shown by msg-view is found, the
1208 * new model of header-view will be attached and the references will be set.
1209 * On each further insertions dimming rules will be checked. However
1210 * this requires extra CPU time at least works.
1211 * (An message might be deleted from TnyFolder and thus will not be
1212 * inserted into the model again for example if it is removed by the
1213 * imap server and the header view is refreshed.)
1216 modest_msg_view_window_on_row_inserted (GtkTreeModel *model,
1217 GtkTreePath *tree_path,
1218 GtkTreeIter *tree_iter,
1219 ModestMsgViewWindow *window)
1221 ModestMsgViewWindowPrivate *priv = NULL;
1222 TnyHeader *header = NULL;
1224 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window));
1225 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1227 g_assert (model == priv->header_model);
1229 /* Check if the newly inserted message is the same we are actually
1230 * showing. IF not, we should remain detached from the header model
1231 * and thus prev and next toolbar buttons should remain dimmed. */
1232 gtk_tree_model_get (model, tree_iter,
1233 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
1236 if (TNY_IS_HEADER (header)) {
1239 uid = modest_tny_folder_get_header_unique_id (header);
1240 if (!g_str_equal(priv->msg_uid, uid)) {
1241 check_dimming_rules_after_change (window);
1243 g_object_unref (G_OBJECT(header));
1247 g_object_unref(G_OBJECT(header));
1250 if (priv->row_reference) {
1251 gtk_tree_row_reference_free (priv->row_reference);
1254 /* Setup row_reference for the actual msg. */
1255 priv->row_reference = gtk_tree_row_reference_new (priv->header_model, tree_path);
1256 if (priv->row_reference == NULL) {
1257 g_warning("%s: No reference for msg header item.", __FUNCTION__);
1261 /* Now set up next_row_reference. */
1262 if (priv->next_row_reference) {
1263 gtk_tree_row_reference_free (priv->next_row_reference);
1266 priv->next_row_reference = gtk_tree_row_reference_copy (priv->row_reference);
1267 select_next_valid_row (priv->header_model,
1268 &(priv->next_row_reference), FALSE, priv->is_outbox);
1270 /* Connect the remaining callbacks to become able to detect
1271 * changes in header-view. */
1272 priv->row_changed_handler =
1273 g_signal_connect (priv->header_model, "row-changed",
1274 G_CALLBACK (modest_msg_view_window_on_row_changed),
1276 priv->row_deleted_handler =
1277 g_signal_connect (priv->header_model, "row-deleted",
1278 G_CALLBACK (modest_msg_view_window_on_row_deleted),
1280 priv->rows_reordered_handler =
1281 g_signal_connect (priv->header_model, "rows-reordered",
1282 G_CALLBACK (modest_msg_view_window_on_row_reordered),
1285 check_dimming_rules_after_change (window);
1289 modest_msg_view_window_on_row_reordered (GtkTreeModel *header_model,
1293 ModestMsgViewWindow *window)
1295 ModestMsgViewWindowPrivate *priv = NULL;
1296 gboolean already_changed = FALSE;
1298 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(window);
1300 /* If the current row was reordered select the proper next
1301 valid row. The same if the next row reference changes */
1302 if (!priv->row_reference ||
1303 !gtk_tree_row_reference_valid (priv->row_reference))
1306 if (priv->next_row_reference &&
1307 gtk_tree_row_reference_valid (priv->next_row_reference)) {
1308 GtkTreePath *cur, *next;
1309 /* Check that the order is still the correct one */
1310 cur = gtk_tree_row_reference_get_path (priv->row_reference);
1311 next = gtk_tree_row_reference_get_path (priv->next_row_reference);
1312 gtk_tree_path_next (cur);
1313 if (gtk_tree_path_compare (cur, next) != 0) {
1314 gtk_tree_row_reference_free (priv->next_row_reference);
1315 priv->next_row_reference = gtk_tree_row_reference_copy (priv->row_reference);
1316 select_next_valid_row (header_model, &(priv->next_row_reference), FALSE, priv->is_outbox);
1317 already_changed = TRUE;
1319 gtk_tree_path_free (cur);
1320 gtk_tree_path_free (next);
1322 if (priv->next_row_reference)
1323 gtk_tree_row_reference_free (priv->next_row_reference);
1324 /* Update next row reference */
1325 priv->next_row_reference = gtk_tree_row_reference_copy (priv->row_reference);
1326 select_next_valid_row (header_model, &(priv->next_row_reference), FALSE, priv->is_outbox);
1327 already_changed = TRUE;
1330 check_dimming_rules_after_change (window);
1333 /* The modest_msg_view_window_update_model_replaced implements update
1334 * function for ModestHeaderViewObserver. Checks whether the TnyFolder
1335 * actually belongs to the header-view is the same as the TnyFolder of
1336 * the message of msg-view or not. If they are different, there is
1337 * nothing to do. If they are the same, then the model has replaced and
1338 * the reference in msg-view shall be replaced from the old model to
1339 * the new model. In this case the view will be detached from it's
1340 * header folder. From this point the next/prev buttons are dimmed.
1343 modest_msg_view_window_update_model_replaced (ModestHeaderViewObserver *observer,
1344 GtkTreeModel *model,
1345 const gchar *tny_folder_id)
1347 ModestMsgViewWindowPrivate *priv = NULL;
1348 ModestMsgViewWindow *window = NULL;
1350 g_assert(MODEST_IS_HEADER_VIEW_OBSERVER(observer));
1351 g_assert(MODEST_IS_MSG_VIEW_WINDOW(observer));
1353 window = MODEST_MSG_VIEW_WINDOW(observer);
1354 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(window);
1356 /* If there is an other folder in the header-view then we do
1357 * not care about it's model (msg list). Else if the
1358 * header-view shows the folder the msg shown by us is in, we
1359 * shall replace our model reference and make some check. */
1360 if(model == NULL || tny_folder_id == NULL ||
1361 (priv->header_folder_id && !g_str_equal(tny_folder_id, priv->header_folder_id)))
1364 /* Model is changed(replaced), so we should forget the old
1365 * one. Because there might be other references and there
1366 * might be some change on the model even if we unreferenced
1367 * it, we need to disconnect our signals here. */
1368 if (priv->header_model) {
1369 if (g_signal_handler_is_connected(G_OBJECT (priv->header_model),
1370 priv->row_changed_handler))
1371 g_signal_handler_disconnect(G_OBJECT (priv->header_model),
1372 priv->row_changed_handler);
1373 if (g_signal_handler_is_connected(G_OBJECT (priv->header_model),
1374 priv->row_deleted_handler))
1375 g_signal_handler_disconnect(G_OBJECT (priv->header_model),
1376 priv->row_deleted_handler);
1377 if (g_signal_handler_is_connected(G_OBJECT (priv->header_model),
1378 priv->row_inserted_handler))
1379 g_signal_handler_disconnect(G_OBJECT (priv->header_model),
1380 priv->row_inserted_handler);
1381 if (g_signal_handler_is_connected(G_OBJECT (priv->header_model),
1382 priv->rows_reordered_handler))
1383 g_signal_handler_disconnect(G_OBJECT (priv->header_model),
1384 priv->rows_reordered_handler);
1387 if (priv->row_reference)
1388 gtk_tree_row_reference_free (priv->row_reference);
1389 if (priv->next_row_reference)
1390 gtk_tree_row_reference_free (priv->next_row_reference);
1391 g_object_unref(priv->header_model);
1394 priv->row_changed_handler = 0;
1395 priv->row_deleted_handler = 0;
1396 priv->row_inserted_handler = 0;
1397 priv->rows_reordered_handler = 0;
1398 priv->next_row_reference = NULL;
1399 priv->row_reference = NULL;
1400 priv->header_model = NULL;
1403 priv->header_model = g_object_ref (model);
1405 /* Also we must connect to the new model for row insertions.
1406 * Only for insertions now. We will need other ones only after
1407 * the msg is show by msg-view is added to the new model. */
1408 priv->row_inserted_handler =
1409 g_signal_connect (priv->header_model, "row-inserted",
1410 G_CALLBACK(modest_msg_view_window_on_row_inserted),
1413 modest_ui_actions_check_menu_dimming_rules(MODEST_WINDOW(window));
1414 modest_ui_actions_check_toolbar_dimming_rules(MODEST_WINDOW(window));
1418 modest_msg_view_window_toolbar_on_transfer_mode (ModestMsgViewWindow *self)
1420 ModestMsgViewWindowPrivate *priv= NULL;
1422 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self), FALSE);
1423 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
1425 return priv->progress_hint;
1429 modest_msg_view_window_get_header (ModestMsgViewWindow *self)
1431 ModestMsgViewWindowPrivate *priv= NULL;
1433 TnyHeader *header = NULL;
1434 GtkTreePath *path = NULL;
1437 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self), NULL);
1438 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
1440 /* If the message was not obtained from a treemodel,
1441 * for instance if it was opened directly by the search UI:
1443 if (priv->header_model == NULL ||
1444 priv->row_reference == NULL ||
1445 !gtk_tree_row_reference_valid (priv->row_reference)) {
1446 msg = modest_msg_view_window_get_message (self);
1448 header = tny_msg_get_header (msg);
1449 g_object_unref (msg);
1454 /* Get iter of the currently selected message in the header view: */
1455 path = gtk_tree_row_reference_get_path (priv->row_reference);
1456 g_return_val_if_fail (path != NULL, NULL);
1457 gtk_tree_model_get_iter (priv->header_model,
1461 /* Get current message header */
1462 gtk_tree_model_get (priv->header_model, &iter,
1463 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
1466 gtk_tree_path_free (path);
1471 modest_msg_view_window_get_message (ModestMsgViewWindow *self)
1473 ModestMsgViewWindowPrivate *priv;
1475 g_return_val_if_fail (self, NULL);
1477 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(self);
1479 return tny_msg_view_get_msg (TNY_MSG_VIEW (priv->msg_view));
1483 modest_msg_view_window_get_message_uid (ModestMsgViewWindow *self)
1485 ModestMsgViewWindowPrivate *priv;
1487 g_return_val_if_fail (self, NULL);
1489 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
1491 return (const gchar*) priv->msg_uid;
1494 /* Used for the Ctrl+F accelerator */
1496 modest_msg_view_window_toggle_find_toolbar (GtkWidget *obj,
1499 ModestMsgViewWindow *window = MODEST_MSG_VIEW_WINDOW (data);
1500 ModestMsgViewWindowPrivate *priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1502 if (GTK_WIDGET_VISIBLE (priv->find_toolbar)) {
1503 modest_msg_view_window_find_toolbar_close (obj, data);
1505 modest_msg_view_window_show_find_toolbar (obj, data);
1509 /* Handler for menu option */
1511 modest_msg_view_window_show_find_toolbar (GtkWidget *obj,
1514 ModestMsgViewWindow *window = MODEST_MSG_VIEW_WINDOW (data);
1515 ModestMsgViewWindowPrivate *priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1517 gtk_widget_show (priv->find_toolbar);
1518 hildon_find_toolbar_highlight_entry (HILDON_FIND_TOOLBAR (priv->find_toolbar), TRUE);
1521 /* Handler for click on the "X" close button in find toolbar */
1523 modest_msg_view_window_find_toolbar_close (GtkWidget *widget,
1524 ModestMsgViewWindow *obj)
1526 ModestMsgViewWindowPrivate *priv;
1528 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (obj);
1531 gtk_widget_hide (priv->find_toolbar);
1532 modest_msg_view_grab_focus (MODEST_MSG_VIEW (priv->msg_view));
1536 modest_msg_view_window_find_toolbar_search (GtkWidget *widget,
1537 ModestMsgViewWindow *obj)
1539 gchar *current_search;
1540 ModestMsgViewWindowPrivate *priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (obj);
1542 if (modest_mime_part_view_is_empty (MODEST_MIME_PART_VIEW (priv->msg_view))) {
1543 hildon_banner_show_information (NULL, NULL, _("mail_ib_nothing_to_find"));
1547 g_object_get (G_OBJECT (widget), "prefix", ¤t_search, NULL);
1549 if ((current_search == NULL) || (strcmp (current_search, "") == 0)) {
1550 g_free (current_search);
1551 hildon_banner_show_information (NULL, NULL, _CS("ecdg_ib_find_rep_enter_text"));
1555 if ((priv->last_search == NULL) || (strcmp (priv->last_search, current_search) != 0)) {
1557 g_free (priv->last_search);
1558 priv->last_search = g_strdup (current_search);
1559 result = modest_isearch_view_search (MODEST_ISEARCH_VIEW (priv->msg_view),
1562 hildon_banner_show_information (NULL, NULL,
1563 _HL("ckct_ib_find_no_matches"));
1564 g_free (priv->last_search);
1565 priv->last_search = NULL;
1567 hildon_find_toolbar_highlight_entry (HILDON_FIND_TOOLBAR (priv->find_toolbar), TRUE);
1570 if (!modest_isearch_view_search_next (MODEST_ISEARCH_VIEW (priv->msg_view))) {
1571 hildon_banner_show_information (NULL, NULL,
1572 _HL("ckct_ib_find_search_complete"));
1573 g_free (priv->last_search);
1574 priv->last_search = NULL;
1576 hildon_find_toolbar_highlight_entry (HILDON_FIND_TOOLBAR (priv->find_toolbar), TRUE);
1580 g_free (current_search);
1585 modest_msg_view_window_set_zoom (ModestWindow *window,
1588 ModestMsgViewWindowPrivate *priv;
1589 ModestWindowPrivate *parent_priv;
1591 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window));
1593 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1594 parent_priv = MODEST_WINDOW_GET_PRIVATE (window);
1595 modest_zoomable_set_zoom (MODEST_ZOOMABLE (priv->msg_view), zoom);
1600 modest_msg_view_window_get_zoom (ModestWindow *window)
1602 ModestMsgViewWindowPrivate *priv;
1604 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), 1.0);
1606 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1607 return modest_zoomable_get_zoom (MODEST_ZOOMABLE (priv->msg_view));
1611 modest_msg_view_window_zoom_plus (ModestWindow *window)
1614 ModestMsgViewWindowPrivate *priv;
1618 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), 1.0);
1619 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1621 zoom_level = modest_zoomable_get_zoom (MODEST_ZOOMABLE (priv->msg_view));
1623 if (zoom_level >= 2.0) {
1624 hildon_banner_show_information (NULL, NULL,
1625 _CS("ckct_ib_max_zoom_level_reached"));
1627 } else if (zoom_level >= 1.5) {
1629 } else if (zoom_level >= 1.2) {
1631 } else if (zoom_level >= 1.0) {
1633 } else if (zoom_level >= 0.8) {
1635 } else if (zoom_level >= 0.5) {
1641 /* set zoom level */
1642 int_zoom = (gint) rint (zoom_level*100.0+0.1);
1643 banner_text = g_strdup_printf (_HL("wdgt_ib_zoom"), int_zoom);
1644 modest_platform_information_banner (GTK_WIDGET (window), NULL, banner_text);
1645 g_free (banner_text);
1646 modest_zoomable_set_zoom (MODEST_ZOOMABLE (priv->msg_view), zoom_level);
1652 modest_msg_view_window_zoom_minus (ModestWindow *window)
1655 ModestMsgViewWindowPrivate *priv;
1659 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), 1.0);
1660 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1662 zoom_level = modest_zoomable_get_zoom (MODEST_ZOOMABLE (priv->msg_view));
1664 if (zoom_level <= 0.5) {
1665 hildon_banner_show_information (NULL, NULL,
1666 _CS("ckct_ib_min_zoom_level_reached"));
1668 } else if (zoom_level <= 0.8) {
1670 } else if (zoom_level <= 1.0) {
1672 } else if (zoom_level <= 1.2) {
1674 } else if (zoom_level <= 1.5) {
1676 } else if (zoom_level <= 2.0) {
1682 /* set zoom level */
1683 int_zoom = (gint) rint (zoom_level*100.0+0.1);
1684 banner_text = g_strdup_printf (_HL("wdgt_ib_zoom"), int_zoom);
1685 modest_platform_information_banner (GTK_WIDGET (window), NULL, banner_text);
1686 g_free (banner_text);
1687 modest_zoomable_set_zoom (MODEST_ZOOMABLE (priv->msg_view), zoom_level);
1694 modest_msg_view_window_key_event (GtkWidget *window,
1700 focus = gtk_window_get_focus (GTK_WINDOW (window));
1702 /* for the find toolbar case */
1703 if (focus && GTK_IS_ENTRY (focus)) {
1704 if (event->keyval == GDK_BackSpace) {
1706 copy = gdk_event_copy ((GdkEvent *) event);
1707 gtk_widget_event (focus, copy);
1708 gdk_event_free (copy);
1713 if (event->keyval == GDK_Up || event->keyval == GDK_KP_Up ||
1714 event->keyval == GDK_Down || event->keyval == GDK_KP_Down ||
1715 event->keyval == GDK_Page_Up || event->keyval == GDK_KP_Page_Up ||
1716 event->keyval == GDK_Page_Down || event->keyval == GDK_KP_Page_Down ||
1717 event->keyval == GDK_Home || event->keyval == GDK_KP_Home ||
1718 event->keyval == GDK_End || event->keyval == GDK_KP_End) {
1719 /* ModestMsgViewWindowPrivate *priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window); */
1720 /* gboolean return_value; */
1722 if (event->type == GDK_KEY_PRESS) {
1723 GtkScrollType scroll_type;
1725 switch (event->keyval) {
1728 scroll_type = GTK_SCROLL_STEP_UP; break;
1731 scroll_type = GTK_SCROLL_STEP_DOWN; break;
1733 case GDK_KP_Page_Up:
1734 scroll_type = GTK_SCROLL_PAGE_UP; break;
1736 case GDK_KP_Page_Down:
1737 scroll_type = GTK_SCROLL_PAGE_DOWN; break;
1740 scroll_type = GTK_SCROLL_START; break;
1743 scroll_type = GTK_SCROLL_END; break;
1744 default: scroll_type = GTK_SCROLL_NONE;
1747 /* g_signal_emit_by_name (G_OBJECT (priv->main_scroll), "scroll-child", */
1748 /* scroll_type, FALSE, &return_value); */
1759 modest_msg_view_window_last_message_selected (ModestMsgViewWindow *window)
1762 ModestMsgViewWindowPrivate *priv;
1763 GtkTreeIter tmp_iter;
1764 gboolean is_last_selected;
1766 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), TRUE);
1767 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1769 /*if no model (so no rows at all), then virtually we are the last*/
1770 if (!priv->header_model || !priv->row_reference)
1773 if (!gtk_tree_row_reference_valid (priv->row_reference))
1776 path = gtk_tree_row_reference_get_path (priv->row_reference);
1780 is_last_selected = TRUE;
1781 while (is_last_selected) {
1783 gtk_tree_path_next (path);
1784 if (!gtk_tree_model_get_iter (priv->header_model, &tmp_iter, path))
1786 gtk_tree_model_get (priv->header_model, &tmp_iter,
1787 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
1790 if (msg_is_visible (header, priv->is_outbox))
1791 is_last_selected = FALSE;
1792 g_object_unref(G_OBJECT(header));
1795 gtk_tree_path_free (path);
1796 return is_last_selected;
1800 modest_msg_view_window_has_headers_model (ModestMsgViewWindow *window)
1802 ModestMsgViewWindowPrivate *priv;
1804 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), TRUE);
1805 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1807 return priv->header_model != NULL;
1811 modest_msg_view_window_is_search_result (ModestMsgViewWindow *window)
1813 ModestMsgViewWindowPrivate *priv;
1815 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), TRUE);
1816 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1818 return priv->is_search_result;
1822 msg_is_visible (TnyHeader *header, gboolean check_outbox)
1824 if ((tny_header_get_flags(header) & TNY_HEADER_FLAG_DELETED))
1826 if (!check_outbox) {
1829 ModestTnySendQueueStatus status;
1830 status = modest_tny_all_send_queues_get_msg_status (header);
1831 return ((status != MODEST_TNY_SEND_QUEUE_FAILED) &&
1832 (status != MODEST_TNY_SEND_QUEUE_SENDING));
1837 modest_msg_view_window_first_message_selected (ModestMsgViewWindow *window)
1840 ModestMsgViewWindowPrivate *priv;
1841 gboolean is_first_selected;
1842 GtkTreeIter tmp_iter;
1844 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), TRUE);
1845 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1847 /*if no model (so no rows at all), then virtually we are the first*/
1848 if (!priv->header_model || !priv->row_reference)
1851 if (!gtk_tree_row_reference_valid (priv->row_reference))
1854 path = gtk_tree_row_reference_get_path (priv->row_reference);
1858 is_first_selected = TRUE;
1859 while (is_first_selected) {
1861 if(!gtk_tree_path_prev (path))
1863 /* Here the 'if' is needless for logic, but let make sure
1864 * iter is valid for gtk_tree_model_get. */
1865 if (!gtk_tree_model_get_iter (priv->header_model, &tmp_iter, path))
1867 gtk_tree_model_get (priv->header_model, &tmp_iter,
1868 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
1871 if (msg_is_visible (header, priv->is_outbox))
1872 is_first_selected = FALSE;
1873 g_object_unref(G_OBJECT(header));
1876 gtk_tree_path_free (path);
1877 return is_first_selected;
1884 GtkTreeRowReference *row_reference;
1888 message_reader_performer (gboolean canceled,
1890 GtkWindow *parent_window,
1891 TnyAccount *account,
1894 ModestMailOperation *mail_op = NULL;
1895 MsgReaderInfo *info;
1897 info = (MsgReaderInfo *) user_data;
1898 if (canceled || err) {
1899 update_window_title (MODEST_MSG_VIEW_WINDOW (parent_window));
1903 /* Register the header - it'll be unregistered in the callback */
1905 modest_window_mgr_register_header (modest_runtime_get_window_mgr (), info->header, NULL);
1907 /* New mail operation */
1908 mail_op = modest_mail_operation_new_with_error_handling (G_OBJECT(parent_window),
1909 modest_ui_actions_disk_operations_error_handler,
1912 modest_mail_operation_queue_add (modest_runtime_get_mail_operation_queue (), mail_op);
1914 modest_mail_operation_get_msg (mail_op, info->header, TRUE, view_msg_cb, info->row_reference);
1916 modest_mail_operation_find_msg (mail_op, info->folder, info->msg_uid, TRUE, view_msg_cb, NULL);
1917 g_object_unref (mail_op);
1919 /* Update dimming rules */
1920 modest_ui_actions_check_toolbar_dimming_rules (MODEST_WINDOW (parent_window));
1921 modest_ui_actions_check_menu_dimming_rules (MODEST_WINDOW (parent_window));
1924 /* Frees. The row_reference will be freed by the view_msg_cb callback */
1925 g_free (info->msg_uid);
1927 g_object_unref (info->folder);
1929 g_object_unref (info->header);
1930 g_slice_free (MsgReaderInfo, info);
1935 * Reads the message whose summary item is @header. It takes care of
1936 * several things, among others:
1938 * If the message was not previously downloaded then ask the user
1939 * before downloading. If there is no connection launch the connection
1940 * dialog. Update toolbar dimming rules.
1942 * Returns: TRUE if the mail operation was started, otherwise if the
1943 * user do not want to download the message, or if the user do not
1944 * want to connect, then the operation is not issued
1947 message_reader (ModestMsgViewWindow *window,
1948 ModestMsgViewWindowPrivate *priv,
1950 const gchar *msg_uid,
1952 GtkTreeRowReference *row_reference)
1954 ModestWindowMgr *mgr;
1955 TnyAccount *account;
1956 MsgReaderInfo *info;
1958 /* We set the header from model while we're loading */
1959 tny_header_view_set_header (TNY_HEADER_VIEW (priv->msg_view), header);
1960 gtk_window_set_title (GTK_WINDOW (window), _CS("ckdg_pb_updating"));
1963 g_object_ref (folder);
1965 mgr = modest_runtime_get_window_mgr ();
1966 /* Msg download completed */
1967 if (!header || !(tny_header_get_flags (header) & TNY_HEADER_FLAG_CACHED)) {
1969 /* Ask the user if he wants to download the message if
1971 if (!tny_device_is_online (modest_runtime_get_device())) {
1972 GtkResponseType response;
1974 response = modest_platform_run_confirmation_dialog (GTK_WINDOW (window),
1975 _("mcen_nc_get_msg"));
1976 if (response == GTK_RESPONSE_CANCEL) {
1977 update_window_title (window);
1982 folder = tny_header_get_folder (header);
1984 info = g_slice_new (MsgReaderInfo);
1985 info->msg_uid = g_strdup (msg_uid);
1987 info->header = g_object_ref (header);
1989 info->header = NULL;
1991 info->folder = g_object_ref (folder);
1993 info->folder = NULL;
1994 if (row_reference) {
1995 info->row_reference = gtk_tree_row_reference_copy (row_reference);
1997 info->row_reference = NULL;
2000 /* Offer the connection dialog if necessary */
2001 modest_platform_connect_if_remote_and_perform ((GtkWindow *) window,
2003 TNY_FOLDER_STORE (folder),
2004 message_reader_performer,
2006 g_object_unref (folder);
2012 folder = tny_header_get_folder (header);
2014 account = tny_folder_get_account (folder);
2015 info = g_slice_new (MsgReaderInfo);
2016 info->msg_uid = g_strdup (msg_uid);
2018 info->folder = g_object_ref (folder);
2020 info->folder = NULL;
2022 info->header = g_object_ref (header);
2024 info->header = NULL;
2026 info->row_reference = gtk_tree_row_reference_copy (row_reference);
2028 info->row_reference = NULL;
2030 message_reader_performer (FALSE, NULL, (GtkWindow *) window, account, info);
2031 g_object_unref (account);
2032 g_object_unref (folder);
2038 modest_msg_view_window_select_next_message (ModestMsgViewWindow *window)
2040 ModestMsgViewWindowPrivate *priv;
2041 GtkTreePath *path= NULL;
2042 GtkTreeIter tmp_iter;
2044 gboolean retval = TRUE;
2045 GtkTreeRowReference *row_reference = NULL;
2047 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), FALSE);
2048 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
2050 if (!priv->row_reference)
2053 /* Update the next row reference if it's not valid. This could
2054 happen if for example the header which it was pointing to,
2055 was deleted. The best place to do it is in the row-deleted
2056 handler but the tinymail model do not work like the glib
2057 tree models and reports the deletion when the row is still
2059 if (!gtk_tree_row_reference_valid (priv->next_row_reference)) {
2060 if (gtk_tree_row_reference_valid (priv->row_reference)) {
2061 priv->next_row_reference = gtk_tree_row_reference_copy (priv->row_reference);
2062 select_next_valid_row (priv->header_model, &(priv->next_row_reference), FALSE, priv->is_outbox);
2065 if (priv->next_row_reference)
2066 path = gtk_tree_row_reference_get_path (priv->next_row_reference);
2070 row_reference = gtk_tree_row_reference_copy (priv->next_row_reference);
2072 gtk_tree_model_get_iter (priv->header_model,
2075 gtk_tree_path_free (path);
2077 gtk_tree_model_get (priv->header_model, &tmp_iter,
2078 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
2081 /* Read the message & show it */
2082 if (!message_reader (window, priv, header, NULL, NULL, row_reference)) {
2085 gtk_tree_row_reference_free (row_reference);
2088 g_object_unref (header);
2094 modest_msg_view_window_select_previous_message (ModestMsgViewWindow *window)
2096 ModestMsgViewWindowPrivate *priv = NULL;
2098 gboolean finished = FALSE;
2099 gboolean retval = FALSE;
2101 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), FALSE);
2102 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
2104 /* Return inmediatly if there is no header model */
2105 if (!priv->header_model || !priv->row_reference)
2108 path = gtk_tree_row_reference_get_path (priv->row_reference);
2109 while (!finished && gtk_tree_path_prev (path)) {
2113 gtk_tree_model_get_iter (priv->header_model, &iter, path);
2114 gtk_tree_model_get (priv->header_model, &iter,
2115 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
2119 if (msg_is_visible (header, priv->is_outbox)) {
2120 GtkTreeRowReference *row_reference;
2121 row_reference = gtk_tree_row_reference_new (priv->header_model, path);
2122 /* Read the message & show it */
2123 retval = message_reader (window, priv, header, NULL, NULL, row_reference);
2124 gtk_tree_row_reference_free (row_reference);
2128 g_object_unref (header);
2132 gtk_tree_path_free (path);
2137 view_msg_cb (ModestMailOperation *mail_op,
2144 ModestMsgViewWindow *self = NULL;
2145 ModestMsgViewWindowPrivate *priv = NULL;
2146 GtkTreeRowReference *row_reference = NULL;
2148 /* Unregister the header (it was registered before creating the mail operation) */
2149 modest_window_mgr_unregister_header (modest_runtime_get_window_mgr (), header);
2151 row_reference = (GtkTreeRowReference *) user_data;
2154 gtk_tree_row_reference_free (row_reference);
2155 self = (ModestMsgViewWindow *) modest_mail_operation_get_source (mail_op);
2157 /* Restore window title */
2158 update_window_title (self);
2159 g_object_unref (self);
2164 /* If there was any error */
2165 if (!modest_ui_actions_msg_retrieval_check (mail_op, header, msg)) {
2167 gtk_tree_row_reference_free (row_reference);
2168 self = (ModestMsgViewWindow *) modest_mail_operation_get_source (mail_op);
2170 /* Restore window title */
2171 update_window_title (self);
2172 g_object_unref (self);
2177 /* Get the window */
2178 self = (ModestMsgViewWindow *) modest_mail_operation_get_source (mail_op);
2179 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self));
2180 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
2182 /* Update the row reference */
2183 if (priv->row_reference != NULL) {
2184 gtk_tree_row_reference_free (priv->row_reference);
2185 priv->row_reference = gtk_tree_row_reference_copy (row_reference);
2186 if (priv->next_row_reference != NULL) {
2187 gtk_tree_row_reference_free (priv->next_row_reference);
2189 priv->next_row_reference = gtk_tree_row_reference_copy (priv->row_reference);
2190 select_next_valid_row (priv->header_model, &(priv->next_row_reference), TRUE, priv->is_outbox);
2193 /* Mark header as read */
2194 if (!(tny_header_get_flags (header) & TNY_HEADER_FLAG_SEEN))
2195 tny_header_set_flag (header, TNY_HEADER_FLAG_SEEN);
2197 /* Set new message */
2198 if (priv->msg_view != NULL && TNY_IS_MSG_VIEW (priv->msg_view)) {
2199 tny_msg_view_set_msg (TNY_MSG_VIEW (priv->msg_view), msg);
2200 modest_msg_view_window_update_priority (self);
2201 update_window_title (MODEST_MSG_VIEW_WINDOW (self));
2202 update_branding (MODEST_MSG_VIEW_WINDOW (self));
2203 modest_msg_view_grab_focus (MODEST_MSG_VIEW (priv->msg_view));
2206 /* Set the new message uid of the window */
2207 if (priv->msg_uid) {
2208 g_free (priv->msg_uid);
2209 priv->msg_uid = modest_tny_folder_get_header_unique_id (header);
2212 /* Notify the observers */
2213 g_signal_emit (G_OBJECT (self), signals[MSG_CHANGED_SIGNAL],
2214 0, priv->header_model, priv->row_reference);
2217 g_object_unref (self);
2219 gtk_tree_row_reference_free (row_reference);
2223 modest_msg_view_window_get_folder_type (ModestMsgViewWindow *window)
2225 ModestMsgViewWindowPrivate *priv;
2227 TnyFolderType folder_type;
2229 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
2231 folder_type = TNY_FOLDER_TYPE_UNKNOWN;
2233 msg = tny_msg_view_get_msg (TNY_MSG_VIEW (priv->msg_view));
2237 folder = tny_msg_get_folder (msg);
2239 folder_type = modest_tny_folder_guess_folder_type (folder);
2240 g_object_unref (folder);
2242 g_object_unref (msg);
2250 modest_msg_view_window_update_priority (ModestMsgViewWindow *window)
2252 ModestMsgViewWindowPrivate *priv;
2253 TnyHeader *header = NULL;
2254 TnyHeaderFlags flags = 0;
2256 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
2258 if (priv->header_model && priv->row_reference) {
2260 GtkTreePath *path = NULL;
2262 path = gtk_tree_row_reference_get_path (priv->row_reference);
2263 g_return_if_fail (path != NULL);
2264 gtk_tree_model_get_iter (priv->header_model,
2266 gtk_tree_row_reference_get_path (priv->row_reference));
2268 gtk_tree_model_get (priv->header_model, &iter, TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
2270 gtk_tree_path_free (path);
2273 msg = tny_msg_view_get_msg (TNY_MSG_VIEW (priv->msg_view));
2275 header = tny_msg_get_header (msg);
2276 g_object_unref (msg);
2281 flags = tny_header_get_flags (header);
2282 g_object_unref(G_OBJECT(header));
2285 modest_msg_view_set_priority (MODEST_MSG_VIEW(priv->msg_view), flags);
2290 toolbar_resize (ModestMsgViewWindow *self)
2292 ModestMsgViewWindowPrivate *priv = NULL;
2293 ModestWindowPrivate *parent_priv = NULL;
2295 gint static_button_size;
2296 ModestWindowMgr *mgr;
2298 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self));
2299 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
2300 parent_priv = MODEST_WINDOW_GET_PRIVATE(self);
2302 mgr = modest_runtime_get_window_mgr ();
2303 static_button_size = modest_window_mgr_get_fullscreen_mode (mgr)?120:120;
2305 if (parent_priv->toolbar) {
2306 /* left size buttons */
2307 widget = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/ToolbarMessageReply");
2308 gtk_tool_item_set_expand (GTK_TOOL_ITEM (widget), FALSE);
2309 gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (widget), FALSE);
2310 gtk_widget_set_size_request (GTK_WIDGET (widget), static_button_size, -1);
2311 widget = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/ToolbarMessageReplyAll");
2312 gtk_tool_item_set_expand (GTK_TOOL_ITEM (widget), FALSE);
2313 gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (widget), FALSE);
2314 gtk_widget_set_size_request (GTK_WIDGET (widget), static_button_size, -1);
2315 widget = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/ToolbarDeleteMessage");
2316 gtk_tool_item_set_expand (GTK_TOOL_ITEM (widget), FALSE);
2317 gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (widget), FALSE);
2318 gtk_widget_set_size_request (GTK_WIDGET (widget), static_button_size, -1);
2319 widget = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/ToolbarDownloadExternalImages");
2320 gtk_tool_item_set_expand (GTK_TOOL_ITEM (widget), FALSE);
2321 gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (widget), FALSE);
2322 gtk_widget_set_size_request (GTK_WIDGET (widget), static_button_size, -1);
2324 gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (priv->next_toolitem), TRUE);
2325 gtk_tool_item_set_expand (GTK_TOOL_ITEM (priv->next_toolitem), TRUE);
2326 gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (priv->prev_toolitem), TRUE);
2327 gtk_tool_item_set_expand (GTK_TOOL_ITEM (priv->prev_toolitem), TRUE);
2332 modest_msg_view_window_show_toolbar (ModestWindow *self,
2333 gboolean show_toolbar)
2335 ModestMsgViewWindowPrivate *priv = NULL;
2336 ModestWindowPrivate *parent_priv;
2338 parent_priv = MODEST_WINDOW_GET_PRIVATE(self);
2339 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(self);
2341 /* Set optimized view status */
2342 priv->optimized_view = !show_toolbar;
2344 if (!parent_priv->toolbar) {
2345 parent_priv->toolbar = gtk_ui_manager_get_widget (parent_priv->ui_manager,
2347 gtk_toolbar_set_icon_size (GTK_TOOLBAR (parent_priv->toolbar), HILDON_ICON_SIZE_FINGER);
2348 gtk_widget_set_no_show_all (parent_priv->toolbar, TRUE);
2350 priv->next_toolitem = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/ToolbarMessageNext");
2351 priv->prev_toolitem = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/ToolbarMessageBack");
2352 toolbar_resize (MODEST_MSG_VIEW_WINDOW (self));
2355 hildon_window_add_toolbar (HILDON_WINDOW (self),
2356 GTK_TOOLBAR (parent_priv->toolbar));
2361 /* Quick hack: this prevents toolbar icons "dance" when progress bar show status is changed */
2362 /* TODO: resize mode migth be GTK_RESIZE_QUEUE, in order to avoid unneccesary shows */
2363 gtk_container_set_resize_mode (GTK_CONTAINER(parent_priv->toolbar), GTK_RESIZE_IMMEDIATE);
2365 gtk_widget_show (GTK_WIDGET (parent_priv->toolbar));
2366 if (modest_msg_view_window_transfer_mode_enabled (MODEST_MSG_VIEW_WINDOW (self)))
2367 set_progress_hint (MODEST_MSG_VIEW_WINDOW (self), TRUE);
2369 set_progress_hint (MODEST_MSG_VIEW_WINDOW (self), FALSE);
2372 gtk_widget_set_no_show_all (parent_priv->toolbar, TRUE);
2373 gtk_widget_hide (GTK_WIDGET (parent_priv->toolbar));
2378 modest_msg_view_window_clipboard_owner_change (GtkClipboard *clipboard,
2380 ModestMsgViewWindow *window)
2382 if (!GTK_WIDGET_VISIBLE (window))
2385 modest_window_check_dimming_rules_group (MODEST_WINDOW (window), MODEST_DIMMING_RULES_CLIPBOARD);
2389 modest_msg_view_window_transfer_mode_enabled (ModestMsgViewWindow *self)
2391 ModestMsgViewWindowPrivate *priv;
2393 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self), FALSE);
2394 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(self);
2396 return priv->progress_hint;
2400 observers_empty (ModestMsgViewWindow *self)
2403 ModestMsgViewWindowPrivate *priv;
2404 gboolean is_empty = TRUE;
2405 guint pending_ops = 0;
2407 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(self);
2408 tmp = priv->progress_widgets;
2410 /* Check all observers */
2411 while (tmp && is_empty) {
2412 pending_ops = modest_progress_object_num_pending_operations (MODEST_PROGRESS_OBJECT(tmp->data));
2413 is_empty = pending_ops == 0;
2415 tmp = g_slist_next(tmp);
2422 on_account_removed (TnyAccountStore *account_store,
2423 TnyAccount *account,
2426 /* Do nothing if it's a transport account, because we only
2427 show the messages of a store account */
2428 if (tny_account_get_account_type(account) == TNY_ACCOUNT_TYPE_STORE) {
2429 const gchar *parent_acc = NULL;
2430 const gchar *our_acc = NULL;
2432 our_acc = modest_window_get_active_account (MODEST_WINDOW (user_data));
2433 parent_acc = modest_tny_account_get_parent_modest_account_name_for_server_account (account);
2435 /* Close this window if I'm showing a message of the removed account */
2436 if (our_acc && parent_acc && strcmp (parent_acc, our_acc) == 0)
2437 modest_ui_actions_on_close_window (NULL, MODEST_WINDOW (user_data));
2442 on_mail_operation_started (ModestMailOperation *mail_op,
2445 ModestMsgViewWindow *self;
2446 ModestMailOperationTypeOperation op_type;
2448 ModestMsgViewWindowPrivate *priv;
2449 GObject *source = NULL;
2451 self = MODEST_MSG_VIEW_WINDOW (user_data);
2452 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
2453 op_type = modest_mail_operation_get_type_operation (mail_op);
2454 tmp = priv->progress_widgets;
2455 source = modest_mail_operation_get_source(mail_op);
2456 if (G_OBJECT (self) == source) {
2457 if (op_type == MODEST_MAIL_OPERATION_TYPE_RECEIVE ||
2458 op_type == MODEST_MAIL_OPERATION_TYPE_OPEN ||
2459 op_type == MODEST_MAIL_OPERATION_TYPE_DELETE) {
2460 set_progress_hint (self, TRUE);
2462 modest_progress_object_add_operation (
2463 MODEST_PROGRESS_OBJECT (tmp->data),
2465 tmp = g_slist_next (tmp);
2469 g_object_unref (source);
2471 /* Update dimming rules */
2472 check_dimming_rules_after_change (self);
2476 on_mail_operation_finished (ModestMailOperation *mail_op,
2479 ModestMsgViewWindow *self;
2480 ModestMailOperationTypeOperation op_type;
2482 ModestMsgViewWindowPrivate *priv;
2484 self = MODEST_MSG_VIEW_WINDOW (user_data);
2485 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
2486 op_type = modest_mail_operation_get_type_operation (mail_op);
2487 tmp = priv->progress_widgets;
2489 if (op_type == MODEST_MAIL_OPERATION_TYPE_RECEIVE ||
2490 op_type == MODEST_MAIL_OPERATION_TYPE_OPEN ||
2491 op_type == MODEST_MAIL_OPERATION_TYPE_DELETE) {
2493 modest_progress_object_remove_operation (MODEST_PROGRESS_OBJECT (tmp->data),
2495 tmp = g_slist_next (tmp);
2498 /* If no more operations are being observed, NORMAL mode is enabled again */
2499 if (observers_empty (self)) {
2500 set_progress_hint (self, FALSE);
2504 /* Update dimming rules. We have to do this right here
2505 and not in view_msg_cb because at that point the
2506 transfer mode is still enabled so the dimming rule
2507 won't let the user delete the message that has been
2508 readed for example */
2509 check_dimming_rules_after_change (self);
2513 on_queue_changed (ModestMailOperationQueue *queue,
2514 ModestMailOperation *mail_op,
2515 ModestMailOperationQueueNotification type,
2516 ModestMsgViewWindow *self)
2518 ModestMsgViewWindowPrivate *priv;
2520 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
2522 /* If this operations was created by another window, do nothing */
2523 if (!modest_mail_operation_is_mine (mail_op, G_OBJECT(self)))
2526 if (type == MODEST_MAIL_OPERATION_QUEUE_OPERATION_ADDED) {
2527 priv->sighandlers = modest_signal_mgr_connect (priv->sighandlers,
2529 "operation-started",
2530 G_CALLBACK (on_mail_operation_started),
2532 priv->sighandlers = modest_signal_mgr_connect (priv->sighandlers,
2534 "operation-finished",
2535 G_CALLBACK (on_mail_operation_finished),
2537 } else if (type == MODEST_MAIL_OPERATION_QUEUE_OPERATION_REMOVED) {
2538 priv->sighandlers = modest_signal_mgr_disconnect (priv->sighandlers,
2540 "operation-started");
2541 priv->sighandlers = modest_signal_mgr_disconnect (priv->sighandlers,
2543 "operation-finished");
2548 modest_msg_view_window_get_attachments (ModestMsgViewWindow *win)
2550 ModestMsgViewWindowPrivate *priv;
2551 TnyList *selected_attachments = NULL;
2553 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (win), NULL);
2554 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (win);
2556 /* In Hildon 2.2 as there's no selection we assume we have all attachments selected */
2557 selected_attachments = modest_msg_view_get_attachments (MODEST_MSG_VIEW (priv->msg_view));
2559 return selected_attachments;
2563 ModestMsgViewWindow *self;
2565 } DecodeAsyncHelper;
2568 on_decode_to_stream_async_handler (TnyMimePart *mime_part,
2574 DecodeAsyncHelper *helper = (DecodeAsyncHelper *) user_data;
2576 /* It could happen that the window was closed */
2577 if (GTK_WIDGET_VISIBLE (helper->self))
2578 set_progress_hint (helper->self, FALSE);
2580 if (cancelled || err) {
2582 gchar *msg = g_strdup_printf (_KR("cerm_device_memory_full"), "");
2583 modest_platform_information_banner (NULL, NULL, msg);
2589 /* make the file read-only */
2590 g_chmod(helper->file_path, 0444);
2592 /* Activate the file */
2593 modest_platform_activate_file (helper->file_path, tny_mime_part_get_content_type (mime_part));
2597 g_object_unref (helper->self);
2598 g_free (helper->file_path);
2599 g_slice_free (DecodeAsyncHelper, helper);
2603 modest_msg_view_window_view_attachment (ModestMsgViewWindow *window,
2604 TnyMimePart *mime_part)
2606 ModestMsgViewWindowPrivate *priv;
2607 const gchar *msg_uid;
2608 gchar *attachment_uid = NULL;
2609 gint attachment_index = 0;
2610 TnyList *attachments;
2612 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window));
2613 g_return_if_fail (TNY_IS_MIME_PART (mime_part) || (mime_part == NULL));
2614 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
2616 msg_uid = modest_msg_view_window_get_message_uid (MODEST_MSG_VIEW_WINDOW (window));
2617 attachments = modest_msg_view_get_attachments (MODEST_MSG_VIEW (priv->msg_view));
2618 attachment_index = modest_list_index (attachments, (GObject *) mime_part);
2619 g_object_unref (attachments);
2621 if (msg_uid && attachment_index >= 0) {
2622 attachment_uid = g_strdup_printf ("%s/%d", msg_uid, attachment_index);
2625 if (mime_part == NULL) {
2626 gboolean error = FALSE;
2627 TnyList *selected_attachments = modest_msg_view_get_selected_attachments (MODEST_MSG_VIEW (priv->msg_view));
2628 if (selected_attachments == NULL || tny_list_get_length (selected_attachments) == 0) {
2630 } else if (tny_list_get_length (selected_attachments) > 1) {
2631 hildon_banner_show_information (NULL, NULL, _("mcen_ib_unable_to_display_more"));
2635 iter = tny_list_create_iterator (selected_attachments);
2636 mime_part = (TnyMimePart *) tny_iterator_get_current (iter);
2637 g_object_unref (iter);
2639 if (selected_attachments)
2640 g_object_unref (selected_attachments);
2645 g_object_ref (mime_part);
2648 if (tny_mime_part_is_purged (mime_part))
2651 if (!modest_tny_mime_part_is_msg (mime_part) && tny_mime_part_get_filename (mime_part)) {
2652 gchar *filepath = NULL;
2653 const gchar *att_filename = tny_mime_part_get_filename (mime_part);
2654 gboolean show_error_banner = FALSE;
2655 TnyFsStream *temp_stream = NULL;
2656 temp_stream = modest_utils_create_temp_stream (att_filename, attachment_uid,
2659 if (temp_stream != NULL) {
2660 DecodeAsyncHelper *helper;
2662 /* Activate progress hint */
2663 set_progress_hint (window, TRUE);
2665 helper = g_slice_new0 (DecodeAsyncHelper);
2666 helper->self = g_object_ref (window);
2667 helper->file_path = g_strdup (filepath);
2669 tny_mime_part_decode_to_stream_async (mime_part, TNY_STREAM (temp_stream),
2670 on_decode_to_stream_async_handler,
2673 g_object_unref (temp_stream);
2674 /* NOTE: files in the temporary area will be automatically
2675 * cleaned after some time if they are no longer in use */
2678 const gchar *content_type;
2679 /* the file may already exist but it isn't writable,
2680 * let's try to open it anyway */
2681 content_type = tny_mime_part_get_content_type (mime_part);
2682 modest_platform_activate_file (filepath, content_type);
2684 g_warning ("%s: modest_utils_create_temp_stream failed", __FUNCTION__);
2685 show_error_banner = TRUE;
2690 if (show_error_banner)
2691 modest_platform_information_banner (NULL, NULL, _("mail_ib_file_operation_failed"));
2692 } else if (!modest_tny_mime_part_is_msg (mime_part)) {
2693 ModestWindowMgr *mgr;
2694 ModestWindow *msg_win = NULL;
2695 TnyMsg *current_msg;
2699 current_msg = modest_msg_view_window_get_message (MODEST_MSG_VIEW_WINDOW (window));
2700 mgr = modest_runtime_get_window_mgr ();
2701 header = tny_msg_get_header (TNY_MSG (current_msg));
2702 found = modest_window_mgr_find_registered_message_uid (mgr,
2707 g_debug ("window for this body is already being created");
2710 /* it's not found, so create a new window for it */
2711 modest_window_mgr_register_header (mgr, header, attachment_uid); /* register the uid before building the window */
2712 gchar *account = g_strdup (modest_window_get_active_account (MODEST_WINDOW (window)));
2713 const gchar *mailbox = modest_window_get_active_mailbox (MODEST_WINDOW (window));
2715 account = modest_account_mgr_get_default_account (modest_runtime_get_account_mgr ());
2717 msg_win = modest_msg_view_window_new_with_other_body (TNY_MSG (current_msg), TNY_MIME_PART (mime_part),
2718 account, mailbox, attachment_uid);
2720 modest_window_set_zoom (MODEST_WINDOW (msg_win),
2721 modest_window_get_zoom (MODEST_WINDOW (window)));
2722 if (modest_window_mgr_register_window (mgr, msg_win, MODEST_WINDOW (window)))
2723 gtk_widget_show_all (GTK_WIDGET (msg_win));
2725 gtk_widget_destroy (GTK_WIDGET (msg_win));
2727 g_object_unref (current_msg);
2729 /* message attachment */
2730 TnyHeader *header = NULL;
2731 ModestWindowMgr *mgr;
2732 ModestWindow *msg_win = NULL;
2735 header = tny_msg_get_header (TNY_MSG (mime_part));
2736 mgr = modest_runtime_get_window_mgr ();
2737 found = modest_window_mgr_find_registered_header (mgr, header, &msg_win);
2740 /* if it's found, but there is no msg_win, it's probably in the process of being created;
2741 * thus, we don't do anything */
2742 g_debug ("window for is already being created");
2744 /* it's not found, so create a new window for it */
2745 modest_window_mgr_register_header (mgr, header, attachment_uid); /* register the uid before building the window */
2746 gchar *account = g_strdup (modest_window_get_active_account (MODEST_WINDOW (window)));
2747 const gchar *mailbox = modest_window_get_active_mailbox (MODEST_WINDOW (window));
2749 account = modest_account_mgr_get_default_account (modest_runtime_get_account_mgr ());
2750 msg_win = modest_msg_view_window_new_for_attachment (TNY_MSG (mime_part), account,
2751 mailbox, attachment_uid);
2752 modest_window_set_zoom (MODEST_WINDOW (msg_win),
2753 modest_window_get_zoom (MODEST_WINDOW (window)));
2754 if (modest_window_mgr_register_window (mgr, msg_win, MODEST_WINDOW (window)))
2755 gtk_widget_show_all (GTK_WIDGET (msg_win));
2757 gtk_widget_destroy (GTK_WIDGET (msg_win));
2763 g_free (attachment_uid);
2765 g_object_unref (mime_part);
2777 GnomeVFSResult result;
2780 static void save_mime_part_info_free (SaveMimePartInfo *info, gboolean with_struct);
2781 static gboolean idle_save_mime_part_show_result (SaveMimePartInfo *info);
2782 static gpointer save_mime_part_to_file (SaveMimePartInfo *info);
2783 static void save_mime_parts_to_file_with_checks (GtkWindow *parent, SaveMimePartInfo *info);
2786 save_mime_part_info_free (SaveMimePartInfo *info, gboolean with_struct)
2790 for (node = info->pairs; node != NULL; node = g_list_next (node)) {
2791 SaveMimePartPair *pair = (SaveMimePartPair *) node->data;
2792 g_free (pair->filename);
2793 g_object_unref (pair->part);
2794 g_slice_free (SaveMimePartPair, pair);
2796 g_list_free (info->pairs);
2799 g_slice_free (SaveMimePartInfo, info);
2804 idle_save_mime_part_show_result (SaveMimePartInfo *info)
2806 /* This is a GDK lock because we are an idle callback and
2807 * hildon_banner_show_information is or does Gtk+ code */
2809 gdk_threads_enter (); /* CHECKED */
2810 if (info->result == GNOME_VFS_OK) {
2811 hildon_banner_show_information (NULL, NULL, _CS("sfil_ib_saved"));
2812 } else if (info->result == GNOME_VFS_ERROR_NO_SPACE) {
2813 gchar *msg = g_strdup_printf (_KR("cerm_device_memory_full"), "");
2814 modest_platform_information_banner (NULL, NULL, msg);
2817 hildon_banner_show_information (NULL, NULL, _("mail_ib_file_operation_failed"));
2819 save_mime_part_info_free (info, FALSE);
2820 gdk_threads_leave (); /* CHECKED */
2826 save_mime_part_to_file (SaveMimePartInfo *info)
2828 GnomeVFSHandle *handle;
2830 SaveMimePartPair *pair = (SaveMimePartPair *) info->pairs->data;
2832 info->result = gnome_vfs_create (&handle, pair->filename, GNOME_VFS_OPEN_WRITE, FALSE, 0644);
2833 if (info->result == GNOME_VFS_OK) {
2834 GError *error = NULL;
2835 stream = tny_vfs_stream_new (handle);
2836 if (tny_mime_part_decode_to_stream (pair->part, stream, &error) < 0) {
2837 g_warning ("modest: could not save attachment %s: %d (%s)\n", pair->filename, error?error->code:-1, error?error->message:"Unknown error");
2839 if ((error->domain == TNY_ERROR_DOMAIN) &&
2840 (error->code == TNY_IO_ERROR_WRITE) &&
2841 (errno == ENOSPC)) {
2842 info->result = GNOME_VFS_ERROR_NO_SPACE;
2844 info->result = GNOME_VFS_ERROR_IO;
2847 g_object_unref (G_OBJECT (stream));
2849 g_warning ("Could not create save attachment %s: %s\n",
2850 pair->filename, gnome_vfs_result_to_string (info->result));
2853 /* Go on saving remaining files */
2854 info->pairs = g_list_remove_link (info->pairs, info->pairs);
2855 if (info->pairs != NULL) {
2856 save_mime_part_to_file (info);
2858 g_idle_add ((GSourceFunc) idle_save_mime_part_show_result, info);
2865 save_mime_parts_to_file_with_checks (GtkWindow *parent,
2866 SaveMimePartInfo *info)
2868 gboolean is_ok = TRUE;
2869 gint replaced_files = 0;
2870 const GList *files = info->pairs;
2871 const GList *iter, *to_replace = NULL;
2873 for (iter = files; (iter != NULL) && (replaced_files < 2); iter = g_list_next(iter)) {
2874 SaveMimePartPair *pair = iter->data;
2875 if (modest_utils_file_exists (pair->filename)) {
2877 if (replaced_files == 1)
2881 if (replaced_files) {
2884 if (replaced_files == 1) {
2885 SaveMimePartPair *pair = to_replace->data;
2886 const gchar *basename = strrchr (pair->filename, G_DIR_SEPARATOR) + 1;
2887 gchar *escaped_basename, *message;
2889 escaped_basename = g_uri_unescape_string (basename, NULL);
2890 message = g_strdup_printf ("%s\n%s",
2891 _FM("docm_nc_replace_file"),
2892 (escaped_basename) ? escaped_basename : "");
2893 response = modest_platform_run_confirmation_dialog (parent, message);
2895 g_free (escaped_basename);
2897 response = modest_platform_run_confirmation_dialog (parent,
2898 _FM("docm_nc_replace_multiple"));
2900 if (response != GTK_RESPONSE_OK)
2905 save_mime_part_info_free (info, TRUE);
2907 g_thread_create ((GThreadFunc)save_mime_part_to_file, info, FALSE, NULL);
2913 save_attachments_response (GtkDialog *dialog,
2917 TnyList *mime_parts;
2919 GList *files_to_save = NULL;
2920 gchar *current_folder;
2922 mime_parts = TNY_LIST (user_data);
2924 if (arg1 != GTK_RESPONSE_OK)
2927 chooser_uri = gtk_file_chooser_get_uri (GTK_FILE_CHOOSER (dialog));
2928 current_folder = gtk_file_chooser_get_current_folder_uri (GTK_FILE_CHOOSER (dialog));
2929 if (current_folder && current_folder != '\0') {
2931 modest_conf_set_string (modest_runtime_get_conf (), MODEST_CONF_LATEST_SAVE_ATTACHMENT_PATH,
2932 current_folder,&err);
2934 g_debug ("Error storing latest used folder: %s", err->message);
2938 g_free (current_folder);
2940 if (!modest_utils_folder_writable (chooser_uri)) {
2941 hildon_banner_show_information
2942 (NULL, NULL, _FM("sfil_ib_readonly_location"));
2946 iter = tny_list_create_iterator (mime_parts);
2947 while (!tny_iterator_is_done (iter)) {
2948 TnyMimePart *mime_part = (TnyMimePart *) tny_iterator_get_current (iter);
2950 if ((modest_tny_mime_part_is_attachment_for_modest (mime_part)) &&
2951 !tny_mime_part_is_purged (mime_part) &&
2952 (tny_mime_part_get_filename (mime_part) != NULL)) {
2953 SaveMimePartPair *pair;
2955 pair = g_slice_new0 (SaveMimePartPair);
2957 if (tny_list_get_length (mime_parts) > 1) {
2959 gnome_vfs_escape_slashes (tny_mime_part_get_filename (mime_part));
2960 pair->filename = g_build_filename (chooser_uri, escaped, NULL);
2963 pair->filename = g_strdup (chooser_uri);
2965 pair->part = mime_part;
2966 files_to_save = g_list_prepend (files_to_save, pair);
2968 tny_iterator_next (iter);
2970 g_object_unref (iter);
2972 g_free (chooser_uri);
2974 if (files_to_save != NULL) {
2975 SaveMimePartInfo *info = g_slice_new0 (SaveMimePartInfo);
2976 info->pairs = files_to_save;
2977 info->result = TRUE;
2978 save_mime_parts_to_file_with_checks ((GtkWindow *) dialog, info);
2982 /* Free and close the dialog */
2983 g_object_unref (mime_parts);
2984 gtk_widget_destroy (GTK_WIDGET (dialog));
2988 modest_msg_view_window_save_attachments (ModestMsgViewWindow *window,
2989 TnyList *mime_parts)
2991 ModestMsgViewWindowPrivate *priv;
2992 GtkWidget *save_dialog = NULL;
2993 gchar *conf_folder = NULL;
2994 gchar *filename = NULL;
2995 gchar *save_multiple_str = NULL;
2997 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window));
2998 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
3000 if (mime_parts == NULL) {
3001 /* In Hildon 2.2 save and delete operate over all the attachments as there's no
3002 * selection available */
3003 mime_parts = modest_msg_view_get_attachments (MODEST_MSG_VIEW (priv->msg_view));
3004 if (mime_parts && !modest_maemo_utils_select_attachments (GTK_WINDOW (window), mime_parts, FALSE)) {
3005 g_object_unref (mime_parts);
3008 if (mime_parts == NULL || tny_list_get_length (mime_parts) == 0) {
3010 g_object_unref (mime_parts);
3016 g_object_ref (mime_parts);
3019 /* prepare dialog */
3020 if (tny_list_get_length (mime_parts) == 1) {
3022 /* only one attachment selected */
3023 iter = tny_list_create_iterator (mime_parts);
3024 TnyMimePart *mime_part = (TnyMimePart *) tny_iterator_get_current (iter);
3025 g_object_unref (iter);
3026 if (!modest_tny_mime_part_is_msg (mime_part) &&
3027 modest_tny_mime_part_is_attachment_for_modest (mime_part) &&
3028 !tny_mime_part_is_purged (mime_part)) {
3029 filename = g_strdup (tny_mime_part_get_filename (mime_part));
3031 /* TODO: show any error? */
3032 g_warning ("%s: Tried to save a non-file attachment", __FUNCTION__);
3033 g_object_unref (mime_parts);
3036 g_object_unref (mime_part);
3038 gint num = tny_list_get_length (mime_parts);
3039 save_multiple_str = g_strdup_printf (dngettext("hildon-fm",
3040 "sfil_va_number_of_objects_attachment",
3041 "sfil_va_number_of_objects_attachments",
3045 save_dialog = hildon_file_chooser_dialog_new (GTK_WINDOW (window),
3046 GTK_FILE_CHOOSER_ACTION_SAVE);
3049 conf_folder = modest_conf_get_string (modest_runtime_get_conf (), MODEST_CONF_LATEST_SAVE_ATTACHMENT_PATH, NULL);
3050 if (conf_folder && conf_folder[0] != '\0') {
3051 gtk_file_chooser_set_current_folder_uri (GTK_FILE_CHOOSER (save_dialog), conf_folder);
3054 /* Set the default folder to images folder */
3055 docs_folder = g_build_filename (g_getenv (MYDOCS_ENV), DOCS_FOLDER, NULL);
3056 gtk_file_chooser_set_current_folder (GTK_FILE_CHOOSER (save_dialog), docs_folder);
3057 g_free (docs_folder);
3059 g_free (conf_folder);
3063 gtk_file_chooser_set_current_name (GTK_FILE_CHOOSER (save_dialog),
3068 /* if multiple, set multiple string */
3069 if (save_multiple_str) {
3070 g_object_set (G_OBJECT (save_dialog), "save-multiple", save_multiple_str, NULL);
3071 gtk_window_set_title (GTK_WINDOW (save_dialog), _FM("sfil_ti_save_objects_files"));
3074 /* We must run this asynchronously, because the hildon dialog
3075 performs a gtk_dialog_run by itself which leads to gdk
3077 g_signal_connect (save_dialog, "response",
3078 G_CALLBACK (save_attachments_response), mime_parts);
3080 gtk_widget_show_all (save_dialog);
3084 show_remove_attachment_information (gpointer userdata)
3086 ModestMsgViewWindow *window = (ModestMsgViewWindow *) userdata;
3087 ModestMsgViewWindowPrivate *priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
3089 /* We're outside the main lock */
3090 gdk_threads_enter ();
3092 if (priv->remove_attachment_banner != NULL) {
3093 gtk_widget_destroy (priv->remove_attachment_banner);
3094 g_object_unref (priv->remove_attachment_banner);
3097 priv->remove_attachment_banner = g_object_ref (
3098 hildon_banner_show_animation (NULL, NULL, _("mcen_me_inbox_remove_attachments")));
3100 gdk_threads_leave ();
3106 modest_msg_view_window_remove_attachments (ModestMsgViewWindow *window, gboolean get_all)
3108 ModestMsgViewWindowPrivate *priv;
3109 TnyList *mime_parts = NULL, *tmp;
3110 gchar *confirmation_message;
3116 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window));
3117 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
3119 /* In hildon 2.2 we ignore the get_all flag as we always get all attachments. This is
3120 * because we don't have selection
3122 mime_parts = modest_msg_view_get_attachments (MODEST_MSG_VIEW (priv->msg_view));
3124 /* Remove already purged messages from mime parts list. We use
3125 a copy of the list to remove items in the original one */
3126 tmp = tny_list_copy (mime_parts);
3127 iter = tny_list_create_iterator (tmp);
3128 while (!tny_iterator_is_done (iter)) {
3129 TnyMimePart *part = TNY_MIME_PART (tny_iterator_get_current (iter));
3130 if (tny_mime_part_is_purged (part))
3131 tny_list_remove (mime_parts, (GObject *) part);
3133 g_object_unref (part);
3134 tny_iterator_next (iter);
3136 g_object_unref (tmp);
3137 g_object_unref (iter);
3139 if (!modest_maemo_utils_select_attachments (GTK_WINDOW (window), mime_parts, TRUE) ||
3140 tny_list_get_length (mime_parts) == 0) {
3141 g_object_unref (mime_parts);
3145 n_attachments = tny_list_get_length (mime_parts);
3146 if (n_attachments == 1) {
3150 iter = tny_list_create_iterator (mime_parts);
3151 part = (TnyMimePart *) tny_iterator_get_current (iter);
3152 g_object_unref (iter);
3153 if (modest_tny_mime_part_is_msg (part)) {
3155 header = tny_msg_get_header (TNY_MSG (part));
3156 filename = tny_header_dup_subject (header);
3157 g_object_unref (header);
3158 if (filename == NULL)
3159 filename = g_strdup (_("mail_va_no_subject"));
3161 filename = g_strdup (tny_mime_part_get_filename (TNY_MIME_PART (part)));
3163 confirmation_message = g_strdup_printf (_("mcen_nc_purge_file_text"), filename);
3165 g_object_unref (part);
3167 confirmation_message = g_strdup_printf (ngettext("mcen_nc_purge_file_text",
3168 "mcen_nc_purge_files_text",
3169 n_attachments), n_attachments);
3171 response = modest_platform_run_confirmation_dialog (GTK_WINDOW (window),
3172 confirmation_message);
3173 g_free (confirmation_message);
3175 if (response != GTK_RESPONSE_OK) {
3176 g_object_unref (mime_parts);
3180 priv->purge_timeout = g_timeout_add (2000, show_remove_attachment_information, window);
3182 iter = tny_list_create_iterator (mime_parts);
3183 while (!tny_iterator_is_done (iter)) {
3186 part = (TnyMimePart *) tny_iterator_get_current (iter);
3187 tny_mime_part_set_purged (TNY_MIME_PART (part));
3188 g_object_unref (part);
3189 tny_iterator_next (iter);
3191 g_object_unref (iter);
3193 msg = tny_msg_view_get_msg (TNY_MSG_VIEW (priv->msg_view));
3194 tny_msg_view_clear (TNY_MSG_VIEW (priv->msg_view));
3195 tny_msg_rewrite_cache (msg);
3196 tny_msg_view_set_msg (TNY_MSG_VIEW (priv->msg_view), msg);
3197 g_object_unref (msg);
3198 update_branding (MODEST_MSG_VIEW_WINDOW (window));
3200 g_object_unref (mime_parts);
3202 if (priv->purge_timeout > 0) {
3203 g_source_remove (priv->purge_timeout);
3204 priv->purge_timeout = 0;
3207 if (priv->remove_attachment_banner) {
3208 gtk_widget_destroy (priv->remove_attachment_banner);
3209 g_object_unref (priv->remove_attachment_banner);
3210 priv->remove_attachment_banner = NULL;
3216 update_window_title (ModestMsgViewWindow *window)
3218 ModestMsgViewWindowPrivate *priv;
3220 TnyHeader *header = NULL;
3221 gchar *subject = NULL;
3223 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
3225 /* Note that if the window is closed while we're retrieving
3226 the message, this widget could de deleted */
3227 if (!priv->msg_view)
3230 msg = tny_msg_view_get_msg (TNY_MSG_VIEW (priv->msg_view));
3232 if (priv->other_body) {
3235 description = modest_tny_mime_part_get_header_value (priv->other_body, "Content-Description");
3237 g_strstrip (description);
3238 subject = description;
3240 } else if (msg != NULL) {
3241 header = tny_msg_get_header (msg);
3242 subject = tny_header_dup_subject (header);
3243 g_object_unref (header);
3244 g_object_unref (msg);
3247 if ((subject == NULL)||(subject[0] == '\0')) {
3249 subject = g_strdup (_("mail_va_no_subject"));
3252 gtk_window_set_title (GTK_WINDOW (window), subject);
3257 on_move_focus (GtkWidget *widget,
3258 GtkDirectionType direction,
3261 g_signal_stop_emission_by_name (G_OBJECT (widget), "move-focus");
3265 fetch_image_open_stream (TnyStreamCache *self, gint64 *expected_size, gchar *uri)
3267 GnomeVFSResult result;
3268 GnomeVFSHandle *handle = NULL;
3269 GnomeVFSFileInfo *info = NULL;
3272 result = gnome_vfs_open (&handle, uri, GNOME_VFS_OPEN_READ);
3273 if (result != GNOME_VFS_OK) {
3278 info = gnome_vfs_file_info_new ();
3279 result = gnome_vfs_get_file_info_from_handle (handle, info, GNOME_VFS_FILE_INFO_DEFAULT);
3280 if (result != GNOME_VFS_OK || ! (info->valid_fields & GNOME_VFS_FILE_INFO_FIELDS_SIZE)) {
3281 /* We put a "safe" default size for going to cache */
3282 *expected_size = (300*1024);
3284 *expected_size = info->size;
3286 gnome_vfs_file_info_unref (info);
3288 stream = tny_vfs_stream_new (handle);
3297 TnyStream *output_stream;
3298 GtkWidget *msg_view;
3303 on_fetch_image_idle_refresh_view (gpointer userdata)
3306 FetchImageData *fidata = (FetchImageData *) userdata;
3308 gdk_threads_enter ();
3309 if (GTK_WIDGET_DRAWABLE (fidata->msg_view)) {
3310 ModestMsgViewWindowPrivate *priv;
3312 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (fidata->window);
3313 priv->fetching_images--;
3314 gtk_widget_queue_draw (fidata->msg_view);
3315 update_progress_hint (MODEST_MSG_VIEW_WINDOW (fidata->window));
3317 gdk_threads_leave ();
3319 g_object_unref (fidata->msg_view);
3320 g_object_unref (fidata->window);
3321 g_slice_free (FetchImageData, fidata);
3326 on_fetch_image_thread (gpointer userdata)
3328 FetchImageData *fidata = (FetchImageData *) userdata;
3329 TnyStreamCache *cache;
3330 TnyStream *cache_stream;
3332 cache = modest_runtime_get_images_cache ();
3334 tny_stream_cache_get_stream (cache,
3336 (TnyStreamCacheOpenStreamFetcher) fetch_image_open_stream,
3337 (gpointer) fidata->uri);
3338 g_free (fidata->cache_id);
3339 g_free (fidata->uri);
3341 if (cache_stream != NULL) {
3344 while (G_LIKELY (!tny_stream_is_eos (cache_stream))) {
3347 nb_read = tny_stream_read (cache_stream, buffer, sizeof (buffer));
3348 if (G_UNLIKELY (nb_read < 0)) {
3350 } else if (G_LIKELY (nb_read > 0)) {
3351 gssize nb_written = 0;
3353 while (G_UNLIKELY (nb_written < nb_read)) {
3356 len = tny_stream_write (fidata->output_stream, buffer + nb_written,
3357 nb_read - nb_written);
3358 if (G_UNLIKELY (len < 0))
3364 tny_stream_close (cache_stream);
3365 g_object_unref (cache_stream);
3368 tny_stream_close (fidata->output_stream);
3369 g_object_unref (fidata->output_stream);
3371 g_idle_add (on_fetch_image_idle_refresh_view, fidata);
3377 on_fetch_image (ModestMsgView *msgview,
3380 ModestMsgViewWindow *window)
3382 const gchar *current_account;
3383 ModestMsgViewWindowPrivate *priv;
3384 FetchImageData *fidata;
3386 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
3388 current_account = modest_window_get_active_account (MODEST_WINDOW (window));
3390 fidata = g_slice_new0 (FetchImageData);
3391 fidata->msg_view = g_object_ref (msgview);
3392 fidata->window = g_object_ref (window);
3393 fidata->uri = g_strdup (uri);
3394 fidata->cache_id = modest_images_cache_get_id (current_account, uri);
3395 fidata->output_stream = g_object_ref (stream);
3397 priv->fetching_images++;
3398 if (g_thread_create (on_fetch_image_thread, fidata, FALSE, NULL) == NULL) {
3399 g_object_unref (fidata->output_stream);
3400 g_free (fidata->cache_id);
3401 g_free (fidata->uri);
3402 g_object_unref (fidata->msg_view);
3403 g_slice_free (FetchImageData, fidata);
3404 tny_stream_close (stream);
3405 priv->fetching_images--;
3406 update_progress_hint (window);
3409 update_progress_hint (window);
3415 setup_menu (ModestMsgViewWindow *self)
3417 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW(self));
3419 /* Settings menu buttons */
3420 modest_hildon2_window_add_to_menu (MODEST_HILDON2_WINDOW (self), _("mcen_me_viewer_find"), NULL,
3421 APP_MENU_CALLBACK (modest_msg_view_window_show_find_toolbar),
3422 MODEST_DIMMING_CALLBACK (modest_ui_dimming_rules_on_find_in_msg));
3424 modest_hildon2_window_add_to_menu (MODEST_HILDON2_WINDOW (self), _("mcen_me_move_messages"), NULL,
3425 APP_MENU_CALLBACK (modest_ui_actions_on_move_to),
3426 MODEST_DIMMING_CALLBACK (modest_ui_dimming_rules_on_move_to));
3428 modest_hildon2_window_add_to_menu (MODEST_HILDON2_WINDOW (self), _("mcen_me_inbox_forward"), "<Control>d",
3429 APP_MENU_CALLBACK (modest_ui_actions_on_forward),
3430 MODEST_DIMMING_CALLBACK (modest_ui_dimming_rules_on_reply_msg));
3432 modest_hildon2_window_add_to_menu (MODEST_HILDON2_WINDOW (self), _("mcen_me_inbox_mark_as_read"), NULL,
3433 APP_MENU_CALLBACK (modest_ui_actions_on_mark_as_read),
3434 MODEST_DIMMING_CALLBACK (modest_ui_dimming_rules_on_mark_as_read_msg_in_view));
3436 modest_hildon2_window_add_to_menu (MODEST_HILDON2_WINDOW (self), _("mcen_me_inbox_mark_as_unread"), NULL,
3437 APP_MENU_CALLBACK (modest_ui_actions_on_mark_as_unread),
3438 MODEST_DIMMING_CALLBACK (modest_ui_dimming_rules_on_mark_as_unread_msg_in_view));
3440 modest_hildon2_window_add_to_menu (MODEST_HILDON2_WINDOW (self), _("mcen_me_viewer_save_attachments"), NULL,
3441 APP_MENU_CALLBACK (modest_ui_actions_save_attachments),
3442 MODEST_DIMMING_CALLBACK (modest_ui_dimming_rules_on_save_attachments));
3443 modest_hildon2_window_add_to_menu (MODEST_HILDON2_WINDOW (self), _("mcen_me_inbox_remove_attachments"), NULL,
3444 APP_MENU_CALLBACK (modest_ui_actions_remove_attachments),
3445 MODEST_DIMMING_CALLBACK (modest_ui_dimming_rules_on_remove_attachments));
3447 modest_hildon2_window_add_to_menu (MODEST_HILDON2_WINDOW (self), _("mcen_me_new_message"), "<Control>n",
3448 APP_MENU_CALLBACK (modest_ui_actions_on_new_msg),
3449 MODEST_DIMMING_CALLBACK (modest_ui_dimming_rules_on_new_msg));
3450 modest_hildon2_window_add_to_menu (MODEST_HILDON2_WINDOW (self), _("mcen_me_viewer_addtocontacts"), NULL,
3451 APP_MENU_CALLBACK (modest_ui_actions_add_to_contacts),
3452 MODEST_DIMMING_CALLBACK (modest_ui_dimming_rules_on_add_to_contacts));
3454 modest_hildon2_window_add_to_menu (MODEST_HILDON2_WINDOW (self), _("mcen_ti_message_properties"), NULL,
3455 APP_MENU_CALLBACK (modest_ui_actions_on_details),
3456 MODEST_DIMMING_CALLBACK (modest_ui_dimming_rules_on_details));
3460 modest_msg_view_window_add_to_contacts (ModestMsgViewWindow *self)
3462 ModestMsgViewWindowPrivate *priv;
3463 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
3464 GSList *recipients = NULL;
3466 gboolean contacts_to_add = FALSE;
3468 msg = tny_msg_view_get_msg (TNY_MSG_VIEW (priv->msg_view));
3472 header = modest_msg_view_window_get_header (self);
3475 recipients = modest_tny_msg_header_get_all_recipients_list (header);
3476 g_object_unref (header);
3478 recipients = modest_tny_msg_get_all_recipients_list (msg);
3479 g_object_unref (msg);
3482 if (recipients != NULL) {
3483 GtkWidget *picker_dialog;
3484 GtkWidget *selector;
3486 gchar *selected = NULL;
3488 selector = hildon_touch_selector_new_text ();
3489 g_object_ref (selector);
3491 for (node = recipients; node != NULL; node = g_slist_next (node)) {
3492 if (!modest_address_book_has_address ((const gchar *) node->data)) {
3493 hildon_touch_selector_append_text (HILDON_TOUCH_SELECTOR (selector),
3494 (const gchar *) node->data);
3495 contacts_to_add = TRUE;
3499 if (contacts_to_add) {
3502 picker_dialog = hildon_picker_dialog_new (GTK_WINDOW (self));
3503 gtk_window_set_title (GTK_WINDOW (picker_dialog), _("mcen_me_viewer_addtocontacts"));
3505 hildon_picker_dialog_set_selector (HILDON_PICKER_DIALOG (picker_dialog),
3506 HILDON_TOUCH_SELECTOR (selector));
3508 picker_result = gtk_dialog_run (GTK_DIALOG (picker_dialog));
3510 if (picker_result == GTK_RESPONSE_OK) {
3511 selected = hildon_touch_selector_get_current_text (HILDON_TOUCH_SELECTOR (selector));
3513 gtk_widget_destroy (picker_dialog);
3516 modest_address_book_add_address (selected);
3521 g_object_unref (selector);
3526 if (recipients) {g_slist_foreach (recipients, (GFunc) g_free, NULL); g_slist_free (recipients);}
3530 _modest_msg_view_window_map_event (GtkWidget *widget,
3534 ModestMsgViewWindow *self = (ModestMsgViewWindow *) userdata;
3536 update_progress_hint (self);
3542 modest_msg_view_window_fetch_images (ModestMsgViewWindow *self)
3544 ModestMsgViewWindowPrivate *priv;
3545 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
3547 modest_msg_view_request_fetch_images (MODEST_MSG_VIEW (priv->msg_view));
3551 modest_msg_view_window_has_blocked_external_images (ModestMsgViewWindow *self)
3553 ModestMsgViewWindowPrivate *priv;
3554 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
3556 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self), FALSE);
3558 return modest_msg_view_has_blocked_external_images (MODEST_MSG_VIEW (priv->msg_view));
3562 modest_msg_view_window_reload (ModestMsgViewWindow *self)
3564 ModestMsgViewWindowPrivate *priv;
3567 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self));
3569 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
3570 header = modest_msg_view_window_get_header (MODEST_MSG_VIEW_WINDOW (self));
3572 if (!message_reader (self, priv, header, NULL, NULL, priv->row_reference)) {
3573 g_warning ("Shouldn't happen, trying to reload a message failed");
3576 g_object_unref (header);
3580 update_branding (ModestMsgViewWindow *self)
3582 const gchar *account;
3583 const gchar *mailbox;
3584 ModestAccountMgr *mgr;
3585 ModestProtocol *protocol = NULL;
3586 gchar *service_name = NULL;
3587 const GdkPixbuf *service_icon = NULL;
3588 ModestMsgViewWindowPrivate *priv;
3590 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
3592 account = modest_window_get_active_account (MODEST_WINDOW (self));
3593 mailbox = modest_window_get_active_mailbox (MODEST_WINDOW (self));
3595 mgr = modest_runtime_get_account_mgr ();
3597 if (modest_account_mgr_account_is_multimailbox (mgr, account, &protocol)) {
3598 if (MODEST_IS_ACCOUNT_PROTOCOL (protocol)) {
3599 service_name = modest_account_protocol_get_service_name (MODEST_ACCOUNT_PROTOCOL (protocol),
3601 service_icon = modest_account_protocol_get_service_icon (MODEST_ACCOUNT_PROTOCOL (protocol),
3602 account, mailbox, MODEST_ICON_SIZE_SMALL);
3606 modest_msg_view_set_branding (MODEST_MSG_VIEW (priv->msg_view), service_name, service_icon);
3607 g_free (service_name);