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;
398 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(obj);
399 parent_priv = MODEST_WINDOW_GET_PRIVATE(obj);
400 parent_priv->ui_manager = gtk_ui_manager_new();
402 action_group = gtk_action_group_new ("ModestMsgViewWindowActions");
403 gtk_action_group_set_translation_domain (action_group, GETTEXT_PACKAGE);
405 /* Add common actions */
406 gtk_action_group_add_actions (action_group,
407 modest_action_entries,
408 G_N_ELEMENTS (modest_action_entries),
410 gtk_action_group_add_toggle_actions (action_group,
411 msg_view_toggle_action_entries,
412 G_N_ELEMENTS (msg_view_toggle_action_entries),
415 gtk_ui_manager_insert_action_group (parent_priv->ui_manager, action_group, 0);
416 g_object_unref (action_group);
418 /* Load the UI definition */
419 gtk_ui_manager_add_ui_from_file (parent_priv->ui_manager, MODEST_UIDIR "modest-msg-view-window-ui.xml",
422 g_printerr ("modest: could not merge modest-msg-view-window-ui.xml: %s\n", error->message);
423 g_error_free (error);
428 /* Add accelerators */
429 gtk_window_add_accel_group (GTK_WINDOW (obj),
430 gtk_ui_manager_get_accel_group (parent_priv->ui_manager));
432 priv->is_search_result = FALSE;
433 priv->is_outbox = FALSE;
435 priv->msg_view = NULL;
436 priv->header_model = NULL;
437 priv->header_folder_id = NULL;
438 priv->clipboard_change_handler = 0;
439 priv->queue_change_handler = 0;
440 priv->account_removed_handler = 0;
441 priv->row_changed_handler = 0;
442 priv->row_deleted_handler = 0;
443 priv->row_inserted_handler = 0;
444 priv->rows_reordered_handler = 0;
445 priv->progress_hint = FALSE;
446 priv->fetching_images = 0;
448 priv->optimized_view = FALSE;
449 priv->purge_timeout = 0;
450 priv->remove_attachment_banner = NULL;
451 priv->msg_uid = NULL;
452 priv->other_body = NULL;
454 priv->sighandlers = NULL;
457 init_window (MODEST_MSG_VIEW_WINDOW(obj));
459 hildon_program_add_window (hildon_program_get_instance(),
465 update_progress_hint (ModestMsgViewWindow *self)
467 ModestMsgViewWindowPrivate *priv;
468 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(self);
470 if (GTK_WIDGET_VISIBLE (self)) {
471 hildon_gtk_window_set_progress_indicator (GTK_WINDOW (self),
472 (priv->progress_hint || (priv->fetching_images > 0))?1:0);
477 set_progress_hint (ModestMsgViewWindow *self,
480 ModestWindowPrivate *parent_priv;
481 ModestMsgViewWindowPrivate *priv;
483 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self));
485 parent_priv = MODEST_WINDOW_GET_PRIVATE(self);
486 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(self);
488 /* Sets current progress hint */
489 priv->progress_hint = enabled;
491 update_progress_hint (self);
497 init_window (ModestMsgViewWindow *obj)
499 GtkWidget *main_vbox;
500 ModestMsgViewWindowPrivate *priv;
502 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(obj);
504 priv->msg_view = GTK_WIDGET (tny_platform_factory_new_msg_view (modest_tny_platform_factory_get_instance ()));
505 modest_msg_view_set_shadow_type (MODEST_MSG_VIEW (priv->msg_view), GTK_SHADOW_NONE);
506 main_vbox = gtk_vbox_new (FALSE, 6);
507 priv->main_scroll = hildon_pannable_area_new ();
508 g_object_set (G_OBJECT (priv->main_scroll),
509 "mov-mode", HILDON_MOVEMENT_MODE_BOTH,
512 gtk_container_add (GTK_CONTAINER (priv->main_scroll), priv->msg_view);
513 gtk_box_pack_start (GTK_BOX(main_vbox), priv->main_scroll, TRUE, TRUE, 0);
514 gtk_container_add (GTK_CONTAINER(obj), main_vbox);
516 /* NULL-ize fields if the window is destroyed */
517 g_signal_connect (priv->msg_view, "destroy", G_CALLBACK (gtk_widget_destroyed), &(priv->msg_view));
519 gtk_widget_show_all (GTK_WIDGET(main_vbox));
523 modest_msg_view_window_disconnect_signals (ModestWindow *self)
525 ModestMsgViewWindowPrivate *priv;
526 GtkWidget *header_view = NULL;
527 GtkWindow *parent_window = NULL;
529 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
531 if (gtk_clipboard_get (GDK_SELECTION_PRIMARY) &&
532 g_signal_handler_is_connected (gtk_clipboard_get (GDK_SELECTION_PRIMARY),
533 priv->clipboard_change_handler))
534 g_signal_handler_disconnect (gtk_clipboard_get (GDK_SELECTION_PRIMARY),
535 priv->clipboard_change_handler);
537 if (g_signal_handler_is_connected (G_OBJECT (modest_runtime_get_mail_operation_queue ()),
538 priv->queue_change_handler))
539 g_signal_handler_disconnect (G_OBJECT (modest_runtime_get_mail_operation_queue ()),
540 priv->queue_change_handler);
542 if (g_signal_handler_is_connected (G_OBJECT (modest_runtime_get_account_store ()),
543 priv->account_removed_handler))
544 g_signal_handler_disconnect (G_OBJECT (modest_runtime_get_account_store ()),
545 priv->account_removed_handler);
547 if (priv->header_model) {
548 if (g_signal_handler_is_connected(G_OBJECT (priv->header_model),
549 priv->row_changed_handler))
550 g_signal_handler_disconnect(G_OBJECT (priv->header_model),
551 priv->row_changed_handler);
553 if (g_signal_handler_is_connected(G_OBJECT (priv->header_model),
554 priv->row_deleted_handler))
555 g_signal_handler_disconnect(G_OBJECT (priv->header_model),
556 priv->row_deleted_handler);
558 if (g_signal_handler_is_connected(G_OBJECT (priv->header_model),
559 priv->row_inserted_handler))
560 g_signal_handler_disconnect(G_OBJECT (priv->header_model),
561 priv->row_inserted_handler);
563 if (g_signal_handler_is_connected(G_OBJECT (priv->header_model),
564 priv->rows_reordered_handler))
565 g_signal_handler_disconnect(G_OBJECT (priv->header_model),
566 priv->rows_reordered_handler);
569 modest_signal_mgr_disconnect_all_and_destroy (priv->sighandlers);
570 priv->sighandlers = NULL;
572 parent_window = gtk_window_get_transient_for (GTK_WINDOW (self));
573 if (parent_window && MODEST_IS_HEADER_WINDOW (parent_window)) {
574 header_view = GTK_WIDGET (modest_header_window_get_header_view (MODEST_HEADER_WINDOW (parent_window)));
576 modest_header_view_remove_observer(MODEST_HEADER_VIEW (header_view),
577 MODEST_HEADER_VIEW_OBSERVER(self));
583 modest_msg_view_window_finalize (GObject *obj)
585 ModestMsgViewWindowPrivate *priv;
587 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (obj);
589 /* Sanity check: shouldn't be needed, the window mgr should
590 call this function before */
591 modest_msg_view_window_disconnect_signals (MODEST_WINDOW (obj));
593 if (priv->other_body != NULL) {
594 g_object_unref (priv->other_body);
595 priv->other_body = NULL;
598 if (priv->header_model != NULL) {
599 g_object_unref (priv->header_model);
600 priv->header_model = NULL;
603 if (priv->remove_attachment_banner) {
604 gtk_widget_destroy (priv->remove_attachment_banner);
605 g_object_unref (priv->remove_attachment_banner);
606 priv->remove_attachment_banner = NULL;
609 if (priv->purge_timeout > 0) {
610 g_source_remove (priv->purge_timeout);
611 priv->purge_timeout = 0;
614 if (priv->row_reference) {
615 gtk_tree_row_reference_free (priv->row_reference);
616 priv->row_reference = NULL;
619 if (priv->next_row_reference) {
620 gtk_tree_row_reference_free (priv->next_row_reference);
621 priv->next_row_reference = NULL;
625 g_free (priv->msg_uid);
626 priv->msg_uid = NULL;
629 G_OBJECT_CLASS(parent_class)->finalize (obj);
633 select_next_valid_row (GtkTreeModel *model,
634 GtkTreeRowReference **row_reference,
638 GtkTreeIter tmp_iter;
640 GtkTreePath *next = NULL;
641 gboolean retval = FALSE, finished;
643 g_return_val_if_fail (gtk_tree_row_reference_valid (*row_reference), FALSE);
645 path = gtk_tree_row_reference_get_path (*row_reference);
646 gtk_tree_model_get_iter (model, &tmp_iter, path);
647 gtk_tree_row_reference_free (*row_reference);
648 *row_reference = NULL;
652 TnyHeader *header = NULL;
654 if (gtk_tree_model_iter_next (model, &tmp_iter)) {
655 gtk_tree_model_get (model, &tmp_iter,
656 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
660 if (msg_is_visible (header, is_outbox)) {
661 next = gtk_tree_model_get_path (model, &tmp_iter);
662 *row_reference = gtk_tree_row_reference_new (model, next);
663 gtk_tree_path_free (next);
667 g_object_unref (header);
670 } else if (cycle && gtk_tree_model_get_iter_first (model, &tmp_iter)) {
671 next = gtk_tree_model_get_path (model, &tmp_iter);
673 /* Ensure that we are not selecting the same */
674 if (gtk_tree_path_compare (path, next) != 0) {
675 gtk_tree_model_get (model, &tmp_iter,
676 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
679 if (msg_is_visible (header, is_outbox)) {
680 *row_reference = gtk_tree_row_reference_new (model, next);
684 g_object_unref (header);
688 /* If we ended up in the same message
689 then there is no valid next
693 gtk_tree_path_free (next);
695 /* If there are no more messages and we don't
696 want to start again in the first one then
697 there is no valid next message */
703 gtk_tree_path_free (path);
708 /* TODO: This should be in _init(), with the parameters as properties. */
710 modest_msg_view_window_construct (ModestMsgViewWindow *self,
711 const gchar *modest_account_name,
712 const gchar *mailbox,
713 const gchar *msg_uid)
716 ModestMsgViewWindowPrivate *priv = NULL;
717 ModestWindowPrivate *parent_priv = NULL;
718 ModestDimmingRulesGroup *toolbar_rules_group = NULL;
719 ModestDimmingRulesGroup *clipboard_rules_group = NULL;
721 obj = G_OBJECT (self);
722 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(obj);
723 parent_priv = MODEST_WINDOW_GET_PRIVATE(obj);
725 priv->msg_uid = g_strdup (msg_uid);
728 parent_priv->menubar = NULL;
730 toolbar_rules_group = modest_dimming_rules_group_new (MODEST_DIMMING_RULES_TOOLBAR, TRUE);
731 clipboard_rules_group = modest_dimming_rules_group_new (MODEST_DIMMING_RULES_CLIPBOARD, FALSE);
734 /* Add common dimming rules */
735 modest_dimming_rules_group_add_rules (toolbar_rules_group,
736 modest_msg_view_toolbar_dimming_entries,
737 G_N_ELEMENTS (modest_msg_view_toolbar_dimming_entries),
738 MODEST_WINDOW (self));
739 modest_dimming_rules_group_add_rules (clipboard_rules_group,
740 modest_msg_view_clipboard_dimming_entries,
741 G_N_ELEMENTS (modest_msg_view_clipboard_dimming_entries),
742 MODEST_WINDOW (self));
744 /* Insert dimming rules group for this window */
745 modest_ui_dimming_manager_insert_rules_group (parent_priv->ui_dimming_manager, toolbar_rules_group);
746 modest_ui_dimming_manager_insert_rules_group (parent_priv->ui_dimming_manager, clipboard_rules_group);
747 g_object_unref (toolbar_rules_group);
748 g_object_unref (clipboard_rules_group);
750 /* g_signal_connect (G_OBJECT(obj), "delete-event", G_CALLBACK(on_delete_event), obj); */
752 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);
753 g_signal_connect (G_OBJECT(priv->msg_view), "activate_link",
754 G_CALLBACK (modest_ui_actions_on_msg_link_clicked), obj);
755 g_signal_connect (G_OBJECT(priv->msg_view), "link_hover",
756 G_CALLBACK (modest_ui_actions_on_msg_link_hover), obj);
757 g_signal_connect (G_OBJECT(priv->msg_view), "attachment_clicked",
758 G_CALLBACK (modest_ui_actions_on_msg_attachment_clicked), obj);
759 g_signal_connect (G_OBJECT(priv->msg_view), "recpt_activated",
760 G_CALLBACK (modest_ui_actions_on_msg_recpt_activated), obj);
761 g_signal_connect (G_OBJECT(priv->msg_view), "show_details",
762 G_CALLBACK (modest_ui_actions_on_details), obj);
763 g_signal_connect (G_OBJECT(priv->msg_view), "link_contextual",
764 G_CALLBACK (modest_ui_actions_on_msg_link_contextual), obj);
765 g_signal_connect (G_OBJECT(priv->msg_view), "limit_error",
766 G_CALLBACK (modest_ui_actions_on_limit_error), obj);
767 g_signal_connect (G_OBJECT (priv->msg_view), "fetch_image",
768 G_CALLBACK (on_fetch_image), obj);
770 g_signal_connect (G_OBJECT (obj), "key-release-event",
771 G_CALLBACK (modest_msg_view_window_key_event),
774 g_signal_connect (G_OBJECT (obj), "key-press-event",
775 G_CALLBACK (modest_msg_view_window_key_event),
778 g_signal_connect (G_OBJECT (obj), "move-focus",
779 G_CALLBACK (on_move_focus), obj);
781 g_signal_connect (G_OBJECT (obj), "map-event",
782 G_CALLBACK (_modest_msg_view_window_map_event),
785 /* Mail Operation Queue */
786 priv->queue_change_handler = g_signal_connect (G_OBJECT (modest_runtime_get_mail_operation_queue ()),
788 G_CALLBACK (on_queue_changed),
791 /* Account manager */
792 priv->account_removed_handler = g_signal_connect (G_OBJECT (modest_runtime_get_account_store ()),
794 G_CALLBACK(on_account_removed),
797 modest_window_set_active_account (MODEST_WINDOW(obj), modest_account_name);
798 modest_window_set_active_mailbox (MODEST_WINDOW(obj), mailbox);
800 /* First add out toolbar ... */
801 modest_msg_view_window_show_toolbar (MODEST_WINDOW (obj), TRUE);
803 /* ... and later the find toolbar. This way find toolbar will
804 be shown over the other */
805 priv->find_toolbar = hildon_find_toolbar_new (NULL);
806 hildon_window_add_toolbar (HILDON_WINDOW (obj), GTK_TOOLBAR (priv->find_toolbar));
807 gtk_widget_set_no_show_all (priv->find_toolbar, TRUE);
808 g_signal_connect (G_OBJECT (priv->find_toolbar), "close",
809 G_CALLBACK (modest_msg_view_window_find_toolbar_close), obj);
810 g_signal_connect (G_OBJECT (priv->find_toolbar), "search",
811 G_CALLBACK (modest_msg_view_window_find_toolbar_search), obj);
812 priv->last_search = NULL;
814 /* Init the clipboard actions dim status */
815 modest_msg_view_grab_focus(MODEST_MSG_VIEW (priv->msg_view));
817 update_window_title (MODEST_MSG_VIEW_WINDOW (obj));
822 /* FIXME: parameter checks */
824 modest_msg_view_window_new_with_header_model (TnyMsg *msg,
825 const gchar *modest_account_name,
826 const gchar *mailbox,
827 const gchar *msg_uid,
829 GtkTreeRowReference *row_reference)
831 ModestMsgViewWindow *window = NULL;
832 ModestMsgViewWindowPrivate *priv = NULL;
833 TnyFolder *header_folder = NULL;
834 ModestHeaderView *header_view = NULL;
835 ModestWindowMgr *mgr = NULL;
838 modest_tny_mime_part_to_string (TNY_MIME_PART (msg), 0);
841 mgr = modest_runtime_get_window_mgr ();
842 window = MODEST_MSG_VIEW_WINDOW (modest_window_mgr_get_msg_view_window (mgr));
843 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), NULL);
845 modest_msg_view_window_construct (window, modest_account_name, mailbox, msg_uid);
847 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
849 /* Remember the message list's TreeModel so we can detect changes
850 * and change the list selection when necessary: */
851 header_folder = modest_header_view_get_folder (header_view);
853 priv->is_outbox = (modest_tny_folder_guess_folder_type (header_folder) ==
854 TNY_FOLDER_TYPE_OUTBOX);
855 priv->header_folder_id = tny_folder_get_id (header_folder);
856 g_object_unref(header_folder);
859 /* Setup row references and connect signals */
860 priv->header_model = g_object_ref (model);
862 if (row_reference && gtk_tree_row_reference_valid (row_reference)) {
863 priv->row_reference = gtk_tree_row_reference_copy (row_reference);
864 priv->next_row_reference = gtk_tree_row_reference_copy (row_reference);
865 select_next_valid_row (model, &(priv->next_row_reference), TRUE, priv->is_outbox);
867 priv->row_reference = NULL;
868 priv->next_row_reference = NULL;
871 /* Connect signals */
872 priv->row_changed_handler =
873 g_signal_connect (GTK_TREE_MODEL(model), "row-changed",
874 G_CALLBACK(modest_msg_view_window_on_row_changed),
876 priv->row_deleted_handler =
877 g_signal_connect (GTK_TREE_MODEL(model), "row-deleted",
878 G_CALLBACK(modest_msg_view_window_on_row_deleted),
880 priv->row_inserted_handler =
881 g_signal_connect (GTK_TREE_MODEL(model), "row-inserted",
882 G_CALLBACK(modest_msg_view_window_on_row_inserted),
884 priv->rows_reordered_handler =
885 g_signal_connect(GTK_TREE_MODEL(model), "rows-reordered",
886 G_CALLBACK(modest_msg_view_window_on_row_reordered),
889 if (header_view != NULL){
890 modest_header_view_add_observer(header_view,
891 MODEST_HEADER_VIEW_OBSERVER(window));
894 tny_msg_view_set_msg (TNY_MSG_VIEW (priv->msg_view), msg);
895 update_window_title (MODEST_MSG_VIEW_WINDOW (window));
896 update_branding (MODEST_MSG_VIEW_WINDOW (window));
898 /* gtk_widget_show_all (GTK_WIDGET (window)); */
899 modest_msg_view_window_update_priority (window);
900 /* Check dimming rules */
901 modest_ui_actions_check_toolbar_dimming_rules (MODEST_WINDOW (window));
902 modest_ui_actions_check_menu_dimming_rules (MODEST_WINDOW (window));
903 modest_window_check_dimming_rules_group (MODEST_WINDOW (window), MODEST_DIMMING_RULES_CLIPBOARD);
905 return MODEST_WINDOW(window);
909 modest_msg_view_window_new_from_uid (const gchar *modest_account_name,
910 const gchar *mailbox,
911 const gchar *msg_uid)
913 ModestMsgViewWindow *window = NULL;
914 ModestMsgViewWindowPrivate *priv = NULL;
915 ModestWindowMgr *mgr = NULL;
917 TnyAccount *account = NULL;
919 mgr = modest_runtime_get_window_mgr ();
920 window = MODEST_MSG_VIEW_WINDOW (modest_window_mgr_get_msg_view_window (mgr));
921 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), NULL);
923 modest_msg_view_window_construct (window, modest_account_name, mailbox, msg_uid);
925 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
929 is_merge = g_str_has_prefix (msg_uid, "merge:");
931 /* Get the account */
933 account = tny_account_store_find_account (TNY_ACCOUNT_STORE (modest_runtime_get_account_store ()),
937 if (is_merge || account) {
938 TnyFolder *folder = NULL;
940 /* Try to get the message, if it's already downloaded
941 we don't need to connect */
943 folder = modest_tny_folder_store_find_folder_from_uri (TNY_FOLDER_STORE (account), msg_uid);
945 ModestTnyAccountStore *account_store;
946 ModestTnyLocalFoldersAccount *local_folders_account;
948 account_store = modest_runtime_get_account_store ();
949 local_folders_account = MODEST_TNY_LOCAL_FOLDERS_ACCOUNT (
950 modest_tny_account_store_get_local_folders_account (account_store));
951 folder = modest_tny_local_folders_account_get_merged_outbox (local_folders_account);
952 g_object_unref (local_folders_account);
956 gboolean device_online;
958 device = modest_runtime_get_device();
959 device_online = tny_device_is_online (device);
961 message_reader (window, priv, NULL, msg_uid, folder, NULL);
963 TnyMsg *msg = tny_folder_find_msg (folder, msg_uid, NULL);
965 tny_msg_view_set_msg (TNY_MSG_VIEW (priv->msg_view), msg);
966 update_window_title (MODEST_MSG_VIEW_WINDOW (window));
967 update_branding (MODEST_MSG_VIEW_WINDOW (window));
968 g_object_unref (msg);
970 message_reader (window, priv, NULL, msg_uid, folder, NULL);
973 g_object_unref (folder);
978 /* Check dimming rules */
979 modest_ui_actions_check_toolbar_dimming_rules (MODEST_WINDOW (window));
980 modest_ui_actions_check_menu_dimming_rules (MODEST_WINDOW (window));
981 modest_window_check_dimming_rules_group (MODEST_WINDOW (window), MODEST_DIMMING_RULES_CLIPBOARD);
983 return MODEST_WINDOW(window);
987 modest_msg_view_window_new_from_header_view (ModestHeaderView *header_view,
988 const gchar *modest_account_name,
989 const gchar *mailbox,
990 const gchar *msg_uid,
991 GtkTreeRowReference *row_reference)
993 ModestMsgViewWindow *window = NULL;
994 ModestMsgViewWindowPrivate *priv = NULL;
995 TnyFolder *header_folder = NULL;
996 ModestWindowMgr *mgr = NULL;
1000 mgr = modest_runtime_get_window_mgr ();
1001 window = MODEST_MSG_VIEW_WINDOW (modest_window_mgr_get_msg_view_window (mgr));
1002 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), NULL);
1004 modest_msg_view_window_construct (window, modest_account_name, mailbox, msg_uid);
1006 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1008 /* Remember the message list's TreeModel so we can detect changes
1009 * and change the list selection when necessary: */
1011 if (header_view != NULL){
1012 header_folder = modest_header_view_get_folder(header_view);
1013 /* This could happen if the header folder was
1014 unseleted before opening this msg window (for
1015 example if the user selects an account in the
1016 folder view of the main window */
1017 if (header_folder) {
1018 priv->is_outbox = (modest_tny_folder_guess_folder_type (header_folder) ==
1019 TNY_FOLDER_TYPE_OUTBOX);
1020 priv->header_folder_id = tny_folder_get_id(header_folder);
1021 g_object_unref(header_folder);
1025 /* Setup row references and connect signals */
1026 priv->header_model = gtk_tree_view_get_model (GTK_TREE_VIEW (header_view));
1027 g_object_ref (priv->header_model);
1029 if (row_reference && gtk_tree_row_reference_valid (row_reference)) {
1030 priv->row_reference = gtk_tree_row_reference_copy (row_reference);
1031 priv->next_row_reference = gtk_tree_row_reference_copy (row_reference);
1032 select_next_valid_row (priv->header_model, &(priv->next_row_reference), TRUE, priv->is_outbox);
1034 priv->row_reference = NULL;
1035 priv->next_row_reference = NULL;
1038 /* Connect signals */
1039 priv->row_changed_handler =
1040 g_signal_connect (GTK_TREE_MODEL(priv->header_model), "row-changed",
1041 G_CALLBACK(modest_msg_view_window_on_row_changed),
1043 priv->row_deleted_handler =
1044 g_signal_connect (GTK_TREE_MODEL(priv->header_model), "row-deleted",
1045 G_CALLBACK(modest_msg_view_window_on_row_deleted),
1047 priv->row_inserted_handler =
1048 g_signal_connect (GTK_TREE_MODEL(priv->header_model), "row-inserted",
1049 G_CALLBACK(modest_msg_view_window_on_row_inserted),
1051 priv->rows_reordered_handler =
1052 g_signal_connect(GTK_TREE_MODEL(priv->header_model), "rows-reordered",
1053 G_CALLBACK(modest_msg_view_window_on_row_reordered),
1056 if (header_view != NULL){
1057 modest_header_view_add_observer(header_view,
1058 MODEST_HEADER_VIEW_OBSERVER(window));
1061 tny_msg_view_set_msg (TNY_MSG_VIEW (priv->msg_view), NULL);
1062 update_branding (MODEST_MSG_VIEW_WINDOW (window));
1064 if (priv->row_reference) {
1065 path = gtk_tree_row_reference_get_path (priv->row_reference);
1066 if (gtk_tree_model_get_iter (priv->header_model, &iter, path)) {
1068 gtk_tree_model_get (priv->header_model, &iter,
1069 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
1071 message_reader (window, priv, header, NULL, NULL, priv->row_reference);
1072 g_object_unref (header);
1074 gtk_tree_path_free (path);
1076 /* Check dimming rules */
1077 modest_ui_actions_check_toolbar_dimming_rules (MODEST_WINDOW (window));
1078 modest_ui_actions_check_menu_dimming_rules (MODEST_WINDOW (window));
1079 modest_window_check_dimming_rules_group (MODEST_WINDOW (window), MODEST_DIMMING_RULES_CLIPBOARD);
1081 return MODEST_WINDOW(window);
1085 modest_msg_view_window_new_for_search_result (TnyMsg *msg,
1086 const gchar *modest_account_name,
1087 const gchar *mailbox,
1088 const gchar *msg_uid)
1090 ModestMsgViewWindow *window = NULL;
1091 ModestMsgViewWindowPrivate *priv = NULL;
1092 ModestWindowMgr *mgr = NULL;
1094 mgr = modest_runtime_get_window_mgr ();
1095 window = MODEST_MSG_VIEW_WINDOW (modest_window_mgr_get_msg_view_window (mgr));
1096 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), NULL);
1097 modest_msg_view_window_construct (window, modest_account_name, mailbox, msg_uid);
1099 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1101 /* Remember that this is a search result,
1102 * so we can disable some UI appropriately: */
1103 priv->is_search_result = TRUE;
1105 tny_msg_view_set_msg (TNY_MSG_VIEW (priv->msg_view), msg);
1106 update_branding (MODEST_MSG_VIEW_WINDOW (window));
1108 update_window_title (window);
1109 /* gtk_widget_show_all (GTK_WIDGET (window));*/
1110 modest_msg_view_window_update_priority (window);
1112 /* Check dimming rules */
1113 modest_ui_actions_check_toolbar_dimming_rules (MODEST_WINDOW (window));
1114 modest_ui_actions_check_menu_dimming_rules (MODEST_WINDOW (window));
1115 modest_window_check_dimming_rules_group (MODEST_WINDOW (window), MODEST_DIMMING_RULES_CLIPBOARD);
1117 return MODEST_WINDOW(window);
1121 modest_msg_view_window_is_other_body (ModestMsgViewWindow *self)
1123 ModestMsgViewWindowPrivate *priv = NULL;
1125 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self), FALSE);
1126 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
1128 return (priv->other_body != NULL);
1132 modest_msg_view_window_new_with_other_body (TnyMsg *msg,
1133 TnyMimePart *other_body,
1134 const gchar *modest_account_name,
1135 const gchar *mailbox,
1136 const gchar *msg_uid)
1138 GObject *obj = NULL;
1139 ModestMsgViewWindowPrivate *priv;
1140 ModestWindowMgr *mgr = NULL;
1142 g_return_val_if_fail (msg, NULL);
1143 mgr = modest_runtime_get_window_mgr ();
1144 obj = G_OBJECT (modest_window_mgr_get_msg_view_window (mgr));
1145 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (obj);
1146 modest_msg_view_window_construct (MODEST_MSG_VIEW_WINDOW (obj),
1147 modest_account_name, mailbox, msg_uid);
1150 priv->other_body = g_object_ref (other_body);
1151 modest_msg_view_set_msg_with_other_body (MODEST_MSG_VIEW (priv->msg_view), msg, other_body);
1153 tny_msg_view_set_msg (TNY_MSG_VIEW (priv->msg_view), msg);
1155 update_window_title (MODEST_MSG_VIEW_WINDOW (obj));
1156 update_branding (MODEST_MSG_VIEW_WINDOW (obj));
1158 /* gtk_widget_show_all (GTK_WIDGET (obj)); */
1160 /* Check dimming rules */
1161 modest_ui_actions_check_toolbar_dimming_rules (MODEST_WINDOW (obj));
1162 modest_ui_actions_check_menu_dimming_rules (MODEST_WINDOW (obj));
1163 modest_window_check_dimming_rules_group (MODEST_WINDOW (obj), MODEST_DIMMING_RULES_CLIPBOARD);
1165 return MODEST_WINDOW(obj);
1169 modest_msg_view_window_new_for_attachment (TnyMsg *msg,
1170 const gchar *modest_account_name,
1171 const gchar *mailbox,
1172 const gchar *msg_uid)
1174 return modest_msg_view_window_new_with_other_body (msg, NULL, modest_account_name, mailbox, msg_uid);
1178 modest_msg_view_window_on_row_changed (GtkTreeModel *header_model,
1181 ModestMsgViewWindow *window)
1183 check_dimming_rules_after_change (window);
1187 modest_msg_view_window_on_row_deleted(GtkTreeModel *header_model,
1189 ModestMsgViewWindow *window)
1191 check_dimming_rules_after_change (window);
1193 /* The window could have dissapeared */
1196 check_dimming_rules_after_change (ModestMsgViewWindow *window)
1198 modest_ui_actions_check_menu_dimming_rules (MODEST_WINDOW (window));
1199 modest_ui_actions_check_toolbar_dimming_rules (MODEST_WINDOW (window));
1203 /* On insertions we check if the folder still has the message we are
1204 * showing or do not. If do not, we do nothing. Which means we are still
1205 * not attached to any header folder and thus next/prev buttons are
1206 * still dimmed. Once the message that is shown by msg-view is found, the
1207 * new model of header-view will be attached and the references will be set.
1208 * On each further insertions dimming rules will be checked. However
1209 * this requires extra CPU time at least works.
1210 * (An message might be deleted from TnyFolder and thus will not be
1211 * inserted into the model again for example if it is removed by the
1212 * imap server and the header view is refreshed.)
1215 modest_msg_view_window_on_row_inserted (GtkTreeModel *model,
1216 GtkTreePath *tree_path,
1217 GtkTreeIter *tree_iter,
1218 ModestMsgViewWindow *window)
1220 ModestMsgViewWindowPrivate *priv = NULL;
1221 TnyHeader *header = NULL;
1223 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window));
1224 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1226 g_assert (model == priv->header_model);
1228 /* Check if the newly inserted message is the same we are actually
1229 * showing. IF not, we should remain detached from the header model
1230 * and thus prev and next toolbar buttons should remain dimmed. */
1231 gtk_tree_model_get (model, tree_iter,
1232 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
1235 if (TNY_IS_HEADER (header)) {
1238 uid = modest_tny_folder_get_header_unique_id (header);
1239 if (!g_str_equal(priv->msg_uid, uid)) {
1240 check_dimming_rules_after_change (window);
1242 g_object_unref (G_OBJECT(header));
1246 g_object_unref(G_OBJECT(header));
1249 if (priv->row_reference) {
1250 gtk_tree_row_reference_free (priv->row_reference);
1253 /* Setup row_reference for the actual msg. */
1254 priv->row_reference = gtk_tree_row_reference_new (priv->header_model, tree_path);
1255 if (priv->row_reference == NULL) {
1256 g_warning("%s: No reference for msg header item.", __FUNCTION__);
1260 /* Now set up next_row_reference. */
1261 if (priv->next_row_reference) {
1262 gtk_tree_row_reference_free (priv->next_row_reference);
1265 priv->next_row_reference = gtk_tree_row_reference_copy (priv->row_reference);
1266 select_next_valid_row (priv->header_model,
1267 &(priv->next_row_reference), FALSE, priv->is_outbox);
1269 /* Connect the remaining callbacks to become able to detect
1270 * changes in header-view. */
1271 priv->row_changed_handler =
1272 g_signal_connect (priv->header_model, "row-changed",
1273 G_CALLBACK (modest_msg_view_window_on_row_changed),
1275 priv->row_deleted_handler =
1276 g_signal_connect (priv->header_model, "row-deleted",
1277 G_CALLBACK (modest_msg_view_window_on_row_deleted),
1279 priv->rows_reordered_handler =
1280 g_signal_connect (priv->header_model, "rows-reordered",
1281 G_CALLBACK (modest_msg_view_window_on_row_reordered),
1284 check_dimming_rules_after_change (window);
1288 modest_msg_view_window_on_row_reordered (GtkTreeModel *header_model,
1292 ModestMsgViewWindow *window)
1294 ModestMsgViewWindowPrivate *priv = NULL;
1295 gboolean already_changed = FALSE;
1297 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(window);
1299 /* If the current row was reordered select the proper next
1300 valid row. The same if the next row reference changes */
1301 if (!priv->row_reference ||
1302 !gtk_tree_row_reference_valid (priv->row_reference))
1305 if (priv->next_row_reference &&
1306 gtk_tree_row_reference_valid (priv->next_row_reference)) {
1307 GtkTreePath *cur, *next;
1308 /* Check that the order is still the correct one */
1309 cur = gtk_tree_row_reference_get_path (priv->row_reference);
1310 next = gtk_tree_row_reference_get_path (priv->next_row_reference);
1311 gtk_tree_path_next (cur);
1312 if (gtk_tree_path_compare (cur, next) != 0) {
1313 gtk_tree_row_reference_free (priv->next_row_reference);
1314 priv->next_row_reference = gtk_tree_row_reference_copy (priv->row_reference);
1315 select_next_valid_row (header_model, &(priv->next_row_reference), FALSE, priv->is_outbox);
1316 already_changed = TRUE;
1318 gtk_tree_path_free (cur);
1319 gtk_tree_path_free (next);
1321 if (priv->next_row_reference)
1322 gtk_tree_row_reference_free (priv->next_row_reference);
1323 /* Update next row reference */
1324 priv->next_row_reference = gtk_tree_row_reference_copy (priv->row_reference);
1325 select_next_valid_row (header_model, &(priv->next_row_reference), FALSE, priv->is_outbox);
1326 already_changed = TRUE;
1329 check_dimming_rules_after_change (window);
1332 /* The modest_msg_view_window_update_model_replaced implements update
1333 * function for ModestHeaderViewObserver. Checks whether the TnyFolder
1334 * actually belongs to the header-view is the same as the TnyFolder of
1335 * the message of msg-view or not. If they are different, there is
1336 * nothing to do. If they are the same, then the model has replaced and
1337 * the reference in msg-view shall be replaced from the old model to
1338 * the new model. In this case the view will be detached from it's
1339 * header folder. From this point the next/prev buttons are dimmed.
1342 modest_msg_view_window_update_model_replaced (ModestHeaderViewObserver *observer,
1343 GtkTreeModel *model,
1344 const gchar *tny_folder_id)
1346 ModestMsgViewWindowPrivate *priv = NULL;
1347 ModestMsgViewWindow *window = NULL;
1349 g_assert(MODEST_IS_HEADER_VIEW_OBSERVER(observer));
1350 g_assert(MODEST_IS_MSG_VIEW_WINDOW(observer));
1352 window = MODEST_MSG_VIEW_WINDOW(observer);
1353 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(window);
1355 /* If there is an other folder in the header-view then we do
1356 * not care about it's model (msg list). Else if the
1357 * header-view shows the folder the msg shown by us is in, we
1358 * shall replace our model reference and make some check. */
1359 if(model == NULL || tny_folder_id == NULL ||
1360 (priv->header_folder_id && !g_str_equal(tny_folder_id, priv->header_folder_id)))
1363 /* Model is changed(replaced), so we should forget the old
1364 * one. Because there might be other references and there
1365 * might be some change on the model even if we unreferenced
1366 * it, we need to disconnect our signals here. */
1367 if (priv->header_model) {
1368 if (g_signal_handler_is_connected(G_OBJECT (priv->header_model),
1369 priv->row_changed_handler))
1370 g_signal_handler_disconnect(G_OBJECT (priv->header_model),
1371 priv->row_changed_handler);
1372 if (g_signal_handler_is_connected(G_OBJECT (priv->header_model),
1373 priv->row_deleted_handler))
1374 g_signal_handler_disconnect(G_OBJECT (priv->header_model),
1375 priv->row_deleted_handler);
1376 if (g_signal_handler_is_connected(G_OBJECT (priv->header_model),
1377 priv->row_inserted_handler))
1378 g_signal_handler_disconnect(G_OBJECT (priv->header_model),
1379 priv->row_inserted_handler);
1380 if (g_signal_handler_is_connected(G_OBJECT (priv->header_model),
1381 priv->rows_reordered_handler))
1382 g_signal_handler_disconnect(G_OBJECT (priv->header_model),
1383 priv->rows_reordered_handler);
1386 if (priv->row_reference)
1387 gtk_tree_row_reference_free (priv->row_reference);
1388 if (priv->next_row_reference)
1389 gtk_tree_row_reference_free (priv->next_row_reference);
1390 g_object_unref(priv->header_model);
1393 priv->row_changed_handler = 0;
1394 priv->row_deleted_handler = 0;
1395 priv->row_inserted_handler = 0;
1396 priv->rows_reordered_handler = 0;
1397 priv->next_row_reference = NULL;
1398 priv->row_reference = NULL;
1399 priv->header_model = NULL;
1402 priv->header_model = g_object_ref (model);
1404 /* Also we must connect to the new model for row insertions.
1405 * Only for insertions now. We will need other ones only after
1406 * the msg is show by msg-view is added to the new model. */
1407 priv->row_inserted_handler =
1408 g_signal_connect (priv->header_model, "row-inserted",
1409 G_CALLBACK(modest_msg_view_window_on_row_inserted),
1412 modest_ui_actions_check_menu_dimming_rules(MODEST_WINDOW(window));
1413 modest_ui_actions_check_toolbar_dimming_rules(MODEST_WINDOW(window));
1417 modest_msg_view_window_toolbar_on_transfer_mode (ModestMsgViewWindow *self)
1419 ModestMsgViewWindowPrivate *priv= NULL;
1421 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self), FALSE);
1422 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
1424 return priv->progress_hint;
1428 modest_msg_view_window_get_header (ModestMsgViewWindow *self)
1430 ModestMsgViewWindowPrivate *priv= NULL;
1432 TnyHeader *header = NULL;
1433 GtkTreePath *path = NULL;
1436 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self), NULL);
1437 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
1439 /* If the message was not obtained from a treemodel,
1440 * for instance if it was opened directly by the search UI:
1442 if (priv->header_model == NULL ||
1443 priv->row_reference == NULL ||
1444 !gtk_tree_row_reference_valid (priv->row_reference)) {
1445 msg = modest_msg_view_window_get_message (self);
1447 header = tny_msg_get_header (msg);
1448 g_object_unref (msg);
1453 /* Get iter of the currently selected message in the header view: */
1454 path = gtk_tree_row_reference_get_path (priv->row_reference);
1455 g_return_val_if_fail (path != NULL, NULL);
1456 gtk_tree_model_get_iter (priv->header_model,
1460 /* Get current message header */
1461 gtk_tree_model_get (priv->header_model, &iter,
1462 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
1465 gtk_tree_path_free (path);
1470 modest_msg_view_window_get_message (ModestMsgViewWindow *self)
1472 ModestMsgViewWindowPrivate *priv;
1474 g_return_val_if_fail (self, NULL);
1476 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(self);
1478 return tny_msg_view_get_msg (TNY_MSG_VIEW (priv->msg_view));
1482 modest_msg_view_window_get_message_uid (ModestMsgViewWindow *self)
1484 ModestMsgViewWindowPrivate *priv;
1486 g_return_val_if_fail (self, NULL);
1488 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
1490 return (const gchar*) priv->msg_uid;
1493 /* Used for the Ctrl+F accelerator */
1495 modest_msg_view_window_toggle_find_toolbar (GtkWidget *obj,
1498 ModestMsgViewWindow *window = MODEST_MSG_VIEW_WINDOW (data);
1499 ModestMsgViewWindowPrivate *priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1501 if (GTK_WIDGET_VISIBLE (priv->find_toolbar)) {
1502 modest_msg_view_window_find_toolbar_close (obj, data);
1504 modest_msg_view_window_show_find_toolbar (obj, data);
1508 /* Handler for menu option */
1510 modest_msg_view_window_show_find_toolbar (GtkWidget *obj,
1513 ModestMsgViewWindow *window = MODEST_MSG_VIEW_WINDOW (data);
1514 ModestMsgViewWindowPrivate *priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1516 gtk_widget_show (priv->find_toolbar);
1517 hildon_find_toolbar_highlight_entry (HILDON_FIND_TOOLBAR (priv->find_toolbar), TRUE);
1520 /* Handler for click on the "X" close button in find toolbar */
1522 modest_msg_view_window_find_toolbar_close (GtkWidget *widget,
1523 ModestMsgViewWindow *obj)
1525 ModestMsgViewWindowPrivate *priv;
1527 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (obj);
1530 gtk_widget_hide (priv->find_toolbar);
1531 modest_msg_view_grab_focus (MODEST_MSG_VIEW (priv->msg_view));
1535 modest_msg_view_window_find_toolbar_search (GtkWidget *widget,
1536 ModestMsgViewWindow *obj)
1538 gchar *current_search;
1539 ModestMsgViewWindowPrivate *priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (obj);
1541 if (modest_mime_part_view_is_empty (MODEST_MIME_PART_VIEW (priv->msg_view))) {
1542 hildon_banner_show_information (NULL, NULL, _("mail_ib_nothing_to_find"));
1546 g_object_get (G_OBJECT (widget), "prefix", ¤t_search, NULL);
1548 if ((current_search == NULL) || (strcmp (current_search, "") == 0)) {
1549 g_free (current_search);
1550 hildon_banner_show_information (NULL, NULL, _CS("ecdg_ib_find_rep_enter_text"));
1554 if ((priv->last_search == NULL) || (strcmp (priv->last_search, current_search) != 0)) {
1556 g_free (priv->last_search);
1557 priv->last_search = g_strdup (current_search);
1558 result = modest_isearch_view_search (MODEST_ISEARCH_VIEW (priv->msg_view),
1561 hildon_banner_show_information (NULL, NULL,
1562 _HL("ckct_ib_find_no_matches"));
1563 g_free (priv->last_search);
1564 priv->last_search = NULL;
1566 hildon_find_toolbar_highlight_entry (HILDON_FIND_TOOLBAR (priv->find_toolbar), TRUE);
1569 if (!modest_isearch_view_search_next (MODEST_ISEARCH_VIEW (priv->msg_view))) {
1570 hildon_banner_show_information (NULL, NULL,
1571 _HL("ckct_ib_find_search_complete"));
1572 g_free (priv->last_search);
1573 priv->last_search = NULL;
1575 hildon_find_toolbar_highlight_entry (HILDON_FIND_TOOLBAR (priv->find_toolbar), TRUE);
1579 g_free (current_search);
1584 modest_msg_view_window_set_zoom (ModestWindow *window,
1587 ModestMsgViewWindowPrivate *priv;
1588 ModestWindowPrivate *parent_priv;
1590 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window));
1592 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1593 parent_priv = MODEST_WINDOW_GET_PRIVATE (window);
1594 modest_zoomable_set_zoom (MODEST_ZOOMABLE (priv->msg_view), zoom);
1599 modest_msg_view_window_get_zoom (ModestWindow *window)
1601 ModestMsgViewWindowPrivate *priv;
1603 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), 1.0);
1605 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1606 return modest_zoomable_get_zoom (MODEST_ZOOMABLE (priv->msg_view));
1610 modest_msg_view_window_zoom_plus (ModestWindow *window)
1613 ModestMsgViewWindowPrivate *priv;
1617 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), 1.0);
1618 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1620 zoom_level = modest_zoomable_get_zoom (MODEST_ZOOMABLE (priv->msg_view));
1622 if (zoom_level >= 2.0) {
1623 hildon_banner_show_information (NULL, NULL,
1624 _CS("ckct_ib_max_zoom_level_reached"));
1626 } else if (zoom_level >= 1.5) {
1628 } else if (zoom_level >= 1.2) {
1630 } else if (zoom_level >= 1.0) {
1632 } else if (zoom_level >= 0.8) {
1634 } else if (zoom_level >= 0.5) {
1640 /* set zoom level */
1641 int_zoom = (gint) rint (zoom_level*100.0+0.1);
1642 banner_text = g_strdup_printf (_HL("wdgt_ib_zoom"), int_zoom);
1643 modest_platform_information_banner (GTK_WIDGET (window), NULL, banner_text);
1644 g_free (banner_text);
1645 modest_zoomable_set_zoom (MODEST_ZOOMABLE (priv->msg_view), zoom_level);
1651 modest_msg_view_window_zoom_minus (ModestWindow *window)
1654 ModestMsgViewWindowPrivate *priv;
1658 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), 1.0);
1659 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1661 zoom_level = modest_zoomable_get_zoom (MODEST_ZOOMABLE (priv->msg_view));
1663 if (zoom_level <= 0.5) {
1664 hildon_banner_show_information (NULL, NULL,
1665 _CS("ckct_ib_min_zoom_level_reached"));
1667 } else if (zoom_level <= 0.8) {
1669 } else if (zoom_level <= 1.0) {
1671 } else if (zoom_level <= 1.2) {
1673 } else if (zoom_level <= 1.5) {
1675 } else if (zoom_level <= 2.0) {
1681 /* set zoom level */
1682 int_zoom = (gint) rint (zoom_level*100.0+0.1);
1683 banner_text = g_strdup_printf (_HL("wdgt_ib_zoom"), int_zoom);
1684 modest_platform_information_banner (GTK_WIDGET (window), NULL, banner_text);
1685 g_free (banner_text);
1686 modest_zoomable_set_zoom (MODEST_ZOOMABLE (priv->msg_view), zoom_level);
1693 modest_msg_view_window_key_event (GtkWidget *window,
1699 focus = gtk_window_get_focus (GTK_WINDOW (window));
1701 /* for the find toolbar case */
1702 if (focus && GTK_IS_ENTRY (focus)) {
1703 if (event->keyval == GDK_BackSpace) {
1705 copy = gdk_event_copy ((GdkEvent *) event);
1706 gtk_widget_event (focus, copy);
1707 gdk_event_free (copy);
1712 if (event->keyval == GDK_Up || event->keyval == GDK_KP_Up ||
1713 event->keyval == GDK_Down || event->keyval == GDK_KP_Down ||
1714 event->keyval == GDK_Page_Up || event->keyval == GDK_KP_Page_Up ||
1715 event->keyval == GDK_Page_Down || event->keyval == GDK_KP_Page_Down ||
1716 event->keyval == GDK_Home || event->keyval == GDK_KP_Home ||
1717 event->keyval == GDK_End || event->keyval == GDK_KP_End) {
1718 /* ModestMsgViewWindowPrivate *priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window); */
1719 /* gboolean return_value; */
1721 if (event->type == GDK_KEY_PRESS) {
1722 GtkScrollType scroll_type;
1724 switch (event->keyval) {
1727 scroll_type = GTK_SCROLL_STEP_UP; break;
1730 scroll_type = GTK_SCROLL_STEP_DOWN; break;
1732 case GDK_KP_Page_Up:
1733 scroll_type = GTK_SCROLL_PAGE_UP; break;
1735 case GDK_KP_Page_Down:
1736 scroll_type = GTK_SCROLL_PAGE_DOWN; break;
1739 scroll_type = GTK_SCROLL_START; break;
1742 scroll_type = GTK_SCROLL_END; break;
1743 default: scroll_type = GTK_SCROLL_NONE;
1746 /* g_signal_emit_by_name (G_OBJECT (priv->main_scroll), "scroll-child", */
1747 /* scroll_type, FALSE, &return_value); */
1758 modest_msg_view_window_last_message_selected (ModestMsgViewWindow *window)
1761 ModestMsgViewWindowPrivate *priv;
1762 GtkTreeIter tmp_iter;
1763 gboolean is_last_selected;
1765 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), TRUE);
1766 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1768 /*if no model (so no rows at all), then virtually we are the last*/
1769 if (!priv->header_model || !priv->row_reference)
1772 if (!gtk_tree_row_reference_valid (priv->row_reference))
1775 path = gtk_tree_row_reference_get_path (priv->row_reference);
1779 is_last_selected = TRUE;
1780 while (is_last_selected) {
1782 gtk_tree_path_next (path);
1783 if (!gtk_tree_model_get_iter (priv->header_model, &tmp_iter, path))
1785 gtk_tree_model_get (priv->header_model, &tmp_iter,
1786 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
1789 if (msg_is_visible (header, priv->is_outbox))
1790 is_last_selected = FALSE;
1791 g_object_unref(G_OBJECT(header));
1794 gtk_tree_path_free (path);
1795 return is_last_selected;
1799 modest_msg_view_window_has_headers_model (ModestMsgViewWindow *window)
1801 ModestMsgViewWindowPrivate *priv;
1803 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), TRUE);
1804 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1806 return priv->header_model != NULL;
1810 modest_msg_view_window_is_search_result (ModestMsgViewWindow *window)
1812 ModestMsgViewWindowPrivate *priv;
1814 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), TRUE);
1815 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1817 return priv->is_search_result;
1821 msg_is_visible (TnyHeader *header, gboolean check_outbox)
1823 if ((tny_header_get_flags(header) & TNY_HEADER_FLAG_DELETED))
1825 if (!check_outbox) {
1828 ModestTnySendQueueStatus status;
1829 status = modest_tny_all_send_queues_get_msg_status (header);
1830 return ((status != MODEST_TNY_SEND_QUEUE_FAILED) &&
1831 (status != MODEST_TNY_SEND_QUEUE_SENDING));
1836 modest_msg_view_window_first_message_selected (ModestMsgViewWindow *window)
1839 ModestMsgViewWindowPrivate *priv;
1840 gboolean is_first_selected;
1841 GtkTreeIter tmp_iter;
1843 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), TRUE);
1844 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1846 /*if no model (so no rows at all), then virtually we are the first*/
1847 if (!priv->header_model || !priv->row_reference)
1850 if (!gtk_tree_row_reference_valid (priv->row_reference))
1853 path = gtk_tree_row_reference_get_path (priv->row_reference);
1857 is_first_selected = TRUE;
1858 while (is_first_selected) {
1860 if(!gtk_tree_path_prev (path))
1862 /* Here the 'if' is needless for logic, but let make sure
1863 * iter is valid for gtk_tree_model_get. */
1864 if (!gtk_tree_model_get_iter (priv->header_model, &tmp_iter, path))
1866 gtk_tree_model_get (priv->header_model, &tmp_iter,
1867 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
1870 if (msg_is_visible (header, priv->is_outbox))
1871 is_first_selected = FALSE;
1872 g_object_unref(G_OBJECT(header));
1875 gtk_tree_path_free (path);
1876 return is_first_selected;
1883 GtkTreeRowReference *row_reference;
1887 message_reader_performer (gboolean canceled,
1889 GtkWindow *parent_window,
1890 TnyAccount *account,
1893 ModestMailOperation *mail_op = NULL;
1894 MsgReaderInfo *info;
1896 info = (MsgReaderInfo *) user_data;
1897 if (canceled || err) {
1898 update_window_title (MODEST_MSG_VIEW_WINDOW (parent_window));
1902 /* Register the header - it'll be unregistered in the callback */
1904 modest_window_mgr_register_header (modest_runtime_get_window_mgr (), info->header, NULL);
1906 /* New mail operation */
1907 mail_op = modest_mail_operation_new_with_error_handling (G_OBJECT(parent_window),
1908 modest_ui_actions_disk_operations_error_handler,
1911 modest_mail_operation_queue_add (modest_runtime_get_mail_operation_queue (), mail_op);
1913 modest_mail_operation_get_msg (mail_op, info->header, TRUE, view_msg_cb, info->row_reference);
1915 modest_mail_operation_find_msg (mail_op, info->folder, info->msg_uid, TRUE, view_msg_cb, NULL);
1916 g_object_unref (mail_op);
1918 /* Update dimming rules */
1919 modest_ui_actions_check_toolbar_dimming_rules (MODEST_WINDOW (parent_window));
1920 modest_ui_actions_check_menu_dimming_rules (MODEST_WINDOW (parent_window));
1923 /* Frees. The row_reference will be freed by the view_msg_cb callback */
1924 g_free (info->msg_uid);
1926 g_object_unref (info->folder);
1928 g_object_unref (info->header);
1929 g_slice_free (MsgReaderInfo, info);
1934 * Reads the message whose summary item is @header. It takes care of
1935 * several things, among others:
1937 * If the message was not previously downloaded then ask the user
1938 * before downloading. If there is no connection launch the connection
1939 * dialog. Update toolbar dimming rules.
1941 * Returns: TRUE if the mail operation was started, otherwise if the
1942 * user do not want to download the message, or if the user do not
1943 * want to connect, then the operation is not issued
1946 message_reader (ModestMsgViewWindow *window,
1947 ModestMsgViewWindowPrivate *priv,
1949 const gchar *msg_uid,
1951 GtkTreeRowReference *row_reference)
1953 ModestWindowMgr *mgr;
1954 TnyAccount *account = NULL;
1955 MsgReaderInfo *info;
1957 /* We set the header from model while we're loading */
1958 tny_header_view_set_header (TNY_HEADER_VIEW (priv->msg_view), header);
1959 gtk_window_set_title (GTK_WINDOW (window), _CS("ckdg_pb_updating"));
1965 g_object_ref (folder);
1967 mgr = modest_runtime_get_window_mgr ();
1968 /* Msg download completed */
1969 if (!header || !(tny_header_get_flags (header) & TNY_HEADER_FLAG_CACHED)) {
1971 /* Ask the user if he wants to download the message if
1973 if (!tny_device_is_online (modest_runtime_get_device())) {
1974 GtkResponseType response;
1976 response = modest_platform_run_confirmation_dialog (GTK_WINDOW (window),
1977 _("mcen_nc_get_msg"));
1978 if (response == GTK_RESPONSE_CANCEL) {
1979 update_window_title (window);
1984 folder = tny_header_get_folder (header);
1986 info = g_slice_new (MsgReaderInfo);
1987 info->msg_uid = g_strdup (msg_uid);
1989 info->header = g_object_ref (header);
1991 info->header = NULL;
1993 info->folder = g_object_ref (folder);
1995 info->folder = NULL;
1996 if (row_reference) {
1997 info->row_reference = gtk_tree_row_reference_copy (row_reference);
1999 info->row_reference = NULL;
2002 /* Offer the connection dialog if necessary */
2003 modest_platform_connect_if_remote_and_perform ((GtkWindow *) window,
2005 TNY_FOLDER_STORE (folder),
2006 message_reader_performer,
2009 g_object_unref (folder);
2015 folder = tny_header_get_folder (header);
2018 account = tny_folder_get_account (folder);
2020 info = g_slice_new (MsgReaderInfo);
2021 info->msg_uid = g_strdup (msg_uid);
2023 info->folder = g_object_ref (folder);
2025 info->folder = NULL;
2027 info->header = g_object_ref (header);
2029 info->header = NULL;
2031 info->row_reference = gtk_tree_row_reference_copy (row_reference);
2033 info->row_reference = NULL;
2035 message_reader_performer (FALSE, NULL, (GtkWindow *) window, account, info);
2037 g_object_unref (account);
2039 g_object_unref (folder);
2045 modest_msg_view_window_select_next_message (ModestMsgViewWindow *window)
2047 ModestMsgViewWindowPrivate *priv;
2048 GtkTreePath *path= NULL;
2049 GtkTreeIter tmp_iter;
2051 gboolean retval = TRUE;
2052 GtkTreeRowReference *row_reference = NULL;
2054 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), FALSE);
2055 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
2057 if (!priv->row_reference)
2060 /* Update the next row reference if it's not valid. This could
2061 happen if for example the header which it was pointing to,
2062 was deleted. The best place to do it is in the row-deleted
2063 handler but the tinymail model do not work like the glib
2064 tree models and reports the deletion when the row is still
2066 if (!gtk_tree_row_reference_valid (priv->next_row_reference)) {
2067 if (priv->next_row_reference) {
2068 gtk_tree_row_reference_free (priv->next_row_reference);
2070 if (gtk_tree_row_reference_valid (priv->row_reference)) {
2071 priv->next_row_reference = gtk_tree_row_reference_copy (priv->row_reference);
2072 select_next_valid_row (priv->header_model, &(priv->next_row_reference), FALSE, priv->is_outbox);
2074 priv->next_row_reference = NULL;
2077 if (priv->next_row_reference)
2078 path = gtk_tree_row_reference_get_path (priv->next_row_reference);
2082 row_reference = gtk_tree_row_reference_copy (priv->next_row_reference);
2084 gtk_tree_model_get_iter (priv->header_model,
2087 gtk_tree_path_free (path);
2089 gtk_tree_model_get (priv->header_model, &tmp_iter,
2090 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
2093 /* Read the message & show it */
2094 if (!message_reader (window, priv, header, NULL, NULL, row_reference)) {
2097 gtk_tree_row_reference_free (row_reference);
2100 g_object_unref (header);
2106 modest_msg_view_window_select_previous_message (ModestMsgViewWindow *window)
2108 ModestMsgViewWindowPrivate *priv = NULL;
2110 gboolean finished = FALSE;
2111 gboolean retval = FALSE;
2113 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), FALSE);
2114 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
2116 if (priv->row_reference && !gtk_tree_row_reference_valid (priv->row_reference)) {
2117 gtk_tree_row_reference_free (priv->row_reference);
2118 priv->row_reference = NULL;
2121 /* Return inmediatly if there is no header model */
2122 if (!priv->header_model || !priv->row_reference)
2125 path = gtk_tree_row_reference_get_path (priv->row_reference);
2126 while (!finished && gtk_tree_path_prev (path)) {
2130 gtk_tree_model_get_iter (priv->header_model, &iter, path);
2131 gtk_tree_model_get (priv->header_model, &iter,
2132 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
2136 if (msg_is_visible (header, priv->is_outbox)) {
2137 GtkTreeRowReference *row_reference;
2138 row_reference = gtk_tree_row_reference_new (priv->header_model, path);
2139 /* Read the message & show it */
2140 retval = message_reader (window, priv, header, NULL, NULL, row_reference);
2141 gtk_tree_row_reference_free (row_reference);
2145 g_object_unref (header);
2149 gtk_tree_path_free (path);
2154 view_msg_cb (ModestMailOperation *mail_op,
2161 ModestMsgViewWindow *self = NULL;
2162 ModestMsgViewWindowPrivate *priv = NULL;
2163 GtkTreeRowReference *row_reference = NULL;
2165 /* Unregister the header (it was registered before creating the mail operation) */
2166 modest_window_mgr_unregister_header (modest_runtime_get_window_mgr (), header);
2168 row_reference = (GtkTreeRowReference *) user_data;
2171 gtk_tree_row_reference_free (row_reference);
2172 self = (ModestMsgViewWindow *) modest_mail_operation_get_source (mail_op);
2174 /* Restore window title */
2175 update_window_title (self);
2176 g_object_unref (self);
2181 /* If there was any error */
2182 if (!modest_ui_actions_msg_retrieval_check (mail_op, header, msg)) {
2184 gtk_tree_row_reference_free (row_reference);
2185 self = (ModestMsgViewWindow *) modest_mail_operation_get_source (mail_op);
2187 /* Restore window title */
2188 update_window_title (self);
2189 g_object_unref (self);
2194 /* Get the window */
2195 self = (ModestMsgViewWindow *) modest_mail_operation_get_source (mail_op);
2196 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self));
2197 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
2199 /* Update the row reference */
2200 if (priv->row_reference != NULL) {
2201 gtk_tree_row_reference_free (priv->row_reference);
2202 priv->row_reference = (row_reference && gtk_tree_row_reference_valid (row_reference))?gtk_tree_row_reference_copy (row_reference):NULL;
2203 if (priv->next_row_reference != NULL) {
2204 gtk_tree_row_reference_free (priv->next_row_reference);
2206 if (priv->row_reference) {
2207 priv->next_row_reference = gtk_tree_row_reference_copy (priv->row_reference);
2208 select_next_valid_row (priv->header_model, &(priv->next_row_reference), TRUE, priv->is_outbox);
2210 priv->next_row_reference = NULL;
2214 /* Mark header as read */
2215 if (!(tny_header_get_flags (header) & TNY_HEADER_FLAG_SEEN))
2216 tny_header_set_flag (header, TNY_HEADER_FLAG_SEEN);
2218 /* Set new message */
2219 if (priv->msg_view != NULL && TNY_IS_MSG_VIEW (priv->msg_view)) {
2220 tny_msg_view_set_msg (TNY_MSG_VIEW (priv->msg_view), msg);
2221 modest_msg_view_window_update_priority (self);
2222 update_window_title (MODEST_MSG_VIEW_WINDOW (self));
2223 update_branding (MODEST_MSG_VIEW_WINDOW (self));
2224 modest_msg_view_grab_focus (MODEST_MSG_VIEW (priv->msg_view));
2227 /* Set the new message uid of the window */
2228 if (priv->msg_uid) {
2229 g_free (priv->msg_uid);
2230 priv->msg_uid = modest_tny_folder_get_header_unique_id (header);
2233 /* Notify the observers */
2234 g_signal_emit (G_OBJECT (self), signals[MSG_CHANGED_SIGNAL],
2235 0, priv->header_model, priv->row_reference);
2238 g_object_unref (self);
2240 gtk_tree_row_reference_free (row_reference);
2244 modest_msg_view_window_get_folder_type (ModestMsgViewWindow *window)
2246 ModestMsgViewWindowPrivate *priv;
2248 TnyFolderType folder_type;
2250 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
2252 folder_type = TNY_FOLDER_TYPE_UNKNOWN;
2254 msg = tny_msg_view_get_msg (TNY_MSG_VIEW (priv->msg_view));
2258 folder = tny_msg_get_folder (msg);
2260 folder_type = modest_tny_folder_guess_folder_type (folder);
2261 g_object_unref (folder);
2263 g_object_unref (msg);
2271 modest_msg_view_window_update_priority (ModestMsgViewWindow *window)
2273 ModestMsgViewWindowPrivate *priv;
2274 TnyHeader *header = NULL;
2275 TnyHeaderFlags flags = 0;
2277 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
2279 if (priv->header_model && priv->row_reference && gtk_tree_row_reference_valid (priv->row_reference)) {
2281 GtkTreePath *path = NULL;
2283 path = gtk_tree_row_reference_get_path (priv->row_reference);
2284 g_return_if_fail (path != NULL);
2285 gtk_tree_model_get_iter (priv->header_model,
2287 gtk_tree_row_reference_get_path (priv->row_reference));
2289 gtk_tree_model_get (priv->header_model, &iter, TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
2291 gtk_tree_path_free (path);
2294 msg = tny_msg_view_get_msg (TNY_MSG_VIEW (priv->msg_view));
2296 header = tny_msg_get_header (msg);
2297 g_object_unref (msg);
2302 flags = tny_header_get_flags (header);
2303 g_object_unref(G_OBJECT(header));
2306 modest_msg_view_set_priority (MODEST_MSG_VIEW(priv->msg_view), flags);
2311 toolbar_resize (ModestMsgViewWindow *self)
2313 ModestMsgViewWindowPrivate *priv = NULL;
2314 ModestWindowPrivate *parent_priv = NULL;
2316 gint static_button_size;
2317 ModestWindowMgr *mgr;
2319 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self));
2320 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
2321 parent_priv = MODEST_WINDOW_GET_PRIVATE(self);
2323 mgr = modest_runtime_get_window_mgr ();
2324 static_button_size = modest_window_mgr_get_fullscreen_mode (mgr)?120:120;
2326 if (parent_priv->toolbar) {
2327 /* left size buttons */
2328 widget = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/ToolbarMessageReply");
2329 gtk_tool_item_set_expand (GTK_TOOL_ITEM (widget), FALSE);
2330 gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (widget), FALSE);
2331 gtk_widget_set_size_request (GTK_WIDGET (widget), static_button_size, -1);
2332 widget = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/ToolbarMessageReplyAll");
2333 gtk_tool_item_set_expand (GTK_TOOL_ITEM (widget), FALSE);
2334 gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (widget), FALSE);
2335 gtk_widget_set_size_request (GTK_WIDGET (widget), static_button_size, -1);
2336 widget = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/ToolbarDeleteMessage");
2337 gtk_tool_item_set_expand (GTK_TOOL_ITEM (widget), FALSE);
2338 gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (widget), FALSE);
2339 gtk_widget_set_size_request (GTK_WIDGET (widget), static_button_size, -1);
2340 widget = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/ToolbarDownloadExternalImages");
2341 gtk_tool_item_set_expand (GTK_TOOL_ITEM (widget), FALSE);
2342 gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (widget), FALSE);
2343 gtk_widget_set_size_request (GTK_WIDGET (widget), static_button_size, -1);
2345 gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (priv->next_toolitem), TRUE);
2346 gtk_tool_item_set_expand (GTK_TOOL_ITEM (priv->next_toolitem), TRUE);
2347 gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (priv->prev_toolitem), TRUE);
2348 gtk_tool_item_set_expand (GTK_TOOL_ITEM (priv->prev_toolitem), TRUE);
2353 modest_msg_view_window_show_toolbar (ModestWindow *self,
2354 gboolean show_toolbar)
2356 ModestMsgViewWindowPrivate *priv = NULL;
2357 ModestWindowPrivate *parent_priv;
2359 parent_priv = MODEST_WINDOW_GET_PRIVATE(self);
2360 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(self);
2362 /* Set optimized view status */
2363 priv->optimized_view = !show_toolbar;
2365 if (!parent_priv->toolbar) {
2366 parent_priv->toolbar = gtk_ui_manager_get_widget (parent_priv->ui_manager,
2368 gtk_toolbar_set_icon_size (GTK_TOOLBAR (parent_priv->toolbar), HILDON_ICON_SIZE_FINGER);
2369 gtk_widget_set_no_show_all (parent_priv->toolbar, TRUE);
2371 priv->next_toolitem = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/ToolbarMessageNext");
2372 priv->prev_toolitem = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/ToolbarMessageBack");
2373 toolbar_resize (MODEST_MSG_VIEW_WINDOW (self));
2376 hildon_window_add_toolbar (HILDON_WINDOW (self),
2377 GTK_TOOLBAR (parent_priv->toolbar));
2382 /* Quick hack: this prevents toolbar icons "dance" when progress bar show status is changed */
2383 /* TODO: resize mode migth be GTK_RESIZE_QUEUE, in order to avoid unneccesary shows */
2384 gtk_container_set_resize_mode (GTK_CONTAINER(parent_priv->toolbar), GTK_RESIZE_IMMEDIATE);
2386 gtk_widget_show (GTK_WIDGET (parent_priv->toolbar));
2387 if (modest_msg_view_window_transfer_mode_enabled (MODEST_MSG_VIEW_WINDOW (self)))
2388 set_progress_hint (MODEST_MSG_VIEW_WINDOW (self), TRUE);
2390 set_progress_hint (MODEST_MSG_VIEW_WINDOW (self), FALSE);
2393 gtk_widget_set_no_show_all (parent_priv->toolbar, TRUE);
2394 gtk_widget_hide (GTK_WIDGET (parent_priv->toolbar));
2399 modest_msg_view_window_clipboard_owner_change (GtkClipboard *clipboard,
2401 ModestMsgViewWindow *window)
2403 if (!GTK_WIDGET_VISIBLE (window))
2406 modest_window_check_dimming_rules_group (MODEST_WINDOW (window), MODEST_DIMMING_RULES_CLIPBOARD);
2410 modest_msg_view_window_transfer_mode_enabled (ModestMsgViewWindow *self)
2412 ModestMsgViewWindowPrivate *priv;
2414 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self), FALSE);
2415 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(self);
2417 return priv->progress_hint;
2421 observers_empty (ModestMsgViewWindow *self)
2424 ModestMsgViewWindowPrivate *priv;
2425 gboolean is_empty = TRUE;
2426 guint pending_ops = 0;
2428 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(self);
2429 tmp = priv->progress_widgets;
2431 /* Check all observers */
2432 while (tmp && is_empty) {
2433 pending_ops = modest_progress_object_num_pending_operations (MODEST_PROGRESS_OBJECT(tmp->data));
2434 is_empty = pending_ops == 0;
2436 tmp = g_slist_next(tmp);
2443 on_account_removed (TnyAccountStore *account_store,
2444 TnyAccount *account,
2447 /* Do nothing if it's a transport account, because we only
2448 show the messages of a store account */
2449 if (tny_account_get_account_type(account) == TNY_ACCOUNT_TYPE_STORE) {
2450 const gchar *parent_acc = NULL;
2451 const gchar *our_acc = NULL;
2453 our_acc = modest_window_get_active_account (MODEST_WINDOW (user_data));
2454 parent_acc = modest_tny_account_get_parent_modest_account_name_for_server_account (account);
2456 /* Close this window if I'm showing a message of the removed account */
2457 if (our_acc && parent_acc && strcmp (parent_acc, our_acc) == 0)
2458 modest_ui_actions_on_close_window (NULL, MODEST_WINDOW (user_data));
2463 on_mail_operation_started (ModestMailOperation *mail_op,
2466 ModestMsgViewWindow *self;
2467 ModestMailOperationTypeOperation op_type;
2469 ModestMsgViewWindowPrivate *priv;
2470 GObject *source = NULL;
2472 self = MODEST_MSG_VIEW_WINDOW (user_data);
2473 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
2474 op_type = modest_mail_operation_get_type_operation (mail_op);
2475 tmp = priv->progress_widgets;
2476 source = modest_mail_operation_get_source(mail_op);
2477 if (G_OBJECT (self) == source) {
2478 if (op_type == MODEST_MAIL_OPERATION_TYPE_RECEIVE ||
2479 op_type == MODEST_MAIL_OPERATION_TYPE_OPEN ||
2480 op_type == MODEST_MAIL_OPERATION_TYPE_DELETE) {
2481 set_progress_hint (self, TRUE);
2483 modest_progress_object_add_operation (
2484 MODEST_PROGRESS_OBJECT (tmp->data),
2486 tmp = g_slist_next (tmp);
2490 g_object_unref (source);
2492 /* Update dimming rules */
2493 check_dimming_rules_after_change (self);
2497 on_mail_operation_finished (ModestMailOperation *mail_op,
2500 ModestMsgViewWindow *self;
2501 ModestMailOperationTypeOperation op_type;
2503 ModestMsgViewWindowPrivate *priv;
2505 self = MODEST_MSG_VIEW_WINDOW (user_data);
2506 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
2507 op_type = modest_mail_operation_get_type_operation (mail_op);
2508 tmp = priv->progress_widgets;
2510 if (op_type == MODEST_MAIL_OPERATION_TYPE_RECEIVE ||
2511 op_type == MODEST_MAIL_OPERATION_TYPE_OPEN ||
2512 op_type == MODEST_MAIL_OPERATION_TYPE_DELETE) {
2514 modest_progress_object_remove_operation (MODEST_PROGRESS_OBJECT (tmp->data),
2516 tmp = g_slist_next (tmp);
2519 /* If no more operations are being observed, NORMAL mode is enabled again */
2520 if (observers_empty (self)) {
2521 set_progress_hint (self, FALSE);
2525 /* Update dimming rules. We have to do this right here
2526 and not in view_msg_cb because at that point the
2527 transfer mode is still enabled so the dimming rule
2528 won't let the user delete the message that has been
2529 readed for example */
2530 check_dimming_rules_after_change (self);
2534 on_queue_changed (ModestMailOperationQueue *queue,
2535 ModestMailOperation *mail_op,
2536 ModestMailOperationQueueNotification type,
2537 ModestMsgViewWindow *self)
2539 ModestMsgViewWindowPrivate *priv;
2541 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
2543 /* If this operations was created by another window, do nothing */
2544 if (!modest_mail_operation_is_mine (mail_op, G_OBJECT(self)))
2547 if (type == MODEST_MAIL_OPERATION_QUEUE_OPERATION_ADDED) {
2548 priv->sighandlers = modest_signal_mgr_connect (priv->sighandlers,
2550 "operation-started",
2551 G_CALLBACK (on_mail_operation_started),
2553 priv->sighandlers = modest_signal_mgr_connect (priv->sighandlers,
2555 "operation-finished",
2556 G_CALLBACK (on_mail_operation_finished),
2558 } else if (type == MODEST_MAIL_OPERATION_QUEUE_OPERATION_REMOVED) {
2559 priv->sighandlers = modest_signal_mgr_disconnect (priv->sighandlers,
2561 "operation-started");
2562 priv->sighandlers = modest_signal_mgr_disconnect (priv->sighandlers,
2564 "operation-finished");
2569 modest_msg_view_window_get_attachments (ModestMsgViewWindow *win)
2571 ModestMsgViewWindowPrivate *priv;
2572 TnyList *selected_attachments = NULL;
2574 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (win), NULL);
2575 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (win);
2577 /* In Hildon 2.2 as there's no selection we assume we have all attachments selected */
2578 selected_attachments = modest_msg_view_get_attachments (MODEST_MSG_VIEW (priv->msg_view));
2580 return selected_attachments;
2584 ModestMsgViewWindow *self;
2586 } DecodeAsyncHelper;
2589 on_decode_to_stream_async_handler (TnyMimePart *mime_part,
2595 DecodeAsyncHelper *helper = (DecodeAsyncHelper *) user_data;
2597 /* It could happen that the window was closed */
2598 if (GTK_WIDGET_VISIBLE (helper->self))
2599 set_progress_hint (helper->self, FALSE);
2601 if (cancelled || err) {
2603 gchar *msg = g_strdup_printf (_KR("cerm_device_memory_full"), "");
2604 modest_platform_information_banner (NULL, NULL, msg);
2610 /* make the file read-only */
2611 g_chmod(helper->file_path, 0444);
2613 /* Activate the file */
2614 modest_platform_activate_file (helper->file_path, tny_mime_part_get_content_type (mime_part));
2618 g_object_unref (helper->self);
2619 g_free (helper->file_path);
2620 g_slice_free (DecodeAsyncHelper, helper);
2624 modest_msg_view_window_view_attachment (ModestMsgViewWindow *window,
2625 TnyMimePart *mime_part)
2627 ModestMsgViewWindowPrivate *priv;
2628 const gchar *msg_uid;
2629 gchar *attachment_uid = NULL;
2630 gint attachment_index = 0;
2631 TnyList *attachments;
2633 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window));
2634 g_return_if_fail (TNY_IS_MIME_PART (mime_part) || (mime_part == NULL));
2635 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
2637 msg_uid = modest_msg_view_window_get_message_uid (MODEST_MSG_VIEW_WINDOW (window));
2638 attachments = modest_msg_view_get_attachments (MODEST_MSG_VIEW (priv->msg_view));
2639 attachment_index = modest_list_index (attachments, (GObject *) mime_part);
2640 g_object_unref (attachments);
2642 if (msg_uid && attachment_index >= 0) {
2643 attachment_uid = g_strdup_printf ("%s/%d", msg_uid, attachment_index);
2646 if (mime_part == NULL) {
2647 gboolean error = FALSE;
2648 TnyList *selected_attachments = modest_msg_view_get_selected_attachments (MODEST_MSG_VIEW (priv->msg_view));
2649 if (selected_attachments == NULL || tny_list_get_length (selected_attachments) == 0) {
2651 } else if (tny_list_get_length (selected_attachments) > 1) {
2652 hildon_banner_show_information (NULL, NULL, _("mcen_ib_unable_to_display_more"));
2656 iter = tny_list_create_iterator (selected_attachments);
2657 mime_part = (TnyMimePart *) tny_iterator_get_current (iter);
2658 g_object_unref (iter);
2660 if (selected_attachments)
2661 g_object_unref (selected_attachments);
2666 g_object_ref (mime_part);
2669 if (tny_mime_part_is_purged (mime_part))
2672 if (!modest_tny_mime_part_is_msg (mime_part) && tny_mime_part_get_filename (mime_part)) {
2673 gchar *filepath = NULL;
2674 const gchar *att_filename = tny_mime_part_get_filename (mime_part);
2675 gboolean show_error_banner = FALSE;
2676 TnyFsStream *temp_stream = NULL;
2677 temp_stream = modest_utils_create_temp_stream (att_filename, attachment_uid,
2680 if (temp_stream != NULL) {
2681 ModestAccountMgr *mgr;
2682 DecodeAsyncHelper *helper;
2683 gboolean decode_in_provider;
2684 ModestProtocol *protocol;
2685 const gchar *account;
2687 /* Activate progress hint */
2688 set_progress_hint (window, TRUE);
2690 helper = g_slice_new0 (DecodeAsyncHelper);
2691 helper->self = g_object_ref (window);
2692 helper->file_path = g_strdup (filepath);
2694 decode_in_provider = FALSE;
2695 mgr = modest_runtime_get_account_mgr ();
2696 account = modest_window_get_active_account (MODEST_WINDOW (window));
2697 if (modest_account_mgr_account_is_multimailbox (mgr, account, &protocol)) {
2698 if (MODEST_IS_ACCOUNT_PROTOCOL (protocol)) {
2699 decode_in_provider =
2700 modest_account_protocol_decode_part_to_stream_async (
2701 MODEST_ACCOUNT_PROTOCOL (protocol),
2704 TNY_STREAM (temp_stream),
2705 on_decode_to_stream_async_handler,
2711 if (!decode_in_provider)
2712 tny_mime_part_decode_to_stream_async (mime_part, TNY_STREAM (temp_stream),
2713 on_decode_to_stream_async_handler,
2716 g_object_unref (temp_stream);
2717 /* NOTE: files in the temporary area will be automatically
2718 * cleaned after some time if they are no longer in use */
2721 const gchar *content_type;
2722 /* the file may already exist but it isn't writable,
2723 * let's try to open it anyway */
2724 content_type = tny_mime_part_get_content_type (mime_part);
2725 modest_platform_activate_file (filepath, content_type);
2727 g_warning ("%s: modest_utils_create_temp_stream failed", __FUNCTION__);
2728 show_error_banner = TRUE;
2733 if (show_error_banner)
2734 modest_platform_information_banner (NULL, NULL, _("mail_ib_file_operation_failed"));
2735 } else if (!modest_tny_mime_part_is_msg (mime_part)) {
2736 ModestWindowMgr *mgr;
2737 ModestWindow *msg_win = NULL;
2738 TnyMsg *current_msg;
2742 current_msg = modest_msg_view_window_get_message (MODEST_MSG_VIEW_WINDOW (window));
2743 mgr = modest_runtime_get_window_mgr ();
2744 header = tny_msg_get_header (TNY_MSG (current_msg));
2745 found = modest_window_mgr_find_registered_message_uid (mgr,
2750 g_debug ("window for this body is already being created");
2753 /* it's not found, so create a new window for it */
2754 modest_window_mgr_register_header (mgr, header, attachment_uid); /* register the uid before building the window */
2755 gchar *account = g_strdup (modest_window_get_active_account (MODEST_WINDOW (window)));
2756 const gchar *mailbox = modest_window_get_active_mailbox (MODEST_WINDOW (window));
2758 account = modest_account_mgr_get_default_account (modest_runtime_get_account_mgr ());
2760 msg_win = modest_msg_view_window_new_with_other_body (TNY_MSG (current_msg), TNY_MIME_PART (mime_part),
2761 account, mailbox, attachment_uid);
2763 modest_window_set_zoom (MODEST_WINDOW (msg_win),
2764 modest_window_get_zoom (MODEST_WINDOW (window)));
2765 if (modest_window_mgr_register_window (mgr, msg_win, MODEST_WINDOW (window)))
2766 gtk_widget_show_all (GTK_WIDGET (msg_win));
2768 gtk_widget_destroy (GTK_WIDGET (msg_win));
2770 g_object_unref (current_msg);
2772 /* message attachment */
2773 TnyHeader *header = NULL;
2774 ModestWindowMgr *mgr;
2775 ModestWindow *msg_win = NULL;
2778 header = tny_msg_get_header (TNY_MSG (mime_part));
2779 mgr = modest_runtime_get_window_mgr ();
2780 found = modest_window_mgr_find_registered_header (mgr, header, &msg_win);
2783 /* if it's found, but there is no msg_win, it's probably in the process of being created;
2784 * thus, we don't do anything */
2785 g_debug ("window for is already being created");
2787 /* it's not found, so create a new window for it */
2788 modest_window_mgr_register_header (mgr, header, attachment_uid); /* register the uid before building the window */
2789 gchar *account = g_strdup (modest_window_get_active_account (MODEST_WINDOW (window)));
2790 const gchar *mailbox = modest_window_get_active_mailbox (MODEST_WINDOW (window));
2792 account = modest_account_mgr_get_default_account (modest_runtime_get_account_mgr ());
2793 msg_win = modest_msg_view_window_new_for_attachment (TNY_MSG (mime_part), account,
2794 mailbox, attachment_uid);
2795 modest_window_set_zoom (MODEST_WINDOW (msg_win),
2796 modest_window_get_zoom (MODEST_WINDOW (window)));
2797 if (modest_window_mgr_register_window (mgr, msg_win, MODEST_WINDOW (window)))
2798 gtk_widget_show_all (GTK_WIDGET (msg_win));
2800 gtk_widget_destroy (GTK_WIDGET (msg_win));
2806 g_free (attachment_uid);
2808 g_object_unref (mime_part);
2820 GnomeVFSResult result;
2822 ModestMsgViewWindow *window;
2825 static void save_mime_part_info_free (SaveMimePartInfo *info, gboolean with_struct);
2826 static gboolean idle_save_mime_part_show_result (SaveMimePartInfo *info);
2827 static gpointer save_mime_part_to_file (SaveMimePartInfo *info);
2828 static void save_mime_parts_to_file_with_checks (GtkWindow *parent, SaveMimePartInfo *info);
2831 save_mime_part_info_free (SaveMimePartInfo *info, gboolean with_struct)
2835 for (node = info->pairs; node != NULL; node = g_list_next (node)) {
2836 SaveMimePartPair *pair = (SaveMimePartPair *) node->data;
2837 g_free (pair->filename);
2838 g_object_unref (pair->part);
2839 g_slice_free (SaveMimePartPair, pair);
2841 g_list_free (info->pairs);
2844 g_object_unref (info->window);
2845 info->window = NULL;
2847 g_slice_free (SaveMimePartInfo, info);
2852 idle_save_mime_part_show_result (SaveMimePartInfo *info)
2854 /* This is a GDK lock because we are an idle callback and
2855 * hildon_banner_show_information is or does Gtk+ code */
2857 gdk_threads_enter (); /* CHECKED */
2858 if (info->result == GNOME_VFS_OK) {
2859 hildon_banner_show_information (NULL, NULL, _CS("sfil_ib_saved"));
2860 } else if (info->result == GNOME_VFS_ERROR_NO_SPACE) {
2863 /* Check if the uri belongs to the external mmc */
2864 if (g_str_has_prefix (info->uri, g_getenv (MODEST_MMC1_VOLUMEPATH_ENV)))
2865 msg = g_strdup_printf (_KR("cerm_device_memory_full"), "");
2867 msg = g_strdup (_KR("cerm_memory_card_full"));
2868 modest_platform_information_banner (NULL, NULL, msg);
2871 hildon_banner_show_information (NULL, NULL, _("mail_ib_file_operation_failed"));
2873 save_mime_part_info_free (info, FALSE);
2874 gdk_threads_leave (); /* CHECKED */
2880 save_mime_part_to_file (SaveMimePartInfo *info)
2882 GnomeVFSHandle *handle;
2884 SaveMimePartPair *pair = (SaveMimePartPair *) info->pairs->data;
2886 info->result = gnome_vfs_create (&handle, pair->filename, GNOME_VFS_OPEN_WRITE, FALSE, 0644);
2887 if (info->result == GNOME_VFS_OK) {
2888 GError *error = NULL;
2889 gboolean decode_in_provider;
2891 ModestAccountMgr *mgr;
2892 const gchar *account;
2893 ModestProtocol *protocol = NULL;
2895 stream = tny_vfs_stream_new (handle);
2897 decode_in_provider = FALSE;
2898 mgr = modest_runtime_get_account_mgr ();
2899 account = modest_window_get_active_account (MODEST_WINDOW (info->window));
2900 if (modest_account_mgr_account_is_multimailbox (mgr, account, &protocol)) {
2901 if (MODEST_IS_ACCOUNT_PROTOCOL (protocol)) {
2902 decode_in_provider =
2903 modest_account_protocol_decode_part_to_stream (
2904 MODEST_ACCOUNT_PROTOCOL (protocol),
2912 if (!decode_in_provider)
2913 written = tny_mime_part_decode_to_stream (pair->part, stream, &error);
2916 g_warning ("modest: could not save attachment %s: %d (%s)\n", pair->filename, error?error->code:-1, error?error->message:"Unknown error");
2918 if ((error->domain == TNY_ERROR_DOMAIN) &&
2919 (error->code == TNY_IO_ERROR_WRITE) &&
2920 (errno == ENOSPC)) {
2921 info->result = GNOME_VFS_ERROR_NO_SPACE;
2923 info->result = GNOME_VFS_ERROR_IO;
2926 g_object_unref (G_OBJECT (stream));
2928 g_warning ("Could not create save attachment %s: %s\n",
2929 pair->filename, gnome_vfs_result_to_string (info->result));
2932 /* Go on saving remaining files */
2933 info->pairs = g_list_remove_link (info->pairs, info->pairs);
2934 if (info->pairs != NULL) {
2935 save_mime_part_to_file (info);
2937 g_idle_add ((GSourceFunc) idle_save_mime_part_show_result, info);
2944 save_mime_parts_to_file_with_checks (GtkWindow *parent,
2945 SaveMimePartInfo *info)
2947 gboolean is_ok = TRUE;
2948 gint replaced_files = 0;
2949 const GList *files = info->pairs;
2950 const GList *iter, *to_replace = NULL;
2952 for (iter = files; (iter != NULL) && (replaced_files < 2); iter = g_list_next(iter)) {
2953 SaveMimePartPair *pair = iter->data;
2954 gchar *unescaped = g_uri_unescape_string (pair->filename, NULL);
2956 if (modest_utils_file_exists (unescaped)) {
2958 if (replaced_files == 1)
2963 if (replaced_files) {
2966 if (replaced_files == 1) {
2967 SaveMimePartPair *pair = to_replace->data;
2968 const gchar *basename = strrchr (pair->filename, G_DIR_SEPARATOR) + 1;
2969 gchar *escaped_basename, *message;
2971 escaped_basename = g_uri_unescape_string (basename, NULL);
2972 message = g_strdup_printf ("%s\n%s",
2973 _FM("docm_nc_replace_file"),
2974 (escaped_basename) ? escaped_basename : "");
2975 response = modest_platform_run_confirmation_dialog (parent, message);
2977 g_free (escaped_basename);
2979 response = modest_platform_run_confirmation_dialog (parent,
2980 _FM("docm_nc_replace_multiple"));
2982 if (response != GTK_RESPONSE_OK)
2987 save_mime_part_info_free (info, TRUE);
2989 g_thread_create ((GThreadFunc)save_mime_part_to_file, info, FALSE, NULL);
2994 typedef struct _SaveAttachmentsInfo {
2995 TnyList *attachments_list;
2996 ModestMsgViewWindow *window;
2997 } SaveAttachmentsInfo;
3000 save_attachments_response (GtkDialog *dialog,
3004 TnyList *mime_parts;
3006 GList *files_to_save = NULL;
3007 gchar *current_folder;
3008 SaveAttachmentsInfo *sa_info = (SaveAttachmentsInfo *) user_data;
3010 mime_parts = TNY_LIST (sa_info->attachments_list);
3012 if (arg1 != GTK_RESPONSE_OK)
3015 chooser_uri = gtk_file_chooser_get_uri (GTK_FILE_CHOOSER (dialog));
3016 current_folder = gtk_file_chooser_get_current_folder_uri (GTK_FILE_CHOOSER (dialog));
3017 if (current_folder && *current_folder != '\0') {
3019 modest_conf_set_string (modest_runtime_get_conf (), MODEST_CONF_LATEST_SAVE_ATTACHMENT_PATH,
3020 current_folder,&err);
3022 g_debug ("Error storing latest used folder: %s", err->message);
3026 g_free (current_folder);
3028 if (!modest_utils_folder_writable (chooser_uri)) {
3029 const gchar *err_msg;
3031 #ifdef MODEST_PLATFORM_MAEMO
3032 if (modest_maemo_utils_in_usb_mode ()) {
3033 err_msg = dgettext ("hildon-status-bar-usb", "usbh_ib_mmc_usb_connected");
3035 err_msg = _FM("sfil_ib_readonly_location");
3038 err_msg = _FM("sfil_ib_readonly_location");
3040 hildon_banner_show_information (NULL, NULL, err_msg);
3044 iter = tny_list_create_iterator (mime_parts);
3045 while (!tny_iterator_is_done (iter)) {
3046 TnyMimePart *mime_part = (TnyMimePart *) tny_iterator_get_current (iter);
3048 if ((modest_tny_mime_part_is_attachment_for_modest (mime_part)) &&
3049 !tny_mime_part_is_purged (mime_part) &&
3050 (tny_mime_part_get_filename (mime_part) != NULL)) {
3051 SaveMimePartPair *pair;
3053 pair = g_slice_new0 (SaveMimePartPair);
3055 if (tny_list_get_length (mime_parts) > 1) {
3057 gnome_vfs_escape_slashes (tny_mime_part_get_filename (mime_part));
3058 pair->filename = g_build_filename (chooser_uri, escaped, NULL);
3061 pair->filename = g_strdup (chooser_uri);
3063 pair->part = mime_part;
3064 files_to_save = g_list_prepend (files_to_save, pair);
3066 tny_iterator_next (iter);
3068 g_object_unref (iter);
3071 if (files_to_save != NULL) {
3072 SaveMimePartInfo *info = g_slice_new0 (SaveMimePartInfo);
3073 info->pairs = files_to_save;
3074 info->result = TRUE;
3075 info->uri = g_strdup (chooser_uri);
3076 info->window = g_object_ref (sa_info->window);
3077 save_mime_parts_to_file_with_checks ((GtkWindow *) dialog, info);
3079 g_free (chooser_uri);
3082 /* Free and close the dialog */
3083 g_object_unref (mime_parts);
3084 g_object_unref (sa_info->window);
3085 g_slice_free (SaveAttachmentsInfo, sa_info);
3086 gtk_widget_destroy (GTK_WIDGET (dialog));
3090 modest_msg_view_window_save_attachments (ModestMsgViewWindow *window,
3091 TnyList *mime_parts)
3093 ModestMsgViewWindowPrivate *priv;
3094 GtkWidget *save_dialog = NULL;
3095 gchar *conf_folder = NULL;
3096 gchar *filename = NULL;
3097 gchar *save_multiple_str = NULL;
3098 const gchar *root_folder = "file:///";
3100 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window));
3101 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
3103 if (mime_parts == NULL) {
3104 /* In Hildon 2.2 save and delete operate over all the attachments as there's no
3105 * selection available */
3106 mime_parts = modest_msg_view_get_attachments (MODEST_MSG_VIEW (priv->msg_view));
3107 if (mime_parts && !modest_maemo_utils_select_attachments (GTK_WINDOW (window), mime_parts, FALSE)) {
3108 g_object_unref (mime_parts);
3111 if (mime_parts == NULL || tny_list_get_length (mime_parts) == 0) {
3113 g_object_unref (mime_parts);
3119 g_object_ref (mime_parts);
3122 /* prepare dialog */
3123 if (tny_list_get_length (mime_parts) == 1) {
3125 /* only one attachment selected */
3126 iter = tny_list_create_iterator (mime_parts);
3127 TnyMimePart *mime_part = (TnyMimePart *) tny_iterator_get_current (iter);
3128 g_object_unref (iter);
3129 if (!modest_tny_mime_part_is_msg (mime_part) &&
3130 modest_tny_mime_part_is_attachment_for_modest (mime_part) &&
3131 !tny_mime_part_is_purged (mime_part)) {
3132 filename = g_strdup (tny_mime_part_get_filename (mime_part));
3134 /* TODO: show any error? */
3135 g_warning ("%s: Tried to save a non-file attachment", __FUNCTION__);
3136 g_object_unref (mime_parts);
3139 g_object_unref (mime_part);
3141 gint num = tny_list_get_length (mime_parts);
3142 save_multiple_str = g_strdup_printf (dngettext("hildon-fm",
3143 "sfil_va_number_of_objects_attachment",
3144 "sfil_va_number_of_objects_attachments",
3148 save_dialog = hildon_file_chooser_dialog_new (GTK_WINDOW (window),
3149 GTK_FILE_CHOOSER_ACTION_SAVE);
3151 /* Get last used folder */
3152 conf_folder = modest_conf_get_string (modest_runtime_get_conf (),
3153 MODEST_CONF_LATEST_SAVE_ATTACHMENT_PATH, NULL);
3155 /* File chooser stops working if we select "file:///" as current folder */
3156 if (conf_folder && g_ascii_strcasecmp (root_folder, conf_folder) != 0) {
3157 g_free (conf_folder);
3161 if (conf_folder && conf_folder[0] != '\0') {
3162 gtk_file_chooser_set_current_folder_uri (GTK_FILE_CHOOSER (save_dialog), conf_folder);
3165 /* Set the default folder to documents folder */
3166 docs_folder = (gchar *) g_strdup(g_get_user_special_dir (G_USER_DIRECTORY_DOCUMENTS));
3169 docs_folder = g_build_filename (g_getenv (MYDOCS_ENV), DOCS_FOLDER, NULL);
3171 gtk_file_chooser_set_current_folder (GTK_FILE_CHOOSER (save_dialog), docs_folder);
3172 g_free (docs_folder);
3174 g_free (conf_folder);
3178 gtk_file_chooser_set_current_name (GTK_FILE_CHOOSER (save_dialog),
3183 /* if multiple, set multiple string */
3184 if (save_multiple_str) {
3185 g_object_set (G_OBJECT (save_dialog), "save-multiple", save_multiple_str, NULL);
3186 gtk_window_set_title (GTK_WINDOW (save_dialog), _FM("sfil_ti_save_objects_files"));
3187 g_free (save_multiple_str);
3190 /* We must run this asynchronously, because the hildon dialog
3191 performs a gtk_dialog_run by itself which leads to gdk
3193 SaveAttachmentsInfo *sa_info;
3194 sa_info = g_slice_new (SaveAttachmentsInfo);
3195 sa_info->attachments_list = mime_parts;
3196 sa_info->window = g_object_ref (window);
3197 g_signal_connect (save_dialog, "response",
3198 G_CALLBACK (save_attachments_response), sa_info);
3200 gtk_widget_show_all (save_dialog);
3204 show_remove_attachment_information (gpointer userdata)
3206 ModestMsgViewWindow *window = (ModestMsgViewWindow *) userdata;
3207 ModestMsgViewWindowPrivate *priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
3209 /* We're outside the main lock */
3210 gdk_threads_enter ();
3212 if (priv->remove_attachment_banner != NULL) {
3213 gtk_widget_destroy (priv->remove_attachment_banner);
3214 g_object_unref (priv->remove_attachment_banner);
3217 priv->remove_attachment_banner = g_object_ref (
3218 hildon_banner_show_animation (NULL, NULL, _("mcen_me_inbox_remove_attachments")));
3220 gdk_threads_leave ();
3226 modest_msg_view_window_remove_attachments (ModestMsgViewWindow *window, gboolean get_all)
3228 ModestMsgViewWindowPrivate *priv;
3229 TnyList *mime_parts = NULL, *tmp;
3230 gchar *confirmation_message;
3236 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window));
3237 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
3239 /* In hildon 2.2 we ignore the get_all flag as we always get all attachments. This is
3240 * because we don't have selection
3242 mime_parts = modest_msg_view_get_attachments (MODEST_MSG_VIEW (priv->msg_view));
3244 /* Remove already purged messages from mime parts list. We use
3245 a copy of the list to remove items in the original one */
3246 tmp = tny_list_copy (mime_parts);
3247 iter = tny_list_create_iterator (tmp);
3248 while (!tny_iterator_is_done (iter)) {
3249 TnyMimePart *part = TNY_MIME_PART (tny_iterator_get_current (iter));
3250 if (tny_mime_part_is_purged (part))
3251 tny_list_remove (mime_parts, (GObject *) part);
3253 g_object_unref (part);
3254 tny_iterator_next (iter);
3256 g_object_unref (tmp);
3257 g_object_unref (iter);
3259 if (!modest_maemo_utils_select_attachments (GTK_WINDOW (window), mime_parts, TRUE) ||
3260 tny_list_get_length (mime_parts) == 0) {
3261 g_object_unref (mime_parts);
3265 n_attachments = tny_list_get_length (mime_parts);
3266 if (n_attachments == 1) {
3270 iter = tny_list_create_iterator (mime_parts);
3271 part = (TnyMimePart *) tny_iterator_get_current (iter);
3272 g_object_unref (iter);
3273 if (modest_tny_mime_part_is_msg (part)) {
3275 header = tny_msg_get_header (TNY_MSG (part));
3276 filename = tny_header_dup_subject (header);
3277 g_object_unref (header);
3278 if (filename == NULL)
3279 filename = g_strdup (_("mail_va_no_subject"));
3281 filename = g_strdup (tny_mime_part_get_filename (TNY_MIME_PART (part)));
3283 confirmation_message = g_strdup_printf (_("mcen_nc_purge_file_text"), filename);
3285 g_object_unref (part);
3287 confirmation_message = g_strdup_printf (ngettext("mcen_nc_purge_file_text",
3288 "mcen_nc_purge_files_text",
3289 n_attachments), n_attachments);
3291 response = modest_platform_run_confirmation_dialog (GTK_WINDOW (window),
3292 confirmation_message);
3293 g_free (confirmation_message);
3295 if (response != GTK_RESPONSE_OK) {
3296 g_object_unref (mime_parts);
3300 priv->purge_timeout = g_timeout_add (2000, show_remove_attachment_information, window);
3302 iter = tny_list_create_iterator (mime_parts);
3303 while (!tny_iterator_is_done (iter)) {
3306 part = (TnyMimePart *) tny_iterator_get_current (iter);
3307 tny_mime_part_set_purged (TNY_MIME_PART (part));
3308 g_object_unref (part);
3309 tny_iterator_next (iter);
3311 g_object_unref (iter);
3313 msg = tny_msg_view_get_msg (TNY_MSG_VIEW (priv->msg_view));
3314 tny_msg_view_clear (TNY_MSG_VIEW (priv->msg_view));
3315 tny_msg_rewrite_cache (msg);
3316 tny_msg_view_set_msg (TNY_MSG_VIEW (priv->msg_view), msg);
3317 g_object_unref (msg);
3318 update_branding (MODEST_MSG_VIEW_WINDOW (window));
3320 g_object_unref (mime_parts);
3322 if (priv->purge_timeout > 0) {
3323 g_source_remove (priv->purge_timeout);
3324 priv->purge_timeout = 0;
3327 if (priv->remove_attachment_banner) {
3328 gtk_widget_destroy (priv->remove_attachment_banner);
3329 g_object_unref (priv->remove_attachment_banner);
3330 priv->remove_attachment_banner = NULL;
3336 update_window_title (ModestMsgViewWindow *window)
3338 ModestMsgViewWindowPrivate *priv;
3340 TnyHeader *header = NULL;
3341 gchar *subject = NULL;
3343 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
3345 /* Note that if the window is closed while we're retrieving
3346 the message, this widget could de deleted */
3347 if (!priv->msg_view)
3350 msg = tny_msg_view_get_msg (TNY_MSG_VIEW (priv->msg_view));
3352 if (priv->other_body) {
3355 description = modest_tny_mime_part_get_header_value (priv->other_body, "Content-Description");
3357 g_strstrip (description);
3358 subject = description;
3360 } else if (msg != NULL) {
3361 header = tny_msg_get_header (msg);
3362 subject = tny_header_dup_subject (header);
3363 g_object_unref (header);
3364 g_object_unref (msg);
3367 if ((subject == NULL)||(subject[0] == '\0')) {
3369 subject = g_strdup (_("mail_va_no_subject"));
3372 gtk_window_set_title (GTK_WINDOW (window), subject);
3377 on_move_focus (GtkWidget *widget,
3378 GtkDirectionType direction,
3381 g_signal_stop_emission_by_name (G_OBJECT (widget), "move-focus");
3385 fetch_image_open_stream (TnyStreamCache *self, gint64 *expected_size, gchar *uri)
3387 GnomeVFSResult result;
3388 GnomeVFSHandle *handle = NULL;
3389 GnomeVFSFileInfo *info = NULL;
3392 result = gnome_vfs_open (&handle, uri, GNOME_VFS_OPEN_READ);
3393 if (result != GNOME_VFS_OK) {
3398 info = gnome_vfs_file_info_new ();
3399 result = gnome_vfs_get_file_info_from_handle (handle, info, GNOME_VFS_FILE_INFO_DEFAULT);
3400 if (result != GNOME_VFS_OK || ! (info->valid_fields & GNOME_VFS_FILE_INFO_FIELDS_SIZE)) {
3401 /* We put a "safe" default size for going to cache */
3402 *expected_size = (300*1024);
3404 *expected_size = info->size;
3406 gnome_vfs_file_info_unref (info);
3408 stream = tny_vfs_stream_new (handle);
3417 TnyStream *output_stream;
3418 GtkWidget *msg_view;
3423 on_fetch_image_idle_refresh_view (gpointer userdata)
3426 FetchImageData *fidata = (FetchImageData *) userdata;
3428 gdk_threads_enter ();
3429 if (GTK_WIDGET_DRAWABLE (fidata->msg_view)) {
3430 ModestMsgViewWindowPrivate *priv;
3432 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (fidata->window);
3433 priv->fetching_images--;
3434 gtk_widget_queue_draw (fidata->msg_view);
3435 update_progress_hint (MODEST_MSG_VIEW_WINDOW (fidata->window));
3437 gdk_threads_leave ();
3439 g_object_unref (fidata->msg_view);
3440 g_object_unref (fidata->window);
3441 g_slice_free (FetchImageData, fidata);
3446 on_fetch_image_thread (gpointer userdata)
3448 FetchImageData *fidata = (FetchImageData *) userdata;
3449 TnyStreamCache *cache;
3450 TnyStream *cache_stream;
3452 cache = modest_runtime_get_images_cache ();
3454 tny_stream_cache_get_stream (cache,
3456 (TnyStreamCacheOpenStreamFetcher) fetch_image_open_stream,
3457 (gpointer) fidata->uri);
3458 g_free (fidata->cache_id);
3459 g_free (fidata->uri);
3461 if (cache_stream != NULL) {
3464 while (G_LIKELY (!tny_stream_is_eos (cache_stream))) {
3467 nb_read = tny_stream_read (cache_stream, buffer, sizeof (buffer));
3468 if (G_UNLIKELY (nb_read < 0)) {
3470 } else if (G_LIKELY (nb_read > 0)) {
3471 gssize nb_written = 0;
3473 while (G_UNLIKELY (nb_written < nb_read)) {
3476 len = tny_stream_write (fidata->output_stream, buffer + nb_written,
3477 nb_read - nb_written);
3478 if (G_UNLIKELY (len < 0))
3484 tny_stream_close (cache_stream);
3485 g_object_unref (cache_stream);
3488 tny_stream_close (fidata->output_stream);
3489 g_object_unref (fidata->output_stream);
3491 g_idle_add (on_fetch_image_idle_refresh_view, fidata);
3497 on_fetch_image (ModestMsgView *msgview,
3500 ModestMsgViewWindow *window)
3502 const gchar *current_account;
3503 ModestMsgViewWindowPrivate *priv;
3504 FetchImageData *fidata;
3506 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
3508 current_account = modest_window_get_active_account (MODEST_WINDOW (window));
3510 fidata = g_slice_new0 (FetchImageData);
3511 fidata->msg_view = g_object_ref (msgview);
3512 fidata->window = g_object_ref (window);
3513 fidata->uri = g_strdup (uri);
3514 fidata->cache_id = modest_images_cache_get_id (current_account, uri);
3515 fidata->output_stream = g_object_ref (stream);
3517 priv->fetching_images++;
3518 if (g_thread_create (on_fetch_image_thread, fidata, FALSE, NULL) == NULL) {
3519 g_object_unref (fidata->output_stream);
3520 g_free (fidata->cache_id);
3521 g_free (fidata->uri);
3522 g_object_unref (fidata->msg_view);
3523 g_slice_free (FetchImageData, fidata);
3524 tny_stream_close (stream);
3525 priv->fetching_images--;
3526 update_progress_hint (window);
3529 update_progress_hint (window);
3535 setup_menu (ModestMsgViewWindow *self)
3537 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW(self));
3539 /* Settings menu buttons */
3540 modest_hildon2_window_add_to_menu (MODEST_HILDON2_WINDOW (self), _("mcen_me_viewer_find"), NULL,
3541 APP_MENU_CALLBACK (modest_msg_view_window_show_find_toolbar),
3542 MODEST_DIMMING_CALLBACK (modest_ui_dimming_rules_on_find_in_msg));
3544 modest_hildon2_window_add_to_menu (MODEST_HILDON2_WINDOW (self),
3545 dngettext(GETTEXT_PACKAGE,
3546 "mcen_me_move_message",
3547 "mcen_me_move_messages",
3550 APP_MENU_CALLBACK (modest_ui_actions_on_move_to),
3551 MODEST_DIMMING_CALLBACK (modest_ui_dimming_rules_on_move_to));
3553 modest_hildon2_window_add_to_menu (MODEST_HILDON2_WINDOW (self), _("mcen_me_inbox_forward"), "<Control>d",
3554 APP_MENU_CALLBACK (modest_ui_actions_on_forward),
3555 MODEST_DIMMING_CALLBACK (modest_ui_dimming_rules_on_reply_msg));
3557 modest_hildon2_window_add_to_menu (MODEST_HILDON2_WINDOW (self), _("mcen_me_inbox_mark_as_read"), NULL,
3558 APP_MENU_CALLBACK (modest_ui_actions_on_mark_as_read),
3559 MODEST_DIMMING_CALLBACK (modest_ui_dimming_rules_on_mark_as_read_msg_in_view));
3561 modest_hildon2_window_add_to_menu (MODEST_HILDON2_WINDOW (self), _("mcen_me_inbox_mark_as_unread"), NULL,
3562 APP_MENU_CALLBACK (modest_ui_actions_on_mark_as_unread),
3563 MODEST_DIMMING_CALLBACK (modest_ui_dimming_rules_on_mark_as_unread_msg_in_view));
3565 modest_hildon2_window_add_to_menu (MODEST_HILDON2_WINDOW (self), _("mcen_me_viewer_save_attachments"), NULL,
3566 APP_MENU_CALLBACK (modest_ui_actions_save_attachments),
3567 MODEST_DIMMING_CALLBACK (modest_ui_dimming_rules_on_save_attachments));
3568 modest_hildon2_window_add_to_menu (MODEST_HILDON2_WINDOW (self), _("mcen_me_inbox_remove_attachments"), NULL,
3569 APP_MENU_CALLBACK (modest_ui_actions_remove_attachments),
3570 MODEST_DIMMING_CALLBACK (modest_ui_dimming_rules_on_remove_attachments));
3572 modest_hildon2_window_add_to_menu (MODEST_HILDON2_WINDOW (self), _("mcen_me_new_message"), "<Control>n",
3573 APP_MENU_CALLBACK (modest_ui_actions_on_new_msg),
3574 MODEST_DIMMING_CALLBACK (modest_ui_dimming_rules_on_new_msg));
3575 modest_hildon2_window_add_to_menu (MODEST_HILDON2_WINDOW (self), _("mcen_me_viewer_addtocontacts"), NULL,
3576 APP_MENU_CALLBACK (modest_ui_actions_add_to_contacts),
3577 MODEST_DIMMING_CALLBACK (modest_ui_dimming_rules_on_add_to_contacts));
3579 modest_hildon2_window_add_to_menu (MODEST_HILDON2_WINDOW (self), _("mcen_ti_message_properties"), NULL,
3580 APP_MENU_CALLBACK (modest_ui_actions_on_details),
3581 MODEST_DIMMING_CALLBACK (modest_ui_dimming_rules_on_details));
3585 modest_msg_view_window_add_to_contacts (ModestMsgViewWindow *self)
3587 ModestMsgViewWindowPrivate *priv;
3588 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
3589 GSList *recipients = NULL;
3591 gboolean contacts_to_add = FALSE;
3593 msg = tny_msg_view_get_msg (TNY_MSG_VIEW (priv->msg_view));
3597 header = modest_msg_view_window_get_header (self);
3600 recipients = modest_tny_msg_header_get_all_recipients_list (header);
3601 g_object_unref (header);
3603 recipients = modest_tny_msg_get_all_recipients_list (msg);
3604 g_object_unref (msg);
3607 if (recipients != NULL) {
3608 GtkWidget *picker_dialog;
3609 GtkWidget *selector;
3611 gchar *selected = NULL;
3613 selector = hildon_touch_selector_new_text ();
3614 g_object_ref (selector);
3616 for (node = recipients; node != NULL; node = g_slist_next (node)) {
3617 if (!modest_address_book_has_address ((const gchar *) node->data)) {
3618 hildon_touch_selector_append_text (HILDON_TOUCH_SELECTOR (selector),
3619 (const gchar *) node->data);
3620 contacts_to_add = TRUE;
3624 if (contacts_to_add) {
3627 picker_dialog = hildon_picker_dialog_new (GTK_WINDOW (self));
3628 gtk_window_set_title (GTK_WINDOW (picker_dialog), _("mcen_me_viewer_addtocontacts"));
3630 hildon_picker_dialog_set_selector (HILDON_PICKER_DIALOG (picker_dialog),
3631 HILDON_TOUCH_SELECTOR (selector));
3633 picker_result = gtk_dialog_run (GTK_DIALOG (picker_dialog));
3635 if (picker_result == GTK_RESPONSE_OK) {
3636 selected = hildon_touch_selector_get_current_text (HILDON_TOUCH_SELECTOR (selector));
3638 gtk_widget_destroy (picker_dialog);
3641 modest_address_book_add_address (selected, (GtkWindow *) self);
3646 g_object_unref (selector);
3651 if (recipients) {g_slist_foreach (recipients, (GFunc) g_free, NULL); g_slist_free (recipients);}
3655 _modest_msg_view_window_map_event (GtkWidget *widget,
3659 ModestMsgViewWindow *self = (ModestMsgViewWindow *) userdata;
3661 update_progress_hint (self);
3667 modest_msg_view_window_fetch_images (ModestMsgViewWindow *self)
3669 ModestMsgViewWindowPrivate *priv;
3670 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
3672 modest_msg_view_request_fetch_images (MODEST_MSG_VIEW (priv->msg_view));
3676 modest_msg_view_window_has_blocked_external_images (ModestMsgViewWindow *self)
3678 ModestMsgViewWindowPrivate *priv;
3679 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
3681 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self), FALSE);
3683 return modest_msg_view_has_blocked_external_images (MODEST_MSG_VIEW (priv->msg_view));
3687 modest_msg_view_window_reload (ModestMsgViewWindow *self)
3689 ModestMsgViewWindowPrivate *priv;
3690 const gchar *msg_uid;
3691 TnyHeader *header = NULL;
3692 TnyFolder *folder = NULL;
3694 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self));
3696 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
3698 header = modest_msg_view_window_get_header (MODEST_MSG_VIEW_WINDOW (self));
3702 folder = tny_header_get_folder (header);
3703 g_object_unref (header);
3708 msg_uid = modest_msg_view_window_get_message_uid (self);
3710 GtkTreeRowReference *row_reference;
3712 if (priv->row_reference && gtk_tree_row_reference_valid (priv->row_reference)) {
3713 row_reference = priv->row_reference;
3715 row_reference = NULL;
3717 if (!message_reader (self, priv, NULL, msg_uid, folder, row_reference))
3718 g_warning ("Shouldn't happen, trying to reload a message failed");
3721 g_object_unref (folder);
3725 update_branding (ModestMsgViewWindow *self)
3727 const gchar *account;
3728 const gchar *mailbox;
3729 ModestAccountMgr *mgr;
3730 ModestProtocol *protocol = NULL;
3731 gchar *service_name = NULL;
3732 const GdkPixbuf *service_icon = NULL;
3733 ModestMsgViewWindowPrivate *priv;
3735 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
3737 account = modest_window_get_active_account (MODEST_WINDOW (self));
3738 mailbox = modest_window_get_active_mailbox (MODEST_WINDOW (self));
3740 mgr = modest_runtime_get_account_mgr ();
3742 if (modest_account_mgr_account_is_multimailbox (mgr, account, &protocol)) {
3743 if (MODEST_IS_ACCOUNT_PROTOCOL (protocol)) {
3744 service_name = modest_account_protocol_get_service_name (MODEST_ACCOUNT_PROTOCOL (protocol),
3746 service_icon = modest_account_protocol_get_service_icon (MODEST_ACCOUNT_PROTOCOL (protocol),
3747 account, mailbox, MODEST_ICON_SIZE_SMALL);
3751 modest_msg_view_set_branding (MODEST_MSG_VIEW (priv->msg_view), service_name, service_icon);
3752 g_free (service_name);