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>
70 #include <modest-hildon2-window-mgr.h>
71 #include <tny-camel-msg.h>
72 #include <tny-camel-bs-mime-part.h>
73 #include <tny-camel-bs-msg.h>
75 #define MYDOCS_ENV "MYDOCSDIR"
76 #define DOCS_FOLDER ".documents"
78 typedef struct _ModestMsgViewWindowPrivate ModestMsgViewWindowPrivate;
79 struct _ModestMsgViewWindowPrivate {
82 GtkWidget *main_scroll;
83 GtkWidget *find_toolbar;
86 /* Progress observers */
87 GSList *progress_widgets;
90 GtkWidget *prev_toolitem;
91 GtkWidget *next_toolitem;
92 gboolean progress_hint;
95 /* Optimized view enabled */
96 gboolean optimized_view;
98 /* Whether this was created via the *_new_for_search_result() function. */
99 gboolean is_search_result;
101 /* Whether the message is in outbox */
104 /* A reference to the @model of the header view
105 * to allow selecting previous/next messages,
106 * if the message is currently selected in the header view.
108 const gchar *header_folder_id;
109 GtkTreeModel *header_model;
110 GtkTreeRowReference *row_reference;
111 GtkTreeRowReference *next_row_reference;
113 gulong clipboard_change_handler;
114 gulong queue_change_handler;
115 gulong account_removed_handler;
116 gulong row_changed_handler;
117 gulong row_deleted_handler;
118 gulong row_inserted_handler;
119 gulong rows_reordered_handler;
120 gulong fetch_image_redraw_handler;
123 GtkWidget *remove_attachment_banner;
126 TnyMimePart *other_body;
131 static void modest_msg_view_window_class_init (ModestMsgViewWindowClass *klass);
132 static void modest_msg_view_window_init (ModestMsgViewWindow *obj);
133 static void modest_header_view_observer_init (ModestHeaderViewObserverIface *iface_class);
134 static void modest_msg_view_window_finalize (GObject *obj);
135 static void modest_msg_view_window_show_find_toolbar (GtkWidget *obj, gpointer data);
136 static void modest_msg_view_window_find_toolbar_close (GtkWidget *widget,
137 ModestMsgViewWindow *obj);
138 static void modest_msg_view_window_find_toolbar_search (GtkWidget *widget,
139 ModestMsgViewWindow *obj);
140 static void modest_msg_view_window_toggle_find_toolbar (GtkWidget *obj,
142 static void modest_msg_view_window_disconnect_signals (ModestWindow *self);
144 static gdouble modest_msg_view_window_get_zoom (ModestWindow *window);
145 static void modest_msg_view_window_set_zoom (ModestWindow *window,
147 static gboolean modest_msg_view_window_zoom_minus (ModestWindow *window);
148 static gboolean modest_msg_view_window_zoom_plus (ModestWindow *window);
149 static gboolean modest_msg_view_window_key_event (GtkWidget *window,
152 static void modest_msg_view_window_update_priority (ModestMsgViewWindow *window);
154 static void modest_msg_view_window_show_toolbar (ModestWindow *window,
155 gboolean show_toolbar);
157 static void modest_msg_view_window_clipboard_owner_change (GtkClipboard *clipboard,
159 ModestMsgViewWindow *window);
161 static void modest_msg_view_window_on_row_changed (GtkTreeModel *header_model,
164 ModestMsgViewWindow *window);
166 static void modest_msg_view_window_on_row_deleted (GtkTreeModel *header_model,
168 ModestMsgViewWindow *window);
170 static void modest_msg_view_window_on_row_inserted (GtkTreeModel *header_model,
171 GtkTreePath *tree_path,
172 GtkTreeIter *tree_iter,
173 ModestMsgViewWindow *window);
175 static void modest_msg_view_window_on_row_reordered (GtkTreeModel *header_model,
179 ModestMsgViewWindow *window);
181 static void modest_msg_view_window_update_model_replaced (ModestHeaderViewObserver *window,
183 const gchar *tny_folder_id);
185 static void on_queue_changed (ModestMailOperationQueue *queue,
186 ModestMailOperation *mail_op,
187 ModestMailOperationQueueNotification type,
188 ModestMsgViewWindow *self);
190 static void on_account_removed (TnyAccountStore *account_store,
194 static void on_move_focus (GtkWidget *widget,
195 GtkDirectionType direction,
198 static void view_msg_cb (ModestMailOperation *mail_op,
205 static void set_progress_hint (ModestMsgViewWindow *self,
208 static void update_window_title (ModestMsgViewWindow *window);
210 static void init_window (ModestMsgViewWindow *obj);
212 static gboolean msg_is_visible (TnyHeader *header, gboolean check_outbox);
214 static void check_dimming_rules_after_change (ModestMsgViewWindow *window);
216 static gboolean on_fetch_image (ModestMsgView *msgview,
219 ModestMsgViewWindow *window);
221 static gboolean modest_msg_view_window_scroll_child (ModestMsgViewWindow *self,
222 GtkScrollType scroll_type,
225 static gboolean message_reader (ModestMsgViewWindow *window,
226 ModestMsgViewWindowPrivate *priv,
228 const gchar *msg_uid,
230 GtkTreeRowReference *row_reference);
232 static void setup_menu (ModestMsgViewWindow *self);
233 static gboolean _modest_msg_view_window_map_event (GtkWidget *widget,
236 static void update_branding (ModestMsgViewWindow *self);
237 static void sync_flags (ModestMsgViewWindow *self);
239 /* list my signals */
246 static const GtkToggleActionEntry msg_view_toggle_action_entries [] = {
247 { "FindInMessage", MODEST_TOOLBAR_ICON_FIND, N_("qgn_toolb_gene_find"), "<CTRL>F", NULL, G_CALLBACK (modest_msg_view_window_toggle_find_toolbar), FALSE },
250 #define MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE((o), \
251 MODEST_TYPE_MSG_VIEW_WINDOW, \
252 ModestMsgViewWindowPrivate))
254 static GtkWindowClass *parent_class = NULL;
256 /* uncomment the following if you have defined any signals */
257 static guint signals[LAST_SIGNAL] = {0};
260 modest_msg_view_window_get_type (void)
262 static GType my_type = 0;
264 static const GTypeInfo my_info = {
265 sizeof(ModestMsgViewWindowClass),
266 NULL, /* base init */
267 NULL, /* base finalize */
268 (GClassInitFunc) modest_msg_view_window_class_init,
269 NULL, /* class finalize */
270 NULL, /* class data */
271 sizeof(ModestMsgViewWindow),
273 (GInstanceInitFunc) modest_msg_view_window_init,
276 my_type = g_type_register_static (MODEST_TYPE_HILDON2_WINDOW,
277 "ModestMsgViewWindow",
280 static const GInterfaceInfo modest_header_view_observer_info =
282 (GInterfaceInitFunc) modest_header_view_observer_init,
283 NULL, /* interface_finalize */
284 NULL /* interface_data */
287 g_type_add_interface_static (my_type,
288 MODEST_TYPE_HEADER_VIEW_OBSERVER,
289 &modest_header_view_observer_info);
295 save_state (ModestWindow *self)
297 modest_widget_memory_save (modest_runtime_get_conf (),
299 MODEST_CONF_MSG_VIEW_WINDOW_KEY);
303 modest_msg_view_window_scroll_child (ModestMsgViewWindow *self,
304 GtkScrollType scroll_type,
308 ModestMsgViewWindowPrivate *priv;
311 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(self);
313 switch (scroll_type) {
314 case GTK_SCROLL_STEP_UP:
317 case GTK_SCROLL_STEP_DOWN:
320 case GTK_SCROLL_PAGE_UP:
323 case GTK_SCROLL_PAGE_DOWN:
326 case GTK_SCROLL_START:
337 modest_maemo_utils_scroll_pannable((HildonPannableArea *) priv->main_scroll, 0, step);
339 return (gboolean) step;
343 add_scroll_binding (GtkBindingSet *binding_set,
345 GtkScrollType scroll)
347 guint keypad_keyval = keyval - GDK_Left + GDK_KP_Left;
349 gtk_binding_entry_add_signal (binding_set, keyval, 0,
351 GTK_TYPE_SCROLL_TYPE, scroll,
352 G_TYPE_BOOLEAN, FALSE);
353 gtk_binding_entry_add_signal (binding_set, keypad_keyval, 0,
355 GTK_TYPE_SCROLL_TYPE, scroll,
356 G_TYPE_BOOLEAN, FALSE);
360 modest_msg_view_window_class_init (ModestMsgViewWindowClass *klass)
362 GObjectClass *gobject_class;
363 HildonWindowClass *hildon_window_class;
364 ModestWindowClass *modest_window_class;
365 GtkBindingSet *binding_set;
367 gobject_class = (GObjectClass*) klass;
368 hildon_window_class = (HildonWindowClass *) klass;
369 modest_window_class = (ModestWindowClass *) klass;
371 parent_class = g_type_class_peek_parent (klass);
372 gobject_class->finalize = modest_msg_view_window_finalize;
374 modest_window_class->set_zoom_func = modest_msg_view_window_set_zoom;
375 modest_window_class->get_zoom_func = modest_msg_view_window_get_zoom;
376 modest_window_class->zoom_plus_func = modest_msg_view_window_zoom_plus;
377 modest_window_class->zoom_minus_func = modest_msg_view_window_zoom_minus;
378 modest_window_class->show_toolbar_func = modest_msg_view_window_show_toolbar;
379 modest_window_class->disconnect_signals_func = modest_msg_view_window_disconnect_signals;
381 modest_window_class->save_state_func = save_state;
383 klass->scroll_child = modest_msg_view_window_scroll_child;
385 signals[MSG_CHANGED_SIGNAL] =
386 g_signal_new ("msg-changed",
387 G_TYPE_FROM_CLASS (gobject_class),
389 G_STRUCT_OFFSET (ModestMsgViewWindowClass, msg_changed),
391 modest_marshal_VOID__POINTER_POINTER,
392 G_TYPE_NONE, 2, G_TYPE_POINTER, G_TYPE_POINTER);
394 signals[SCROLL_CHILD_SIGNAL] =
395 g_signal_new ("scroll-child",
396 G_TYPE_FROM_CLASS (gobject_class),
397 G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
398 G_STRUCT_OFFSET (ModestMsgViewWindowClass, scroll_child),
400 modest_marshal_BOOLEAN__ENUM_BOOLEAN,
401 G_TYPE_BOOLEAN, 2, GTK_TYPE_SCROLL_TYPE, G_TYPE_BOOLEAN);
403 binding_set = gtk_binding_set_by_class (klass);
404 add_scroll_binding (binding_set, GDK_Up, GTK_SCROLL_STEP_UP);
405 add_scroll_binding (binding_set, GDK_Down, GTK_SCROLL_STEP_DOWN);
406 add_scroll_binding (binding_set, GDK_Page_Up, GTK_SCROLL_PAGE_UP);
407 add_scroll_binding (binding_set, GDK_Page_Down, GTK_SCROLL_PAGE_DOWN);
408 add_scroll_binding (binding_set, GDK_Home, GTK_SCROLL_START);
409 add_scroll_binding (binding_set, GDK_End, GTK_SCROLL_END);
411 g_type_class_add_private (gobject_class, sizeof(ModestMsgViewWindowPrivate));
415 static void modest_header_view_observer_init(
416 ModestHeaderViewObserverIface *iface_class)
418 iface_class->update_func = modest_msg_view_window_update_model_replaced;
422 modest_msg_view_window_init (ModestMsgViewWindow *obj)
424 ModestMsgViewWindowPrivate *priv;
425 ModestWindowPrivate *parent_priv = NULL;
426 GtkActionGroup *action_group = NULL;
427 GError *error = NULL;
429 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(obj);
430 parent_priv = MODEST_WINDOW_GET_PRIVATE(obj);
431 parent_priv->ui_manager = gtk_ui_manager_new();
433 action_group = gtk_action_group_new ("ModestMsgViewWindowActions");
434 gtk_action_group_set_translation_domain (action_group, GETTEXT_PACKAGE);
436 /* Add common actions */
437 gtk_action_group_add_actions (action_group,
438 modest_action_entries,
439 G_N_ELEMENTS (modest_action_entries),
441 gtk_action_group_add_toggle_actions (action_group,
442 msg_view_toggle_action_entries,
443 G_N_ELEMENTS (msg_view_toggle_action_entries),
446 gtk_ui_manager_insert_action_group (parent_priv->ui_manager, action_group, 0);
447 g_object_unref (action_group);
449 /* Load the UI definition */
450 gtk_ui_manager_add_ui_from_file (parent_priv->ui_manager, MODEST_UIDIR "modest-msg-view-window-ui.xml",
453 g_printerr ("modest: could not merge modest-msg-view-window-ui.xml: %s\n", error->message);
454 g_error_free (error);
459 /* Add accelerators */
460 gtk_window_add_accel_group (GTK_WINDOW (obj),
461 gtk_ui_manager_get_accel_group (parent_priv->ui_manager));
463 priv->is_search_result = FALSE;
464 priv->is_outbox = FALSE;
466 priv->msg_view = NULL;
467 priv->header_model = NULL;
468 priv->header_folder_id = NULL;
469 priv->clipboard_change_handler = 0;
470 priv->queue_change_handler = 0;
471 priv->account_removed_handler = 0;
472 priv->row_changed_handler = 0;
473 priv->row_deleted_handler = 0;
474 priv->row_inserted_handler = 0;
475 priv->rows_reordered_handler = 0;
476 priv->fetch_image_redraw_handler = 0;
477 priv->progress_hint = FALSE;
478 priv->fetching_images = 0;
480 priv->optimized_view = FALSE;
481 priv->purge_timeout = 0;
482 priv->remove_attachment_banner = NULL;
483 priv->msg_uid = NULL;
484 priv->other_body = NULL;
486 priv->sighandlers = NULL;
489 init_window (MODEST_MSG_VIEW_WINDOW(obj));
491 hildon_program_add_window (hildon_program_get_instance(),
497 update_progress_hint (ModestMsgViewWindow *self)
499 ModestMsgViewWindowPrivate *priv;
500 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(self);
502 if (GTK_WIDGET_VISIBLE (self)) {
503 hildon_gtk_window_set_progress_indicator (GTK_WINDOW (self),
504 (priv->progress_hint || (priv->fetching_images > 0))?1:0);
509 set_progress_hint (ModestMsgViewWindow *self,
512 ModestWindowPrivate *parent_priv;
513 ModestMsgViewWindowPrivate *priv;
515 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self));
517 parent_priv = MODEST_WINDOW_GET_PRIVATE(self);
518 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(self);
520 /* Sets current progress hint */
521 priv->progress_hint = enabled;
523 update_progress_hint (self);
529 init_window (ModestMsgViewWindow *obj)
531 GtkWidget *main_vbox;
532 ModestMsgViewWindowPrivate *priv;
534 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(obj);
536 priv->msg_view = GTK_WIDGET (tny_platform_factory_new_msg_view (modest_tny_platform_factory_get_instance ()));
537 modest_msg_view_set_shadow_type (MODEST_MSG_VIEW (priv->msg_view), GTK_SHADOW_NONE);
538 main_vbox = gtk_vbox_new (FALSE, 6);
539 priv->main_scroll = hildon_pannable_area_new ();
540 g_object_set (G_OBJECT (priv->main_scroll),
541 "mov-mode", HILDON_MOVEMENT_MODE_BOTH,
544 gtk_container_add (GTK_CONTAINER (priv->main_scroll), priv->msg_view);
545 gtk_box_pack_start (GTK_BOX(main_vbox), priv->main_scroll, TRUE, TRUE, 0);
546 gtk_container_add (GTK_CONTAINER(obj), main_vbox);
548 /* NULL-ize fields if the window is destroyed */
549 g_signal_connect (priv->msg_view, "destroy", G_CALLBACK (gtk_widget_destroyed), &(priv->msg_view));
551 gtk_widget_show_all (GTK_WIDGET(main_vbox));
555 modest_msg_view_window_disconnect_signals (ModestWindow *self)
557 ModestMsgViewWindowPrivate *priv;
558 GtkWidget *header_view = NULL;
559 GtkWindow *parent_window = NULL;
561 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
563 if (gtk_clipboard_get (GDK_SELECTION_PRIMARY) &&
564 g_signal_handler_is_connected (gtk_clipboard_get (GDK_SELECTION_PRIMARY),
565 priv->clipboard_change_handler))
566 g_signal_handler_disconnect (gtk_clipboard_get (GDK_SELECTION_PRIMARY),
567 priv->clipboard_change_handler);
569 if (g_signal_handler_is_connected (G_OBJECT (modest_runtime_get_mail_operation_queue ()),
570 priv->queue_change_handler))
571 g_signal_handler_disconnect (G_OBJECT (modest_runtime_get_mail_operation_queue ()),
572 priv->queue_change_handler);
574 if (g_signal_handler_is_connected (G_OBJECT (modest_runtime_get_account_store ()),
575 priv->account_removed_handler))
576 g_signal_handler_disconnect (G_OBJECT (modest_runtime_get_account_store ()),
577 priv->account_removed_handler);
579 if (priv->header_model) {
580 if (g_signal_handler_is_connected(G_OBJECT (priv->header_model),
581 priv->row_changed_handler))
582 g_signal_handler_disconnect(G_OBJECT (priv->header_model),
583 priv->row_changed_handler);
585 if (g_signal_handler_is_connected(G_OBJECT (priv->header_model),
586 priv->row_deleted_handler))
587 g_signal_handler_disconnect(G_OBJECT (priv->header_model),
588 priv->row_deleted_handler);
590 if (g_signal_handler_is_connected(G_OBJECT (priv->header_model),
591 priv->row_inserted_handler))
592 g_signal_handler_disconnect(G_OBJECT (priv->header_model),
593 priv->row_inserted_handler);
595 if (g_signal_handler_is_connected(G_OBJECT (priv->header_model),
596 priv->rows_reordered_handler))
597 g_signal_handler_disconnect(G_OBJECT (priv->header_model),
598 priv->rows_reordered_handler);
601 modest_signal_mgr_disconnect_all_and_destroy (priv->sighandlers);
602 priv->sighandlers = NULL;
604 parent_window = gtk_window_get_transient_for (GTK_WINDOW (self));
605 if (parent_window && MODEST_IS_HEADER_WINDOW (parent_window)) {
606 header_view = GTK_WIDGET (modest_header_window_get_header_view (MODEST_HEADER_WINDOW (parent_window)));
608 modest_header_view_remove_observer(MODEST_HEADER_VIEW (header_view),
609 MODEST_HEADER_VIEW_OBSERVER(self));
615 modest_msg_view_window_finalize (GObject *obj)
617 ModestMsgViewWindowPrivate *priv;
619 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (obj);
621 /* Sanity check: shouldn't be needed, the window mgr should
622 call this function before */
623 modest_msg_view_window_disconnect_signals (MODEST_WINDOW (obj));
625 if (priv->fetch_image_redraw_handler > 0) {
626 g_source_remove (priv->fetch_image_redraw_handler);
627 priv->fetch_image_redraw_handler = 0;
630 if (priv->other_body != NULL) {
631 g_object_unref (priv->other_body);
632 priv->other_body = NULL;
635 if (priv->header_model != NULL) {
636 g_object_unref (priv->header_model);
637 priv->header_model = NULL;
640 if (priv->remove_attachment_banner) {
641 gtk_widget_destroy (priv->remove_attachment_banner);
642 g_object_unref (priv->remove_attachment_banner);
643 priv->remove_attachment_banner = NULL;
646 if (priv->purge_timeout > 0) {
647 g_source_remove (priv->purge_timeout);
648 priv->purge_timeout = 0;
651 if (priv->row_reference) {
652 gtk_tree_row_reference_free (priv->row_reference);
653 priv->row_reference = NULL;
656 if (priv->next_row_reference) {
657 gtk_tree_row_reference_free (priv->next_row_reference);
658 priv->next_row_reference = NULL;
662 g_free (priv->msg_uid);
663 priv->msg_uid = NULL;
666 G_OBJECT_CLASS(parent_class)->finalize (obj);
670 select_next_valid_row (GtkTreeModel *model,
671 GtkTreeRowReference **row_reference,
675 GtkTreeIter tmp_iter;
677 GtkTreePath *next = NULL;
678 gboolean retval = FALSE, finished;
680 g_return_val_if_fail (gtk_tree_row_reference_valid (*row_reference), FALSE);
682 path = gtk_tree_row_reference_get_path (*row_reference);
683 gtk_tree_model_get_iter (model, &tmp_iter, path);
684 gtk_tree_row_reference_free (*row_reference);
685 *row_reference = NULL;
689 TnyHeader *header = NULL;
691 if (gtk_tree_model_iter_next (model, &tmp_iter)) {
692 gtk_tree_model_get (model, &tmp_iter,
693 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
697 if (msg_is_visible (header, is_outbox)) {
698 next = gtk_tree_model_get_path (model, &tmp_iter);
699 *row_reference = gtk_tree_row_reference_new (model, next);
700 gtk_tree_path_free (next);
704 g_object_unref (header);
707 } else if (cycle && gtk_tree_model_get_iter_first (model, &tmp_iter)) {
708 next = gtk_tree_model_get_path (model, &tmp_iter);
710 /* Ensure that we are not selecting the same */
711 if (gtk_tree_path_compare (path, next) != 0) {
712 gtk_tree_model_get (model, &tmp_iter,
713 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
716 if (msg_is_visible (header, is_outbox)) {
717 *row_reference = gtk_tree_row_reference_new (model, next);
721 g_object_unref (header);
725 /* If we ended up in the same message
726 then there is no valid next
730 gtk_tree_path_free (next);
732 /* If there are no more messages and we don't
733 want to start again in the first one then
734 there is no valid next message */
740 gtk_tree_path_free (path);
745 /* TODO: This should be in _init(), with the parameters as properties. */
747 modest_msg_view_window_construct (ModestMsgViewWindow *self,
748 const gchar *modest_account_name,
749 const gchar *mailbox,
750 const gchar *msg_uid)
753 ModestMsgViewWindowPrivate *priv = NULL;
754 ModestWindowPrivate *parent_priv = NULL;
755 ModestDimmingRulesGroup *toolbar_rules_group = NULL;
756 ModestDimmingRulesGroup *clipboard_rules_group = NULL;
758 obj = G_OBJECT (self);
759 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(obj);
760 parent_priv = MODEST_WINDOW_GET_PRIVATE(obj);
762 priv->msg_uid = g_strdup (msg_uid);
765 parent_priv->menubar = NULL;
767 toolbar_rules_group = modest_dimming_rules_group_new (MODEST_DIMMING_RULES_TOOLBAR, TRUE);
768 clipboard_rules_group = modest_dimming_rules_group_new (MODEST_DIMMING_RULES_CLIPBOARD, FALSE);
771 /* Add common dimming rules */
772 modest_dimming_rules_group_add_rules (toolbar_rules_group,
773 modest_msg_view_toolbar_dimming_entries,
774 G_N_ELEMENTS (modest_msg_view_toolbar_dimming_entries),
775 MODEST_WINDOW (self));
776 modest_dimming_rules_group_add_rules (clipboard_rules_group,
777 modest_msg_view_clipboard_dimming_entries,
778 G_N_ELEMENTS (modest_msg_view_clipboard_dimming_entries),
779 MODEST_WINDOW (self));
781 /* Insert dimming rules group for this window */
782 modest_ui_dimming_manager_insert_rules_group (parent_priv->ui_dimming_manager, toolbar_rules_group);
783 modest_ui_dimming_manager_insert_rules_group (parent_priv->ui_dimming_manager, clipboard_rules_group);
784 g_object_unref (toolbar_rules_group);
785 g_object_unref (clipboard_rules_group);
787 /* g_signal_connect (G_OBJECT(obj), "delete-event", G_CALLBACK(on_delete_event), obj); */
789 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);
790 g_signal_connect (G_OBJECT(priv->msg_view), "activate_link",
791 G_CALLBACK (modest_ui_actions_on_msg_link_clicked), obj);
792 g_signal_connect (G_OBJECT(priv->msg_view), "link_hover",
793 G_CALLBACK (modest_ui_actions_on_msg_link_hover), obj);
794 g_signal_connect (G_OBJECT(priv->msg_view), "attachment_clicked",
795 G_CALLBACK (modest_ui_actions_on_msg_attachment_clicked), obj);
796 g_signal_connect (G_OBJECT(priv->msg_view), "recpt_activated",
797 G_CALLBACK (modest_ui_actions_on_msg_recpt_activated), obj);
798 g_signal_connect (G_OBJECT(priv->msg_view), "show_details",
799 G_CALLBACK (modest_ui_actions_on_details), obj);
800 g_signal_connect (G_OBJECT(priv->msg_view), "link_contextual",
801 G_CALLBACK (modest_ui_actions_on_msg_link_contextual), obj);
802 g_signal_connect (G_OBJECT(priv->msg_view), "limit_error",
803 G_CALLBACK (modest_ui_actions_on_limit_error), obj);
804 g_signal_connect (G_OBJECT (priv->msg_view), "fetch_image",
805 G_CALLBACK (on_fetch_image), obj);
807 g_signal_connect (G_OBJECT (obj), "key-release-event",
808 G_CALLBACK (modest_msg_view_window_key_event),
811 g_signal_connect (G_OBJECT (obj), "key-press-event",
812 G_CALLBACK (modest_msg_view_window_key_event),
815 g_signal_connect (G_OBJECT (obj), "move-focus",
816 G_CALLBACK (on_move_focus), obj);
818 g_signal_connect (G_OBJECT (obj), "map-event",
819 G_CALLBACK (_modest_msg_view_window_map_event),
822 /* Mail Operation Queue */
823 priv->queue_change_handler = g_signal_connect (G_OBJECT (modest_runtime_get_mail_operation_queue ()),
825 G_CALLBACK (on_queue_changed),
828 /* Account manager */
829 priv->account_removed_handler = g_signal_connect (G_OBJECT (modest_runtime_get_account_store ()),
831 G_CALLBACK(on_account_removed),
834 modest_window_set_active_account (MODEST_WINDOW(obj), modest_account_name);
835 modest_window_set_active_mailbox (MODEST_WINDOW(obj), mailbox);
837 /* First add out toolbar ... */
838 modest_msg_view_window_show_toolbar (MODEST_WINDOW (obj), TRUE);
840 /* ... and later the find toolbar. This way find toolbar will
841 be shown over the other */
842 priv->find_toolbar = hildon_find_toolbar_new (NULL);
843 hildon_window_add_toolbar (HILDON_WINDOW (obj), GTK_TOOLBAR (priv->find_toolbar));
844 gtk_widget_set_no_show_all (priv->find_toolbar, TRUE);
845 g_signal_connect (G_OBJECT (priv->find_toolbar), "close",
846 G_CALLBACK (modest_msg_view_window_find_toolbar_close), obj);
847 g_signal_connect (G_OBJECT (priv->find_toolbar), "search",
848 G_CALLBACK (modest_msg_view_window_find_toolbar_search), obj);
849 priv->last_search = NULL;
851 /* Init the clipboard actions dim status */
852 modest_msg_view_grab_focus(MODEST_MSG_VIEW (priv->msg_view));
854 update_window_title (MODEST_MSG_VIEW_WINDOW (obj));
859 /* FIXME: parameter checks */
861 modest_msg_view_window_new_with_header_model (TnyMsg *msg,
862 const gchar *modest_account_name,
863 const gchar *mailbox,
864 const gchar *msg_uid,
866 GtkTreeRowReference *row_reference)
868 ModestMsgViewWindow *window = NULL;
869 ModestMsgViewWindowPrivate *priv = NULL;
870 TnyFolder *header_folder = NULL;
871 ModestHeaderView *header_view = NULL;
872 ModestWindowMgr *mgr = NULL;
875 modest_tny_mime_part_to_string (TNY_MIME_PART (msg), 0);
878 mgr = modest_runtime_get_window_mgr ();
879 window = MODEST_MSG_VIEW_WINDOW (modest_window_mgr_get_msg_view_window (mgr));
880 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), NULL);
882 modest_msg_view_window_construct (window, modest_account_name, mailbox, msg_uid);
884 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
886 /* Remember the message list's TreeModel so we can detect changes
887 * and change the list selection when necessary: */
888 header_folder = modest_header_view_get_folder (header_view);
890 priv->is_outbox = (modest_tny_folder_guess_folder_type (header_folder) ==
891 TNY_FOLDER_TYPE_OUTBOX);
892 priv->header_folder_id = tny_folder_get_id (header_folder);
893 g_object_unref(header_folder);
896 /* Setup row references and connect signals */
897 priv->header_model = g_object_ref (model);
899 if (row_reference && gtk_tree_row_reference_valid (row_reference)) {
900 priv->row_reference = gtk_tree_row_reference_copy (row_reference);
901 priv->next_row_reference = gtk_tree_row_reference_copy (row_reference);
902 select_next_valid_row (model, &(priv->next_row_reference), TRUE, priv->is_outbox);
904 priv->row_reference = NULL;
905 priv->next_row_reference = NULL;
908 /* Connect signals */
909 priv->row_changed_handler =
910 g_signal_connect (GTK_TREE_MODEL(model), "row-changed",
911 G_CALLBACK(modest_msg_view_window_on_row_changed),
913 priv->row_deleted_handler =
914 g_signal_connect (GTK_TREE_MODEL(model), "row-deleted",
915 G_CALLBACK(modest_msg_view_window_on_row_deleted),
917 priv->row_inserted_handler =
918 g_signal_connect (GTK_TREE_MODEL(model), "row-inserted",
919 G_CALLBACK(modest_msg_view_window_on_row_inserted),
921 priv->rows_reordered_handler =
922 g_signal_connect(GTK_TREE_MODEL(model), "rows-reordered",
923 G_CALLBACK(modest_msg_view_window_on_row_reordered),
926 if (header_view != NULL){
927 modest_header_view_add_observer(header_view,
928 MODEST_HEADER_VIEW_OBSERVER(window));
931 tny_msg_view_set_msg (TNY_MSG_VIEW (priv->msg_view), msg);
932 update_window_title (MODEST_MSG_VIEW_WINDOW (window));
933 update_branding (MODEST_MSG_VIEW_WINDOW (window));
935 /* gtk_widget_show_all (GTK_WIDGET (window)); */
936 modest_msg_view_window_update_priority (window);
937 /* Check dimming rules */
938 modest_ui_actions_check_toolbar_dimming_rules (MODEST_WINDOW (window));
939 modest_ui_actions_check_menu_dimming_rules (MODEST_WINDOW (window));
940 modest_window_check_dimming_rules_group (MODEST_WINDOW (window), MODEST_DIMMING_RULES_CLIPBOARD);
942 return MODEST_WINDOW(window);
946 modest_msg_view_window_new_from_uid (const gchar *modest_account_name,
947 const gchar *mailbox,
948 const gchar *msg_uid)
950 ModestMsgViewWindow *window = NULL;
951 ModestMsgViewWindowPrivate *priv = NULL;
952 ModestWindowMgr *mgr = NULL;
954 TnyAccount *account = NULL;
956 mgr = modest_runtime_get_window_mgr ();
957 window = MODEST_MSG_VIEW_WINDOW (modest_window_mgr_get_msg_view_window (mgr));
958 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), NULL);
960 modest_msg_view_window_construct (window, modest_account_name, mailbox, msg_uid);
962 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
964 is_merge = g_str_has_prefix (msg_uid, "merge:");
966 /* Get the account */
968 account = tny_account_store_find_account (TNY_ACCOUNT_STORE (modest_runtime_get_account_store ()),
971 if (is_merge || account) {
972 TnyFolder *folder = NULL;
974 /* Try to get the message, if it's already downloaded
975 we don't need to connect */
977 folder = modest_tny_folder_store_find_folder_from_uri (TNY_FOLDER_STORE (account), msg_uid);
979 ModestTnyAccountStore *account_store;
980 ModestTnyLocalFoldersAccount *local_folders_account;
982 account_store = modest_runtime_get_account_store ();
983 local_folders_account = MODEST_TNY_LOCAL_FOLDERS_ACCOUNT (
984 modest_tny_account_store_get_local_folders_account (account_store));
985 folder = modest_tny_local_folders_account_get_merged_outbox (local_folders_account);
986 g_object_unref (local_folders_account);
990 gboolean device_online;
992 device = modest_runtime_get_device();
993 device_online = tny_device_is_online (device);
995 message_reader (window, priv, NULL, msg_uid, folder, NULL);
997 TnyMsg *msg = tny_folder_find_msg (folder, msg_uid, NULL);
999 tny_msg_view_set_msg (TNY_MSG_VIEW (priv->msg_view), msg);
1000 update_window_title (MODEST_MSG_VIEW_WINDOW (window));
1001 update_branding (MODEST_MSG_VIEW_WINDOW (window));
1002 g_object_unref (msg);
1003 /* Sync flags to server */
1004 sync_flags (MODEST_MSG_VIEW_WINDOW (window));
1006 message_reader (window, priv, NULL, msg_uid, folder, NULL);
1009 g_object_unref (folder);
1014 /* Check dimming rules */
1015 modest_ui_actions_check_toolbar_dimming_rules (MODEST_WINDOW (window));
1016 modest_ui_actions_check_menu_dimming_rules (MODEST_WINDOW (window));
1017 modest_window_check_dimming_rules_group (MODEST_WINDOW (window), MODEST_DIMMING_RULES_CLIPBOARD);
1019 return MODEST_WINDOW(window);
1023 modest_msg_view_window_new_from_header_view (ModestHeaderView *header_view,
1024 const gchar *modest_account_name,
1025 const gchar *mailbox,
1026 const gchar *msg_uid,
1027 GtkTreeRowReference *row_reference)
1029 ModestMsgViewWindow *window = NULL;
1030 ModestMsgViewWindowPrivate *priv = NULL;
1031 TnyFolder *header_folder = NULL;
1032 ModestWindowMgr *mgr = NULL;
1036 mgr = modest_runtime_get_window_mgr ();
1037 window = MODEST_MSG_VIEW_WINDOW (modest_window_mgr_get_msg_view_window (mgr));
1038 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), NULL);
1040 modest_msg_view_window_construct (window, modest_account_name, mailbox, msg_uid);
1042 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1044 /* Remember the message list's TreeModel so we can detect changes
1045 * and change the list selection when necessary: */
1047 if (header_view != NULL){
1048 header_folder = modest_header_view_get_folder(header_view);
1049 /* This could happen if the header folder was
1050 unseleted before opening this msg window (for
1051 example if the user selects an account in the
1052 folder view of the main window */
1053 if (header_folder) {
1054 priv->is_outbox = (modest_tny_folder_guess_folder_type (header_folder) ==
1055 TNY_FOLDER_TYPE_OUTBOX);
1056 priv->header_folder_id = tny_folder_get_id(header_folder);
1057 g_object_unref(header_folder);
1061 /* Setup row references and connect signals */
1062 priv->header_model = gtk_tree_view_get_model (GTK_TREE_VIEW (header_view));
1063 g_object_ref (priv->header_model);
1065 if (row_reference && gtk_tree_row_reference_valid (row_reference)) {
1066 priv->row_reference = gtk_tree_row_reference_copy (row_reference);
1067 priv->next_row_reference = gtk_tree_row_reference_copy (row_reference);
1068 select_next_valid_row (priv->header_model, &(priv->next_row_reference), TRUE, priv->is_outbox);
1070 priv->row_reference = NULL;
1071 priv->next_row_reference = NULL;
1074 /* Connect signals */
1075 priv->row_changed_handler =
1076 g_signal_connect (GTK_TREE_MODEL(priv->header_model), "row-changed",
1077 G_CALLBACK(modest_msg_view_window_on_row_changed),
1079 priv->row_deleted_handler =
1080 g_signal_connect (GTK_TREE_MODEL(priv->header_model), "row-deleted",
1081 G_CALLBACK(modest_msg_view_window_on_row_deleted),
1083 priv->row_inserted_handler =
1084 g_signal_connect (GTK_TREE_MODEL(priv->header_model), "row-inserted",
1085 G_CALLBACK(modest_msg_view_window_on_row_inserted),
1087 priv->rows_reordered_handler =
1088 g_signal_connect(GTK_TREE_MODEL(priv->header_model), "rows-reordered",
1089 G_CALLBACK(modest_msg_view_window_on_row_reordered),
1092 if (header_view != NULL){
1093 modest_header_view_add_observer(header_view,
1094 MODEST_HEADER_VIEW_OBSERVER(window));
1097 tny_msg_view_set_msg (TNY_MSG_VIEW (priv->msg_view), NULL);
1098 update_branding (MODEST_MSG_VIEW_WINDOW (window));
1100 if (priv->row_reference) {
1101 path = gtk_tree_row_reference_get_path (priv->row_reference);
1102 if (gtk_tree_model_get_iter (priv->header_model, &iter, path)) {
1104 gtk_tree_model_get (priv->header_model, &iter,
1105 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
1107 message_reader (window, priv, header, NULL, NULL, priv->row_reference);
1108 g_object_unref (header);
1110 gtk_tree_path_free (path);
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_new_for_search_result (TnyMsg *msg,
1122 const gchar *modest_account_name,
1123 const gchar *mailbox,
1124 const gchar *msg_uid)
1126 ModestMsgViewWindow *window = NULL;
1127 ModestMsgViewWindowPrivate *priv = NULL;
1128 ModestWindowMgr *mgr = NULL;
1130 mgr = modest_runtime_get_window_mgr ();
1131 window = MODEST_MSG_VIEW_WINDOW (modest_window_mgr_get_msg_view_window (mgr));
1132 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), NULL);
1133 modest_msg_view_window_construct (window, modest_account_name, mailbox, msg_uid);
1135 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1137 /* Remember that this is a search result,
1138 * so we can disable some UI appropriately: */
1139 priv->is_search_result = TRUE;
1141 tny_msg_view_set_msg (TNY_MSG_VIEW (priv->msg_view), msg);
1142 update_branding (MODEST_MSG_VIEW_WINDOW (window));
1144 update_window_title (window);
1145 /* gtk_widget_show_all (GTK_WIDGET (window));*/
1146 modest_msg_view_window_update_priority (window);
1148 /* Check dimming rules */
1149 modest_ui_actions_check_toolbar_dimming_rules (MODEST_WINDOW (window));
1150 modest_ui_actions_check_menu_dimming_rules (MODEST_WINDOW (window));
1151 modest_window_check_dimming_rules_group (MODEST_WINDOW (window), MODEST_DIMMING_RULES_CLIPBOARD);
1153 return MODEST_WINDOW(window);
1157 modest_msg_view_window_is_other_body (ModestMsgViewWindow *self)
1159 ModestMsgViewWindowPrivate *priv = NULL;
1161 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self), FALSE);
1162 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
1164 return (priv->other_body != NULL);
1168 modest_msg_view_window_new_with_other_body (TnyMsg *msg,
1169 TnyMimePart *other_body,
1170 const gchar *modest_account_name,
1171 const gchar *mailbox,
1172 const gchar *msg_uid)
1174 GObject *obj = NULL;
1175 ModestMsgViewWindowPrivate *priv;
1176 ModestWindowMgr *mgr = NULL;
1178 g_return_val_if_fail (msg, NULL);
1179 mgr = modest_runtime_get_window_mgr ();
1180 obj = G_OBJECT (modest_window_mgr_get_msg_view_window (mgr));
1181 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (obj);
1182 modest_msg_view_window_construct (MODEST_MSG_VIEW_WINDOW (obj),
1183 modest_account_name, mailbox, msg_uid);
1186 priv->other_body = g_object_ref (other_body);
1187 modest_msg_view_set_msg_with_other_body (MODEST_MSG_VIEW (priv->msg_view), msg, other_body);
1189 tny_msg_view_set_msg (TNY_MSG_VIEW (priv->msg_view), msg);
1191 update_window_title (MODEST_MSG_VIEW_WINDOW (obj));
1192 update_branding (MODEST_MSG_VIEW_WINDOW (obj));
1194 /* gtk_widget_show_all (GTK_WIDGET (obj)); */
1196 /* Check dimming rules */
1197 modest_ui_actions_check_toolbar_dimming_rules (MODEST_WINDOW (obj));
1198 modest_ui_actions_check_menu_dimming_rules (MODEST_WINDOW (obj));
1199 modest_window_check_dimming_rules_group (MODEST_WINDOW (obj), MODEST_DIMMING_RULES_CLIPBOARD);
1201 return MODEST_WINDOW(obj);
1205 modest_msg_view_window_new_for_attachment (TnyMsg *msg,
1206 const gchar *modest_account_name,
1207 const gchar *mailbox,
1208 const gchar *msg_uid)
1210 return modest_msg_view_window_new_with_other_body (msg, NULL, modest_account_name, mailbox, msg_uid);
1214 modest_msg_view_window_on_row_changed (GtkTreeModel *header_model,
1217 ModestMsgViewWindow *window)
1219 check_dimming_rules_after_change (window);
1223 modest_msg_view_window_on_row_deleted(GtkTreeModel *header_model,
1225 ModestMsgViewWindow *window)
1227 check_dimming_rules_after_change (window);
1229 /* The window could have dissapeared */
1232 check_dimming_rules_after_change (ModestMsgViewWindow *window)
1234 modest_ui_actions_check_menu_dimming_rules (MODEST_WINDOW (window));
1235 modest_ui_actions_check_toolbar_dimming_rules (MODEST_WINDOW (window));
1239 /* On insertions we check if the folder still has the message we are
1240 * showing or do not. If do not, we do nothing. Which means we are still
1241 * not attached to any header folder and thus next/prev buttons are
1242 * still dimmed. Once the message that is shown by msg-view is found, the
1243 * new model of header-view will be attached and the references will be set.
1244 * On each further insertions dimming rules will be checked. However
1245 * this requires extra CPU time at least works.
1246 * (An message might be deleted from TnyFolder and thus will not be
1247 * inserted into the model again for example if it is removed by the
1248 * imap server and the header view is refreshed.)
1251 modest_msg_view_window_on_row_inserted (GtkTreeModel *model,
1252 GtkTreePath *tree_path,
1253 GtkTreeIter *tree_iter,
1254 ModestMsgViewWindow *window)
1256 ModestMsgViewWindowPrivate *priv = NULL;
1257 TnyHeader *header = NULL;
1259 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window));
1260 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1262 g_assert (model == priv->header_model);
1264 /* Check if the newly inserted message is the same we are actually
1265 * showing. IF not, we should remain detached from the header model
1266 * and thus prev and next toolbar buttons should remain dimmed. */
1267 gtk_tree_model_get (model, tree_iter,
1268 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
1271 if (TNY_IS_HEADER (header)) {
1274 uid = modest_tny_folder_get_header_unique_id (header);
1275 if (!g_str_equal(priv->msg_uid, uid)) {
1276 check_dimming_rules_after_change (window);
1278 g_object_unref (G_OBJECT(header));
1282 g_object_unref(G_OBJECT(header));
1285 if (priv->row_reference) {
1286 gtk_tree_row_reference_free (priv->row_reference);
1289 /* Setup row_reference for the actual msg. */
1290 priv->row_reference = gtk_tree_row_reference_new (priv->header_model, tree_path);
1291 if (priv->row_reference == NULL) {
1292 g_warning("%s: No reference for msg header item.", __FUNCTION__);
1296 /* Now set up next_row_reference. */
1297 if (priv->next_row_reference) {
1298 gtk_tree_row_reference_free (priv->next_row_reference);
1301 priv->next_row_reference = gtk_tree_row_reference_copy (priv->row_reference);
1302 select_next_valid_row (priv->header_model,
1303 &(priv->next_row_reference), FALSE, priv->is_outbox);
1305 /* Connect the remaining callbacks to become able to detect
1306 * changes in header-view. */
1307 priv->row_changed_handler =
1308 g_signal_connect (priv->header_model, "row-changed",
1309 G_CALLBACK (modest_msg_view_window_on_row_changed),
1311 priv->row_deleted_handler =
1312 g_signal_connect (priv->header_model, "row-deleted",
1313 G_CALLBACK (modest_msg_view_window_on_row_deleted),
1315 priv->rows_reordered_handler =
1316 g_signal_connect (priv->header_model, "rows-reordered",
1317 G_CALLBACK (modest_msg_view_window_on_row_reordered),
1320 check_dimming_rules_after_change (window);
1324 modest_msg_view_window_on_row_reordered (GtkTreeModel *header_model,
1328 ModestMsgViewWindow *window)
1330 ModestMsgViewWindowPrivate *priv = NULL;
1331 gboolean already_changed = FALSE;
1333 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(window);
1335 /* If the current row was reordered select the proper next
1336 valid row. The same if the next row reference changes */
1337 if (!priv->row_reference ||
1338 !gtk_tree_row_reference_valid (priv->row_reference))
1341 if (priv->next_row_reference &&
1342 gtk_tree_row_reference_valid (priv->next_row_reference)) {
1343 GtkTreePath *cur, *next;
1344 /* Check that the order is still the correct one */
1345 cur = gtk_tree_row_reference_get_path (priv->row_reference);
1346 next = gtk_tree_row_reference_get_path (priv->next_row_reference);
1347 gtk_tree_path_next (cur);
1348 if (gtk_tree_path_compare (cur, next) != 0) {
1349 gtk_tree_row_reference_free (priv->next_row_reference);
1350 priv->next_row_reference = gtk_tree_row_reference_copy (priv->row_reference);
1351 select_next_valid_row (header_model, &(priv->next_row_reference), FALSE, priv->is_outbox);
1352 already_changed = TRUE;
1354 gtk_tree_path_free (cur);
1355 gtk_tree_path_free (next);
1357 if (priv->next_row_reference)
1358 gtk_tree_row_reference_free (priv->next_row_reference);
1359 /* Update next row reference */
1360 priv->next_row_reference = gtk_tree_row_reference_copy (priv->row_reference);
1361 select_next_valid_row (header_model, &(priv->next_row_reference), FALSE, priv->is_outbox);
1362 already_changed = TRUE;
1365 check_dimming_rules_after_change (window);
1368 /* The modest_msg_view_window_update_model_replaced implements update
1369 * function for ModestHeaderViewObserver. Checks whether the TnyFolder
1370 * actually belongs to the header-view is the same as the TnyFolder of
1371 * the message of msg-view or not. If they are different, there is
1372 * nothing to do. If they are the same, then the model has replaced and
1373 * the reference in msg-view shall be replaced from the old model to
1374 * the new model. In this case the view will be detached from it's
1375 * header folder. From this point the next/prev buttons are dimmed.
1378 modest_msg_view_window_update_model_replaced (ModestHeaderViewObserver *observer,
1379 GtkTreeModel *model,
1380 const gchar *tny_folder_id)
1382 ModestMsgViewWindowPrivate *priv = NULL;
1383 ModestMsgViewWindow *window = NULL;
1385 g_assert(MODEST_IS_HEADER_VIEW_OBSERVER(observer));
1386 g_assert(MODEST_IS_MSG_VIEW_WINDOW(observer));
1388 window = MODEST_MSG_VIEW_WINDOW(observer);
1389 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(window);
1391 /* If there is an other folder in the header-view then we do
1392 * not care about it's model (msg list). Else if the
1393 * header-view shows the folder the msg shown by us is in, we
1394 * shall replace our model reference and make some check. */
1395 if(model == NULL || tny_folder_id == NULL ||
1396 (priv->header_folder_id && !g_str_equal(tny_folder_id, priv->header_folder_id)))
1399 /* Model is changed(replaced), so we should forget the old
1400 * one. Because there might be other references and there
1401 * might be some change on the model even if we unreferenced
1402 * it, we need to disconnect our signals here. */
1403 if (priv->header_model) {
1404 if (g_signal_handler_is_connected(G_OBJECT (priv->header_model),
1405 priv->row_changed_handler))
1406 g_signal_handler_disconnect(G_OBJECT (priv->header_model),
1407 priv->row_changed_handler);
1408 if (g_signal_handler_is_connected(G_OBJECT (priv->header_model),
1409 priv->row_deleted_handler))
1410 g_signal_handler_disconnect(G_OBJECT (priv->header_model),
1411 priv->row_deleted_handler);
1412 if (g_signal_handler_is_connected(G_OBJECT (priv->header_model),
1413 priv->row_inserted_handler))
1414 g_signal_handler_disconnect(G_OBJECT (priv->header_model),
1415 priv->row_inserted_handler);
1416 if (g_signal_handler_is_connected(G_OBJECT (priv->header_model),
1417 priv->rows_reordered_handler))
1418 g_signal_handler_disconnect(G_OBJECT (priv->header_model),
1419 priv->rows_reordered_handler);
1422 if (priv->row_reference)
1423 gtk_tree_row_reference_free (priv->row_reference);
1424 if (priv->next_row_reference)
1425 gtk_tree_row_reference_free (priv->next_row_reference);
1426 g_object_unref(priv->header_model);
1429 priv->row_changed_handler = 0;
1430 priv->row_deleted_handler = 0;
1431 priv->row_inserted_handler = 0;
1432 priv->rows_reordered_handler = 0;
1433 priv->next_row_reference = NULL;
1434 priv->row_reference = NULL;
1435 priv->header_model = NULL;
1438 priv->header_model = g_object_ref (model);
1440 /* Also we must connect to the new model for row insertions.
1441 * Only for insertions now. We will need other ones only after
1442 * the msg is show by msg-view is added to the new model. */
1443 priv->row_inserted_handler =
1444 g_signal_connect (priv->header_model, "row-inserted",
1445 G_CALLBACK(modest_msg_view_window_on_row_inserted),
1448 modest_ui_actions_check_menu_dimming_rules(MODEST_WINDOW(window));
1449 modest_ui_actions_check_toolbar_dimming_rules(MODEST_WINDOW(window));
1453 modest_msg_view_window_toolbar_on_transfer_mode (ModestMsgViewWindow *self)
1455 ModestMsgViewWindowPrivate *priv= NULL;
1457 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self), FALSE);
1458 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
1460 return priv->progress_hint;
1464 modest_msg_view_window_get_header (ModestMsgViewWindow *self)
1466 ModestMsgViewWindowPrivate *priv= NULL;
1468 TnyHeader *header = NULL;
1469 GtkTreePath *path = NULL;
1472 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self), NULL);
1473 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
1475 /* If the message was not obtained from a treemodel,
1476 * for instance if it was opened directly by the search UI:
1478 if (priv->header_model == NULL ||
1479 priv->row_reference == NULL ||
1480 !gtk_tree_row_reference_valid (priv->row_reference)) {
1481 msg = modest_msg_view_window_get_message (self);
1483 header = tny_msg_get_header (msg);
1484 g_object_unref (msg);
1489 /* Get iter of the currently selected message in the header view: */
1490 path = gtk_tree_row_reference_get_path (priv->row_reference);
1491 g_return_val_if_fail (path != NULL, NULL);
1492 gtk_tree_model_get_iter (priv->header_model,
1496 /* Get current message header */
1497 gtk_tree_model_get (priv->header_model, &iter,
1498 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
1501 gtk_tree_path_free (path);
1506 modest_msg_view_window_get_message (ModestMsgViewWindow *self)
1508 ModestMsgViewWindowPrivate *priv;
1510 g_return_val_if_fail (self, NULL);
1512 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(self);
1514 return tny_msg_view_get_msg (TNY_MSG_VIEW (priv->msg_view));
1518 modest_msg_view_window_get_message_uid (ModestMsgViewWindow *self)
1520 ModestMsgViewWindowPrivate *priv;
1522 g_return_val_if_fail (self, NULL);
1524 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
1526 return (const gchar*) priv->msg_uid;
1529 /* Used for the Ctrl+F accelerator */
1531 modest_msg_view_window_toggle_find_toolbar (GtkWidget *obj,
1534 ModestMsgViewWindow *window = MODEST_MSG_VIEW_WINDOW (data);
1535 ModestMsgViewWindowPrivate *priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1537 if (GTK_WIDGET_VISIBLE (priv->find_toolbar)) {
1538 modest_msg_view_window_find_toolbar_close (obj, data);
1540 modest_msg_view_window_show_find_toolbar (obj, data);
1544 /* Handler for menu option */
1546 modest_msg_view_window_show_find_toolbar (GtkWidget *obj,
1549 ModestMsgViewWindow *window = MODEST_MSG_VIEW_WINDOW (data);
1550 ModestMsgViewWindowPrivate *priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1552 gtk_widget_show (priv->find_toolbar);
1553 hildon_find_toolbar_highlight_entry (HILDON_FIND_TOOLBAR (priv->find_toolbar), TRUE);
1556 /* Handler for click on the "X" close button in find toolbar */
1558 modest_msg_view_window_find_toolbar_close (GtkWidget *widget,
1559 ModestMsgViewWindow *obj)
1561 ModestMsgViewWindowPrivate *priv;
1563 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (obj);
1566 gtk_widget_hide (priv->find_toolbar);
1567 modest_msg_view_grab_focus (MODEST_MSG_VIEW (priv->msg_view));
1571 modest_msg_view_window_find_toolbar_search (GtkWidget *widget,
1572 ModestMsgViewWindow *obj)
1574 gchar *current_search;
1575 ModestMsgViewWindowPrivate *priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (obj);
1577 if (modest_mime_part_view_is_empty (MODEST_MIME_PART_VIEW (priv->msg_view))) {
1578 hildon_banner_show_information (NULL, NULL, _("mail_ib_nothing_to_find"));
1582 g_object_get (G_OBJECT (widget), "prefix", ¤t_search, NULL);
1584 if ((current_search == NULL) || (strcmp (current_search, "") == 0)) {
1585 g_free (current_search);
1586 hildon_banner_show_information (NULL, NULL, _CS("ecdg_ib_find_rep_enter_text"));
1590 if ((priv->last_search == NULL) || (strcmp (priv->last_search, current_search) != 0)) {
1592 g_free (priv->last_search);
1593 priv->last_search = g_strdup (current_search);
1594 result = modest_isearch_view_search (MODEST_ISEARCH_VIEW (priv->msg_view),
1597 hildon_banner_show_information (NULL, NULL,
1598 _HL("ckct_ib_find_no_matches"));
1599 g_free (priv->last_search);
1600 priv->last_search = NULL;
1602 hildon_find_toolbar_highlight_entry (HILDON_FIND_TOOLBAR (priv->find_toolbar), TRUE);
1605 if (!modest_isearch_view_search_next (MODEST_ISEARCH_VIEW (priv->msg_view))) {
1606 hildon_banner_show_information (NULL, NULL,
1607 _HL("ckct_ib_find_search_complete"));
1608 g_free (priv->last_search);
1609 priv->last_search = NULL;
1611 hildon_find_toolbar_highlight_entry (HILDON_FIND_TOOLBAR (priv->find_toolbar), TRUE);
1615 g_free (current_search);
1620 modest_msg_view_window_set_zoom (ModestWindow *window,
1623 ModestMsgViewWindowPrivate *priv;
1624 ModestWindowPrivate *parent_priv;
1626 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window));
1628 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1629 parent_priv = MODEST_WINDOW_GET_PRIVATE (window);
1630 modest_zoomable_set_zoom (MODEST_ZOOMABLE (priv->msg_view), zoom);
1635 modest_msg_view_window_get_zoom (ModestWindow *window)
1637 ModestMsgViewWindowPrivate *priv;
1639 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), 1.0);
1641 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1642 return modest_zoomable_get_zoom (MODEST_ZOOMABLE (priv->msg_view));
1646 modest_msg_view_window_zoom_plus (ModestWindow *window)
1649 ModestMsgViewWindowPrivate *priv;
1653 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), 1.0);
1654 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1656 zoom_level = modest_zoomable_get_zoom (MODEST_ZOOMABLE (priv->msg_view));
1658 if (zoom_level >= 2.0) {
1659 hildon_banner_show_information (NULL, NULL,
1660 _CS("ckct_ib_max_zoom_level_reached"));
1662 } else if (zoom_level >= 1.5) {
1664 } else if (zoom_level >= 1.2) {
1666 } else if (zoom_level >= 1.0) {
1668 } else if (zoom_level >= 0.8) {
1670 } else if (zoom_level >= 0.5) {
1676 /* set zoom level */
1677 int_zoom = (gint) rint (zoom_level*100.0+0.1);
1678 banner_text = g_strdup_printf (_HL("wdgt_ib_zoom"), int_zoom);
1679 modest_platform_information_banner (GTK_WIDGET (window), NULL, banner_text);
1680 g_free (banner_text);
1681 modest_zoomable_set_zoom (MODEST_ZOOMABLE (priv->msg_view), zoom_level);
1687 modest_msg_view_window_zoom_minus (ModestWindow *window)
1690 ModestMsgViewWindowPrivate *priv;
1694 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), 1.0);
1695 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1697 zoom_level = modest_zoomable_get_zoom (MODEST_ZOOMABLE (priv->msg_view));
1699 if (zoom_level <= 0.5) {
1700 hildon_banner_show_information (NULL, NULL,
1701 _CS("ckct_ib_min_zoom_level_reached"));
1703 } else if (zoom_level <= 0.8) {
1705 } else if (zoom_level <= 1.0) {
1707 } else if (zoom_level <= 1.2) {
1709 } else if (zoom_level <= 1.5) {
1711 } else if (zoom_level <= 2.0) {
1717 /* set zoom level */
1718 int_zoom = (gint) rint (zoom_level*100.0+0.1);
1719 banner_text = g_strdup_printf (_HL("wdgt_ib_zoom"), int_zoom);
1720 modest_platform_information_banner (GTK_WIDGET (window), NULL, banner_text);
1721 g_free (banner_text);
1722 modest_zoomable_set_zoom (MODEST_ZOOMABLE (priv->msg_view), zoom_level);
1728 modest_msg_view_window_key_event (GtkWidget *window,
1734 focus = gtk_window_get_focus (GTK_WINDOW (window));
1736 /* for the find toolbar case */
1737 if (focus && GTK_IS_ENTRY (focus)) {
1738 if (event->keyval == GDK_BackSpace) {
1740 copy = gdk_event_copy ((GdkEvent *) event);
1741 gtk_widget_event (focus, copy);
1742 gdk_event_free (copy);
1752 modest_msg_view_window_last_message_selected (ModestMsgViewWindow *window)
1755 ModestMsgViewWindowPrivate *priv;
1756 GtkTreeIter tmp_iter;
1757 gboolean is_last_selected;
1759 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), TRUE);
1760 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1762 /*if no model (so no rows at all), then virtually we are the last*/
1763 if (!priv->header_model || !priv->row_reference)
1766 if (!gtk_tree_row_reference_valid (priv->row_reference))
1769 path = gtk_tree_row_reference_get_path (priv->row_reference);
1773 is_last_selected = TRUE;
1774 while (is_last_selected) {
1776 gtk_tree_path_next (path);
1777 if (!gtk_tree_model_get_iter (priv->header_model, &tmp_iter, path))
1779 gtk_tree_model_get (priv->header_model, &tmp_iter,
1780 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
1783 if (msg_is_visible (header, priv->is_outbox))
1784 is_last_selected = FALSE;
1785 g_object_unref(G_OBJECT(header));
1788 gtk_tree_path_free (path);
1789 return is_last_selected;
1793 modest_msg_view_window_has_headers_model (ModestMsgViewWindow *window)
1795 ModestMsgViewWindowPrivate *priv;
1797 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), TRUE);
1798 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1800 return priv->header_model != NULL;
1804 modest_msg_view_window_is_search_result (ModestMsgViewWindow *window)
1806 ModestMsgViewWindowPrivate *priv;
1808 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), TRUE);
1809 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1811 return priv->is_search_result;
1815 msg_is_visible (TnyHeader *header, gboolean check_outbox)
1817 if ((tny_header_get_flags(header) & TNY_HEADER_FLAG_DELETED))
1819 if (!check_outbox) {
1822 ModestTnySendQueueStatus status;
1823 status = modest_tny_all_send_queues_get_msg_status (header);
1824 return ((status != MODEST_TNY_SEND_QUEUE_FAILED) &&
1825 (status != MODEST_TNY_SEND_QUEUE_SENDING));
1830 modest_msg_view_window_first_message_selected (ModestMsgViewWindow *window)
1833 ModestMsgViewWindowPrivate *priv;
1834 gboolean is_first_selected;
1835 GtkTreeIter tmp_iter;
1837 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), TRUE);
1838 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1840 /*if no model (so no rows at all), then virtually we are the first*/
1841 if (!priv->header_model || !priv->row_reference)
1844 if (!gtk_tree_row_reference_valid (priv->row_reference))
1847 path = gtk_tree_row_reference_get_path (priv->row_reference);
1851 is_first_selected = TRUE;
1852 while (is_first_selected) {
1854 if(!gtk_tree_path_prev (path))
1856 /* Here the 'if' is needless for logic, but let make sure
1857 * iter is valid for gtk_tree_model_get. */
1858 if (!gtk_tree_model_get_iter (priv->header_model, &tmp_iter, path))
1860 gtk_tree_model_get (priv->header_model, &tmp_iter,
1861 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
1864 if (msg_is_visible (header, priv->is_outbox))
1865 is_first_selected = FALSE;
1866 g_object_unref(G_OBJECT(header));
1869 gtk_tree_path_free (path);
1870 return is_first_selected;
1877 GtkTreeRowReference *row_reference;
1881 message_reader_performer (gboolean canceled,
1883 GtkWindow *parent_window,
1884 TnyAccount *account,
1887 ModestMailOperation *mail_op = NULL;
1888 MsgReaderInfo *info;
1890 info = (MsgReaderInfo *) user_data;
1891 if (canceled || err) {
1892 update_window_title (MODEST_MSG_VIEW_WINDOW (parent_window));
1893 modest_ui_actions_on_close_window (NULL, MODEST_WINDOW (parent_window));
1897 /* Register the header - it'll be unregistered in the callback */
1899 modest_window_mgr_register_header (modest_runtime_get_window_mgr (), info->header, NULL);
1901 /* New mail operation */
1902 mail_op = modest_mail_operation_new_with_error_handling (G_OBJECT(parent_window),
1903 modest_ui_actions_disk_operations_error_handler,
1906 modest_mail_operation_queue_add (modest_runtime_get_mail_operation_queue (), mail_op);
1908 modest_mail_operation_get_msg (mail_op, info->header, TRUE, view_msg_cb, info->row_reference);
1910 modest_mail_operation_find_msg (mail_op, info->folder, info->msg_uid, TRUE, view_msg_cb, NULL);
1911 g_object_unref (mail_op);
1913 /* Update dimming rules */
1914 modest_ui_actions_check_toolbar_dimming_rules (MODEST_WINDOW (parent_window));
1915 modest_ui_actions_check_menu_dimming_rules (MODEST_WINDOW (parent_window));
1918 /* Frees. The row_reference will be freed by the view_msg_cb callback */
1919 g_free (info->msg_uid);
1921 g_object_unref (info->folder);
1923 g_object_unref (info->header);
1924 g_slice_free (MsgReaderInfo, info);
1929 * Reads the message whose summary item is @header. It takes care of
1930 * several things, among others:
1932 * If the message was not previously downloaded then ask the user
1933 * before downloading. If there is no connection launch the connection
1934 * dialog. Update toolbar dimming rules.
1936 * Returns: TRUE if the mail operation was started, otherwise if the
1937 * user do not want to download the message, or if the user do not
1938 * want to connect, then the operation is not issued
1941 message_reader (ModestMsgViewWindow *window,
1942 ModestMsgViewWindowPrivate *priv,
1944 const gchar *msg_uid,
1946 GtkTreeRowReference *row_reference)
1948 ModestWindowMgr *mgr;
1949 TnyAccount *account = NULL;
1950 MsgReaderInfo *info;
1952 /* We set the header from model while we're loading */
1953 tny_header_view_set_header (TNY_HEADER_VIEW (priv->msg_view), header);
1954 gtk_window_set_title (GTK_WINDOW (window), _CS("ckdg_pb_updating"));
1960 g_object_ref (folder);
1962 mgr = modest_runtime_get_window_mgr ();
1963 /* Msg download completed */
1964 if (!header || !(tny_header_get_flags (header) & TNY_HEADER_FLAG_CACHED)) {
1966 /* Ask the user if he wants to download the message if
1968 if (!tny_device_is_online (modest_runtime_get_device())) {
1969 GtkResponseType response;
1971 response = modest_platform_run_confirmation_dialog (GTK_WINDOW (window),
1972 _("mcen_nc_get_msg"));
1973 if (response == GTK_RESPONSE_CANCEL) {
1974 update_window_title (window);
1979 folder = tny_header_get_folder (header);
1981 info = g_slice_new (MsgReaderInfo);
1982 info->msg_uid = g_strdup (msg_uid);
1984 info->header = g_object_ref (header);
1986 info->header = NULL;
1988 info->folder = g_object_ref (folder);
1990 info->folder = NULL;
1991 if (row_reference) {
1992 info->row_reference = gtk_tree_row_reference_copy (row_reference);
1994 info->row_reference = NULL;
1997 /* Offer the connection dialog if necessary */
1998 modest_platform_connect_if_remote_and_perform ((GtkWindow *) window,
2000 TNY_FOLDER_STORE (folder),
2001 message_reader_performer,
2004 g_object_unref (folder);
2010 folder = tny_header_get_folder (header);
2013 account = tny_folder_get_account (folder);
2015 info = g_slice_new (MsgReaderInfo);
2016 info->msg_uid = g_strdup (msg_uid);
2018 info->folder = g_object_ref (folder);
2020 info->folder = NULL;
2022 info->header = g_object_ref (header);
2024 info->header = NULL;
2026 info->row_reference = gtk_tree_row_reference_copy (row_reference);
2028 info->row_reference = NULL;
2030 message_reader_performer (FALSE, NULL, (GtkWindow *) window, account, info);
2032 g_object_unref (account);
2034 g_object_unref (folder);
2040 modest_msg_view_window_select_next_message (ModestMsgViewWindow *window)
2042 ModestMsgViewWindowPrivate *priv;
2043 GtkTreePath *path= NULL;
2044 GtkTreeIter tmp_iter;
2046 gboolean retval = TRUE;
2047 GtkTreeRowReference *row_reference = NULL;
2049 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), FALSE);
2050 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
2052 if (!priv->row_reference)
2055 /* Update the next row reference if it's not valid. This could
2056 happen if for example the header which it was pointing to,
2057 was deleted. The best place to do it is in the row-deleted
2058 handler but the tinymail model do not work like the glib
2059 tree models and reports the deletion when the row is still
2061 if (!gtk_tree_row_reference_valid (priv->next_row_reference)) {
2062 if (priv->next_row_reference) {
2063 gtk_tree_row_reference_free (priv->next_row_reference);
2065 if (gtk_tree_row_reference_valid (priv->row_reference)) {
2066 priv->next_row_reference = gtk_tree_row_reference_copy (priv->row_reference);
2067 select_next_valid_row (priv->header_model, &(priv->next_row_reference), FALSE, priv->is_outbox);
2069 priv->next_row_reference = NULL;
2072 if (priv->next_row_reference)
2073 path = gtk_tree_row_reference_get_path (priv->next_row_reference);
2077 row_reference = gtk_tree_row_reference_copy (priv->next_row_reference);
2079 gtk_tree_model_get_iter (priv->header_model,
2082 gtk_tree_path_free (path);
2084 gtk_tree_model_get (priv->header_model, &tmp_iter,
2085 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
2088 /* Read the message & show it */
2089 if (!message_reader (window, priv, header, NULL, NULL, row_reference)) {
2092 gtk_tree_row_reference_free (row_reference);
2095 g_object_unref (header);
2101 modest_msg_view_window_select_previous_message (ModestMsgViewWindow *window)
2103 ModestMsgViewWindowPrivate *priv = NULL;
2105 gboolean finished = FALSE;
2106 gboolean retval = FALSE;
2108 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), FALSE);
2109 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
2111 if (priv->row_reference && !gtk_tree_row_reference_valid (priv->row_reference)) {
2112 gtk_tree_row_reference_free (priv->row_reference);
2113 priv->row_reference = NULL;
2116 /* Return inmediatly if there is no header model */
2117 if (!priv->header_model || !priv->row_reference)
2120 path = gtk_tree_row_reference_get_path (priv->row_reference);
2121 while (!finished && gtk_tree_path_prev (path)) {
2125 gtk_tree_model_get_iter (priv->header_model, &iter, path);
2126 gtk_tree_model_get (priv->header_model, &iter,
2127 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
2131 if (msg_is_visible (header, priv->is_outbox)) {
2132 GtkTreeRowReference *row_reference;
2133 row_reference = gtk_tree_row_reference_new (priv->header_model, path);
2134 /* Read the message & show it */
2135 retval = message_reader (window, priv, header, NULL, NULL, row_reference);
2136 gtk_tree_row_reference_free (row_reference);
2140 g_object_unref (header);
2144 gtk_tree_path_free (path);
2149 view_msg_cb (ModestMailOperation *mail_op,
2156 ModestMsgViewWindow *self = NULL;
2157 ModestMsgViewWindowPrivate *priv = NULL;
2158 GtkTreeRowReference *row_reference = NULL;
2160 /* Unregister the header (it was registered before creating the mail operation) */
2161 modest_window_mgr_unregister_header (modest_runtime_get_window_mgr (), header);
2163 row_reference = (GtkTreeRowReference *) user_data;
2166 gtk_tree_row_reference_free (row_reference);
2167 self = (ModestMsgViewWindow *) modest_mail_operation_get_source (mail_op);
2169 /* Restore window title */
2170 update_window_title (self);
2171 modest_ui_actions_on_close_window (NULL, MODEST_WINDOW (self));
2172 g_object_unref (self);
2177 /* If there was any error */
2178 if (!modest_ui_actions_msg_retrieval_check (mail_op, header, msg)) {
2180 gtk_tree_row_reference_free (row_reference);
2181 self = (ModestMsgViewWindow *) modest_mail_operation_get_source (mail_op);
2183 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
2184 /* First we check if the parent is a folder window */
2185 if (priv->msg_uid && !modest_hildon2_window_mgr_get_folder_window (MODEST_HILDON2_WINDOW_MGR (modest_runtime_get_window_mgr ()))) {
2187 TnyAccount *account = NULL;
2188 GtkWidget *header_window = NULL;
2190 is_merge = g_str_has_prefix (priv->msg_uid, "merge:");
2192 /* Get the account */
2194 account = tny_account_store_find_account (TNY_ACCOUNT_STORE (modest_runtime_get_account_store ()),
2197 if (is_merge || account) {
2198 TnyFolder *folder = NULL;
2200 /* Try to get the message, if it's already downloaded
2201 we don't need to connect */
2203 folder = modest_tny_folder_store_find_folder_from_uri (TNY_FOLDER_STORE (account),
2206 ModestTnyAccountStore *account_store;
2207 ModestTnyLocalFoldersAccount *local_folders_account;
2209 account_store = modest_runtime_get_account_store ();
2210 local_folders_account = MODEST_TNY_LOCAL_FOLDERS_ACCOUNT (
2211 modest_tny_account_store_get_local_folders_account (account_store));
2212 folder = modest_tny_local_folders_account_get_merged_outbox (local_folders_account);
2213 g_object_unref (local_folders_account);
2215 if (account) g_object_unref (account);
2218 header_window = (GtkWidget *)
2219 modest_header_window_new (
2221 modest_window_get_active_account (MODEST_WINDOW (self)),
2222 modest_window_get_active_mailbox (MODEST_WINDOW (self)));
2223 if (!modest_window_mgr_register_window (modest_runtime_get_window_mgr (),
2224 MODEST_WINDOW (header_window),
2226 gtk_widget_destroy (GTK_WIDGET (header_window));
2228 gtk_widget_show_all (GTK_WIDGET (header_window));
2230 g_object_unref (folder);
2236 /* Restore window title */
2237 update_window_title (self);
2238 modest_ui_actions_on_close_window (NULL, MODEST_WINDOW (self));
2239 g_object_unref (self);
2244 if (msg && TNY_IS_CAMEL_BS_MSG (msg)) {
2246 body = modest_tny_msg_find_body_part (msg, TRUE);
2248 if (body && !tny_camel_bs_mime_part_is_fetched (TNY_CAMEL_BS_MIME_PART (body))) {
2249 /* We have body structure but not the body mime part. What we do
2250 * is restarting load of message */
2251 self = (ModestMsgViewWindow *) modest_mail_operation_get_source (mail_op);
2252 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
2254 tny_header_unset_flag (TNY_HEADER (header), TNY_HEADER_FLAG_CACHED);
2256 modest_msg_view_window_reload (self);
2259 gtk_tree_row_reference_free (row_reference);
2260 g_object_unref (body);
2265 g_object_unref (body);
2268 /* Get the window */
2269 self = (ModestMsgViewWindow *) modest_mail_operation_get_source (mail_op);
2270 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self));
2271 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
2273 /* Update the row reference */
2274 if (priv->row_reference != NULL) {
2275 gtk_tree_row_reference_free (priv->row_reference);
2276 priv->row_reference = (row_reference && gtk_tree_row_reference_valid (row_reference))?gtk_tree_row_reference_copy (row_reference):NULL;
2277 if (priv->next_row_reference != NULL) {
2278 gtk_tree_row_reference_free (priv->next_row_reference);
2280 if (priv->row_reference) {
2281 priv->next_row_reference = gtk_tree_row_reference_copy (priv->row_reference);
2282 select_next_valid_row (priv->header_model, &(priv->next_row_reference), TRUE, priv->is_outbox);
2284 priv->next_row_reference = NULL;
2288 /* Mark header as read */
2289 if (!(tny_header_get_flags (header) & TNY_HEADER_FLAG_SEEN))
2290 tny_header_set_flag (header, TNY_HEADER_FLAG_SEEN);
2292 /* Set new message */
2293 if (priv->msg_view != NULL && TNY_IS_MSG_VIEW (priv->msg_view)) {
2294 tny_msg_view_set_msg (TNY_MSG_VIEW (priv->msg_view), msg);
2295 modest_msg_view_window_update_priority (self);
2296 update_window_title (MODEST_MSG_VIEW_WINDOW (self));
2297 update_branding (MODEST_MSG_VIEW_WINDOW (self));
2298 modest_msg_view_grab_focus (MODEST_MSG_VIEW (priv->msg_view));
2301 /* Set the new message uid of the window */
2302 if (priv->msg_uid) {
2303 g_free (priv->msg_uid);
2304 priv->msg_uid = modest_tny_folder_get_header_unique_id (header);
2307 /* Notify the observers */
2308 g_signal_emit (G_OBJECT (self), signals[MSG_CHANGED_SIGNAL],
2309 0, priv->header_model, priv->row_reference);
2311 /* Sync the flags if the message is not opened from a header
2312 model, i.e, if it's opened from a notification */
2313 if (!priv->header_model)
2317 g_object_unref (self);
2319 gtk_tree_row_reference_free (row_reference);
2323 modest_msg_view_window_get_folder_type (ModestMsgViewWindow *window)
2325 ModestMsgViewWindowPrivate *priv;
2327 TnyFolderType folder_type;
2329 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
2331 folder_type = TNY_FOLDER_TYPE_UNKNOWN;
2333 msg = tny_msg_view_get_msg (TNY_MSG_VIEW (priv->msg_view));
2337 folder = tny_msg_get_folder (msg);
2339 folder_type = modest_tny_folder_guess_folder_type (folder);
2340 g_object_unref (folder);
2342 g_object_unref (msg);
2350 modest_msg_view_window_update_priority (ModestMsgViewWindow *window)
2352 ModestMsgViewWindowPrivate *priv;
2353 TnyHeader *header = NULL;
2354 TnyHeaderFlags flags = 0;
2356 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
2358 if (priv->header_model && priv->row_reference && gtk_tree_row_reference_valid (priv->row_reference)) {
2360 GtkTreePath *path = NULL;
2362 path = gtk_tree_row_reference_get_path (priv->row_reference);
2363 g_return_if_fail (path != NULL);
2364 gtk_tree_model_get_iter (priv->header_model,
2366 gtk_tree_row_reference_get_path (priv->row_reference));
2368 gtk_tree_model_get (priv->header_model, &iter, TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
2370 gtk_tree_path_free (path);
2373 msg = tny_msg_view_get_msg (TNY_MSG_VIEW (priv->msg_view));
2375 header = tny_msg_get_header (msg);
2376 g_object_unref (msg);
2381 flags = tny_header_get_flags (header);
2382 g_object_unref(G_OBJECT(header));
2385 modest_msg_view_set_priority (MODEST_MSG_VIEW(priv->msg_view), flags);
2390 toolbar_resize (ModestMsgViewWindow *self)
2392 ModestMsgViewWindowPrivate *priv = NULL;
2393 ModestWindowPrivate *parent_priv = NULL;
2395 gint static_button_size;
2396 ModestWindowMgr *mgr;
2398 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self));
2399 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
2400 parent_priv = MODEST_WINDOW_GET_PRIVATE(self);
2402 mgr = modest_runtime_get_window_mgr ();
2403 static_button_size = modest_window_mgr_get_fullscreen_mode (mgr)?120:120;
2405 if (parent_priv->toolbar) {
2406 /* Set expandable and homogeneous tool buttons */
2407 widget = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/ToolbarMessageReply");
2408 gtk_tool_item_set_expand (GTK_TOOL_ITEM (widget), TRUE);
2409 gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (widget), TRUE);
2410 widget = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/ToolbarMessageReplyAll");
2411 gtk_tool_item_set_expand (GTK_TOOL_ITEM (widget), TRUE);
2412 gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (widget), TRUE);
2413 widget = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/ToolbarMessageForward");
2414 gtk_tool_item_set_expand (GTK_TOOL_ITEM (widget), TRUE);
2415 gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (widget), TRUE);
2416 widget = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/ToolbarDeleteMessage");
2417 gtk_tool_item_set_expand (GTK_TOOL_ITEM (widget), TRUE);
2418 gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (widget), TRUE);
2419 widget = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/ToolbarDownloadExternalImages");
2420 gtk_tool_item_set_expand (GTK_TOOL_ITEM (widget), TRUE);
2421 gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (widget), TRUE);
2422 gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (priv->next_toolitem), TRUE);
2423 gtk_tool_item_set_expand (GTK_TOOL_ITEM (priv->next_toolitem), TRUE);
2424 gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (priv->prev_toolitem), TRUE);
2425 gtk_tool_item_set_expand (GTK_TOOL_ITEM (priv->prev_toolitem), TRUE);
2430 modest_msg_view_window_show_toolbar (ModestWindow *self,
2431 gboolean show_toolbar)
2433 ModestMsgViewWindowPrivate *priv = NULL;
2434 ModestWindowPrivate *parent_priv;
2436 parent_priv = MODEST_WINDOW_GET_PRIVATE(self);
2437 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(self);
2439 /* Set optimized view status */
2440 priv->optimized_view = !show_toolbar;
2442 if (!parent_priv->toolbar) {
2443 parent_priv->toolbar = gtk_ui_manager_get_widget (parent_priv->ui_manager,
2445 gtk_toolbar_set_icon_size (GTK_TOOLBAR (parent_priv->toolbar), HILDON_ICON_SIZE_FINGER);
2446 gtk_widget_set_no_show_all (parent_priv->toolbar, TRUE);
2448 priv->next_toolitem = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/ToolbarMessageNext");
2449 priv->prev_toolitem = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/ToolbarMessageBack");
2450 toolbar_resize (MODEST_MSG_VIEW_WINDOW (self));
2453 hildon_window_add_toolbar (HILDON_WINDOW (self),
2454 GTK_TOOLBAR (parent_priv->toolbar));
2459 /* Quick hack: this prevents toolbar icons "dance" when progress bar show status is changed */
2460 /* TODO: resize mode migth be GTK_RESIZE_QUEUE, in order to avoid unneccesary shows */
2461 gtk_container_set_resize_mode (GTK_CONTAINER(parent_priv->toolbar), GTK_RESIZE_IMMEDIATE);
2463 gtk_widget_show (GTK_WIDGET (parent_priv->toolbar));
2464 if (modest_msg_view_window_transfer_mode_enabled (MODEST_MSG_VIEW_WINDOW (self)))
2465 set_progress_hint (MODEST_MSG_VIEW_WINDOW (self), TRUE);
2467 set_progress_hint (MODEST_MSG_VIEW_WINDOW (self), FALSE);
2470 gtk_widget_set_no_show_all (parent_priv->toolbar, TRUE);
2471 gtk_widget_hide (GTK_WIDGET (parent_priv->toolbar));
2476 modest_msg_view_window_clipboard_owner_change (GtkClipboard *clipboard,
2478 ModestMsgViewWindow *window)
2480 if (!GTK_WIDGET_VISIBLE (window))
2483 modest_window_check_dimming_rules_group (MODEST_WINDOW (window), MODEST_DIMMING_RULES_CLIPBOARD);
2487 modest_msg_view_window_transfer_mode_enabled (ModestMsgViewWindow *self)
2489 ModestMsgViewWindowPrivate *priv;
2491 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self), FALSE);
2492 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(self);
2494 return priv->progress_hint;
2498 observers_empty (ModestMsgViewWindow *self)
2501 ModestMsgViewWindowPrivate *priv;
2502 gboolean is_empty = TRUE;
2503 guint pending_ops = 0;
2505 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(self);
2506 tmp = priv->progress_widgets;
2508 /* Check all observers */
2509 while (tmp && is_empty) {
2510 pending_ops = modest_progress_object_num_pending_operations (MODEST_PROGRESS_OBJECT(tmp->data));
2511 is_empty = pending_ops == 0;
2513 tmp = g_slist_next(tmp);
2520 on_account_removed (TnyAccountStore *account_store,
2521 TnyAccount *account,
2524 /* Do nothing if it's a transport account, because we only
2525 show the messages of a store account */
2526 if (tny_account_get_account_type(account) == TNY_ACCOUNT_TYPE_STORE) {
2527 const gchar *parent_acc = NULL;
2528 const gchar *our_acc = NULL;
2530 our_acc = modest_window_get_active_account (MODEST_WINDOW (user_data));
2531 parent_acc = modest_tny_account_get_parent_modest_account_name_for_server_account (account);
2533 /* Close this window if I'm showing a message of the removed account */
2534 if (our_acc && parent_acc && strcmp (parent_acc, our_acc) == 0)
2535 modest_ui_actions_on_close_window (NULL, MODEST_WINDOW (user_data));
2540 on_mail_operation_started (ModestMailOperation *mail_op,
2543 ModestMsgViewWindow *self;
2544 ModestMailOperationTypeOperation op_type;
2546 ModestMsgViewWindowPrivate *priv;
2547 GObject *source = NULL;
2549 self = MODEST_MSG_VIEW_WINDOW (user_data);
2550 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
2551 op_type = modest_mail_operation_get_type_operation (mail_op);
2552 tmp = priv->progress_widgets;
2553 source = modest_mail_operation_get_source(mail_op);
2554 if (G_OBJECT (self) == source) {
2555 if (op_type == MODEST_MAIL_OPERATION_TYPE_RECEIVE ||
2556 op_type == MODEST_MAIL_OPERATION_TYPE_OPEN ||
2557 op_type == MODEST_MAIL_OPERATION_TYPE_DELETE) {
2558 set_progress_hint (self, TRUE);
2560 modest_progress_object_add_operation (
2561 MODEST_PROGRESS_OBJECT (tmp->data),
2563 tmp = g_slist_next (tmp);
2567 g_object_unref (source);
2569 /* Update dimming rules */
2570 check_dimming_rules_after_change (self);
2574 on_mail_operation_finished (ModestMailOperation *mail_op,
2577 ModestMsgViewWindow *self;
2578 ModestMailOperationTypeOperation op_type;
2580 ModestMsgViewWindowPrivate *priv;
2582 self = MODEST_MSG_VIEW_WINDOW (user_data);
2583 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
2584 op_type = modest_mail_operation_get_type_operation (mail_op);
2585 tmp = priv->progress_widgets;
2587 if (op_type == MODEST_MAIL_OPERATION_TYPE_RECEIVE ||
2588 op_type == MODEST_MAIL_OPERATION_TYPE_OPEN ||
2589 op_type == MODEST_MAIL_OPERATION_TYPE_DELETE) {
2591 modest_progress_object_remove_operation (MODEST_PROGRESS_OBJECT (tmp->data),
2593 tmp = g_slist_next (tmp);
2596 /* If no more operations are being observed, NORMAL mode is enabled again */
2597 if (observers_empty (self)) {
2598 set_progress_hint (self, FALSE);
2602 /* Update dimming rules. We have to do this right here
2603 and not in view_msg_cb because at that point the
2604 transfer mode is still enabled so the dimming rule
2605 won't let the user delete the message that has been
2606 readed for example */
2607 check_dimming_rules_after_change (self);
2611 on_queue_changed (ModestMailOperationQueue *queue,
2612 ModestMailOperation *mail_op,
2613 ModestMailOperationQueueNotification type,
2614 ModestMsgViewWindow *self)
2616 ModestMsgViewWindowPrivate *priv;
2618 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
2620 /* If this operations was created by another window, do nothing */
2621 if (!modest_mail_operation_is_mine (mail_op, G_OBJECT(self)))
2624 if (type == MODEST_MAIL_OPERATION_QUEUE_OPERATION_ADDED) {
2625 priv->sighandlers = modest_signal_mgr_connect (priv->sighandlers,
2627 "operation-started",
2628 G_CALLBACK (on_mail_operation_started),
2630 priv->sighandlers = modest_signal_mgr_connect (priv->sighandlers,
2632 "operation-finished",
2633 G_CALLBACK (on_mail_operation_finished),
2635 } else if (type == MODEST_MAIL_OPERATION_QUEUE_OPERATION_REMOVED) {
2636 priv->sighandlers = modest_signal_mgr_disconnect (priv->sighandlers,
2638 "operation-started");
2639 priv->sighandlers = modest_signal_mgr_disconnect (priv->sighandlers,
2641 "operation-finished");
2646 modest_msg_view_window_get_attachments (ModestMsgViewWindow *win)
2648 ModestMsgViewWindowPrivate *priv;
2649 TnyList *selected_attachments = NULL;
2651 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (win), NULL);
2652 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (win);
2654 /* In Hildon 2.2 as there's no selection we assume we have all attachments selected */
2655 selected_attachments = modest_msg_view_get_attachments (MODEST_MSG_VIEW (priv->msg_view));
2657 return selected_attachments;
2661 ModestMsgViewWindow *self;
2663 gchar *attachment_uid;
2664 } DecodeAsyncHelper;
2667 on_decode_to_stream_async_handler (TnyMimePart *mime_part,
2673 DecodeAsyncHelper *helper = (DecodeAsyncHelper *) user_data;
2674 const gchar *content_type;
2676 if (cancelled || err) {
2679 if ((err->domain == TNY_ERROR_DOMAIN) &&
2680 (err->code == TNY_IO_ERROR_WRITE) &&
2681 (errno == ENOSPC)) {
2682 msg = g_strdup_printf (_KR("cerm_device_memory_full"), "");
2684 msg = g_strdup (_("mail_ib_file_operation_failed"));
2686 modest_platform_information_banner (NULL, NULL, msg);
2692 /* It could happen that the window was closed. So we
2693 assume it is a cancelation */
2694 if (!GTK_WIDGET_VISIBLE (helper->self))
2697 /* Remove the progress hint */
2698 set_progress_hint (helper->self, FALSE);
2700 content_type = tny_mime_part_get_content_type (mime_part);
2701 if (g_str_has_prefix (content_type, "message/rfc822")) {
2702 ModestWindowMgr *mgr;
2703 ModestWindow *msg_win = NULL;
2706 const gchar *mailbox;
2707 TnyStream *file_stream;
2710 fd = g_open (helper->file_path, O_RDONLY, 0644);
2712 file_stream = tny_fs_stream_new (fd);
2714 mgr = modest_runtime_get_window_mgr ();
2716 account = g_strdup (modest_window_get_active_account (MODEST_WINDOW (helper->self)));
2717 mailbox = modest_window_get_active_mailbox (MODEST_WINDOW (helper->self));
2720 account = modest_account_mgr_get_default_account (modest_runtime_get_account_mgr ());
2722 msg = tny_camel_msg_new ();
2723 tny_camel_msg_parse (msg, file_stream);
2724 msg_win = modest_msg_view_window_new_for_attachment (TNY_MSG (msg), account, mailbox, helper->attachment_uid);
2725 modest_window_set_zoom (MODEST_WINDOW (msg_win),
2726 modest_window_get_zoom (MODEST_WINDOW (helper->self)));
2727 if (modest_window_mgr_register_window (mgr, msg_win, MODEST_WINDOW (helper->self)))
2728 gtk_widget_show_all (GTK_WIDGET (msg_win));
2730 gtk_widget_destroy (GTK_WIDGET (msg_win));
2731 g_object_unref (msg);
2732 g_object_unref (file_stream);
2734 modest_platform_information_banner (NULL, NULL, _("mail_ib_file_operation_failed"));
2739 /* make the file read-only */
2740 g_chmod(helper->file_path, 0444);
2742 /* Activate the file */
2743 modest_platform_activate_file (helper->file_path, tny_mime_part_get_content_type (mime_part));
2748 g_object_unref (helper->self);
2749 g_free (helper->file_path);
2750 g_free (helper->attachment_uid);
2751 g_slice_free (DecodeAsyncHelper, helper);
2755 view_attachment_connect_handler (gboolean canceled,
2757 GtkWindow *parent_window,
2758 TnyAccount *account,
2762 if (canceled || err) {
2763 g_object_unref (part);
2767 modest_msg_view_window_view_attachment (MODEST_MSG_VIEW_WINDOW (parent_window),
2769 g_object_unref (part);
2773 modest_msg_view_window_view_attachment (ModestMsgViewWindow *window,
2774 TnyMimePart *mime_part)
2776 ModestMsgViewWindowPrivate *priv;
2777 const gchar *msg_uid;
2778 gchar *attachment_uid = NULL;
2779 gint attachment_index = 0;
2780 TnyList *attachments;
2782 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window));
2783 g_return_if_fail (TNY_IS_MIME_PART (mime_part) || (mime_part == NULL));
2784 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
2786 msg_uid = modest_msg_view_window_get_message_uid (MODEST_MSG_VIEW_WINDOW (window));
2787 attachments = modest_msg_view_get_attachments (MODEST_MSG_VIEW (priv->msg_view));
2788 attachment_index = modest_list_index (attachments, (GObject *) mime_part);
2789 g_object_unref (attachments);
2791 if (msg_uid && attachment_index >= 0) {
2792 attachment_uid = g_strdup_printf ("%s/%d", msg_uid, attachment_index);
2795 if (mime_part == NULL) {
2796 gboolean error = FALSE;
2797 TnyList *selected_attachments = modest_msg_view_get_selected_attachments (MODEST_MSG_VIEW (priv->msg_view));
2798 if (selected_attachments == NULL || tny_list_get_length (selected_attachments) == 0) {
2800 } else if (tny_list_get_length (selected_attachments) > 1) {
2801 hildon_banner_show_information (NULL, NULL, _("mcen_ib_unable_to_display_more"));
2805 iter = tny_list_create_iterator (selected_attachments);
2806 mime_part = (TnyMimePart *) tny_iterator_get_current (iter);
2807 g_object_unref (iter);
2809 if (selected_attachments)
2810 g_object_unref (selected_attachments);
2815 g_object_ref (mime_part);
2818 if (tny_mime_part_is_purged (mime_part))
2821 if (TNY_IS_CAMEL_BS_MIME_PART (mime_part) &&
2822 !tny_camel_bs_mime_part_is_fetched (TNY_CAMEL_BS_MIME_PART (mime_part))) {
2824 TnyAccount *account;
2826 is_merge = g_str_has_prefix (priv->msg_uid, "merge:");
2828 /* Get the account */
2830 account = tny_account_store_find_account (TNY_ACCOUNT_STORE (modest_runtime_get_account_store ()),
2833 if (!tny_device_is_online (modest_runtime_get_device())) {
2834 modest_platform_connect_and_perform (GTK_WINDOW (window),
2836 TNY_ACCOUNT (account),
2837 (ModestConnectedPerformer) view_attachment_connect_handler,
2838 g_object_ref (mime_part));
2843 if (!modest_tny_mime_part_is_msg (mime_part) && tny_mime_part_get_filename (mime_part)) {
2844 gchar *filepath = NULL;
2845 const gchar *att_filename = tny_mime_part_get_filename (mime_part);
2846 gboolean show_error_banner = FALSE;
2847 TnyFsStream *temp_stream = NULL;
2848 temp_stream = modest_utils_create_temp_stream (att_filename, attachment_uid,
2851 if (temp_stream != NULL) {
2852 ModestAccountMgr *mgr;
2853 DecodeAsyncHelper *helper;
2854 gboolean decode_in_provider;
2855 ModestProtocol *protocol;
2856 const gchar *account;
2858 /* Activate progress hint */
2859 set_progress_hint (window, TRUE);
2861 helper = g_slice_new0 (DecodeAsyncHelper);
2862 helper->self = g_object_ref (window);
2863 helper->file_path = g_strdup (filepath);
2864 helper->attachment_uid = g_strdup (attachment_uid);
2866 decode_in_provider = FALSE;
2867 mgr = modest_runtime_get_account_mgr ();
2868 account = modest_window_get_active_account (MODEST_WINDOW (window));
2869 if (modest_account_mgr_account_is_multimailbox (mgr, account, &protocol)) {
2870 if (MODEST_IS_ACCOUNT_PROTOCOL (protocol)) {
2872 uri = g_strconcat ("file://", filepath, NULL);
2873 decode_in_provider =
2874 modest_account_protocol_decode_part_to_stream_async (
2875 MODEST_ACCOUNT_PROTOCOL (protocol),
2878 TNY_STREAM (temp_stream),
2879 on_decode_to_stream_async_handler,
2886 if (!decode_in_provider)
2887 tny_mime_part_decode_to_stream_async (mime_part, TNY_STREAM (temp_stream),
2888 on_decode_to_stream_async_handler,
2891 g_object_unref (temp_stream);
2892 /* NOTE: files in the temporary area will be automatically
2893 * cleaned after some time if they are no longer in use */
2896 const gchar *content_type;
2897 /* the file may already exist but it isn't writable,
2898 * let's try to open it anyway */
2899 content_type = tny_mime_part_get_content_type (mime_part);
2900 modest_platform_activate_file (filepath, content_type);
2902 g_warning ("%s: modest_utils_create_temp_stream failed", __FUNCTION__);
2903 show_error_banner = TRUE;
2908 if (show_error_banner)
2909 modest_platform_information_banner (NULL, NULL, _("mail_ib_file_operation_failed"));
2910 } else if (!modest_tny_mime_part_is_msg (mime_part)) {
2911 ModestWindowMgr *mgr;
2912 ModestWindow *msg_win = NULL;
2913 TnyMsg *current_msg;
2917 current_msg = modest_msg_view_window_get_message (MODEST_MSG_VIEW_WINDOW (window));
2918 mgr = modest_runtime_get_window_mgr ();
2919 header = tny_msg_get_header (TNY_MSG (current_msg));
2920 found = modest_window_mgr_find_registered_message_uid (mgr,
2925 g_debug ("window for this body is already being created");
2928 /* it's not found, so create a new window for it */
2929 modest_window_mgr_register_header (mgr, header, attachment_uid); /* register the uid before building the window */
2930 gchar *account = g_strdup (modest_window_get_active_account (MODEST_WINDOW (window)));
2931 const gchar *mailbox = modest_window_get_active_mailbox (MODEST_WINDOW (window));
2933 account = modest_account_mgr_get_default_account (modest_runtime_get_account_mgr ());
2935 msg_win = modest_msg_view_window_new_with_other_body (TNY_MSG (current_msg), TNY_MIME_PART (mime_part),
2936 account, mailbox, attachment_uid);
2938 modest_window_set_zoom (MODEST_WINDOW (msg_win),
2939 modest_window_get_zoom (MODEST_WINDOW (window)));
2940 if (modest_window_mgr_register_window (mgr, msg_win, MODEST_WINDOW (window)))
2941 gtk_widget_show_all (GTK_WIDGET (msg_win));
2943 gtk_widget_destroy (GTK_WIDGET (msg_win));
2945 g_object_unref (current_msg);
2947 /* message attachment */
2948 TnyHeader *header = NULL;
2949 ModestWindowMgr *mgr;
2950 ModestWindow *msg_win = NULL;
2953 header = tny_msg_get_header (TNY_MSG (mime_part));
2954 mgr = modest_runtime_get_window_mgr ();
2955 found = modest_window_mgr_find_registered_header (mgr, header, &msg_win);
2958 /* if it's found, but there is no msg_win, it's probably in the process of being created;
2959 * thus, we don't do anything */
2960 g_debug ("window for is already being created");
2962 /* it's not found, so create a new window for it */
2963 modest_window_mgr_register_header (mgr, header, attachment_uid); /* register the uid before building the window */
2964 gchar *account = g_strdup (modest_window_get_active_account (MODEST_WINDOW (window)));
2965 const gchar *mailbox = modest_window_get_active_mailbox (MODEST_WINDOW (window));
2967 account = modest_account_mgr_get_default_account (modest_runtime_get_account_mgr ());
2968 msg_win = modest_msg_view_window_new_for_attachment (TNY_MSG (mime_part), account,
2969 mailbox, attachment_uid);
2970 modest_window_set_zoom (MODEST_WINDOW (msg_win),
2971 modest_window_get_zoom (MODEST_WINDOW (window)));
2972 if (modest_window_mgr_register_window (mgr, msg_win, MODEST_WINDOW (window)))
2973 gtk_widget_show_all (GTK_WIDGET (msg_win));
2975 gtk_widget_destroy (GTK_WIDGET (msg_win));
2981 g_free (attachment_uid);
2983 g_object_unref (mime_part);
2995 GnomeVFSResult result;
2997 ModestMsgViewWindow *window;
3000 static void save_mime_part_info_free (SaveMimePartInfo *info, gboolean with_struct);
3001 static gboolean idle_save_mime_part_show_result (SaveMimePartInfo *info);
3002 static gpointer save_mime_part_to_file (SaveMimePartInfo *info);
3003 static void save_mime_parts_to_file_with_checks (GtkWindow *parent, SaveMimePartInfo *info);
3006 save_mime_part_info_free (SaveMimePartInfo *info, gboolean with_struct)
3010 for (node = info->pairs; node != NULL; node = g_list_next (node)) {
3011 SaveMimePartPair *pair = (SaveMimePartPair *) node->data;
3012 g_free (pair->filename);
3013 g_object_unref (pair->part);
3014 g_slice_free (SaveMimePartPair, pair);
3016 g_list_free (info->pairs);
3019 g_object_unref (info->window);
3020 info->window = NULL;
3022 g_slice_free (SaveMimePartInfo, info);
3027 idle_save_mime_part_show_result (SaveMimePartInfo *info)
3029 /* This is a GDK lock because we are an idle callback and
3030 * hildon_banner_show_information is or does Gtk+ code */
3032 gdk_threads_enter (); /* CHECKED */
3033 if (info->result == GNOME_VFS_ERROR_CANCELLED) {
3035 } else if (info->result == GNOME_VFS_OK) {
3036 hildon_banner_show_information (NULL, NULL, _CS("sfil_ib_saved"));
3037 } else if (info->result == GNOME_VFS_ERROR_NO_SPACE) {
3040 /* Check if the uri belongs to the external mmc */
3041 if (g_str_has_prefix (info->uri, g_getenv (MODEST_MMC1_VOLUMEPATH_ENV)))
3042 msg = g_strdup_printf (_KR("cerm_device_memory_full"), "");
3044 msg = g_strdup (_KR("cerm_memory_card_full"));
3045 modest_platform_information_banner (NULL, NULL, msg);
3048 hildon_banner_show_information (NULL, NULL, _("mail_ib_file_operation_failed"));
3050 save_mime_part_info_free (info, FALSE);
3051 gdk_threads_leave (); /* CHECKED */
3057 save_mime_part_to_file_connect_handler (gboolean canceled,
3059 GtkWindow *parent_window,
3060 TnyAccount *account,
3061 SaveMimePartInfo *info)
3063 if (canceled || err) {
3064 if (canceled && !err) {
3065 info->result = GNOME_VFS_ERROR_CANCELLED;
3067 g_idle_add ((GSourceFunc) idle_save_mime_part_show_result, info);
3069 g_thread_create ((GThreadFunc)save_mime_part_to_file, info, FALSE, NULL);
3074 save_mime_part_to_file_connect_idle (SaveMimePartInfo *info)
3077 TnyAccount *account;
3078 ModestMsgViewWindowPrivate *priv;
3080 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (info->window);
3082 is_merge = g_str_has_prefix (priv->msg_uid, "merge:");
3085 /* Get the account */
3087 account = tny_account_store_find_account (TNY_ACCOUNT_STORE (modest_runtime_get_account_store ()),
3090 modest_platform_connect_and_perform (GTK_WINDOW (info->window),
3092 TNY_ACCOUNT (account),
3093 (ModestConnectedPerformer) save_mime_part_to_file_connect_handler,
3099 save_mime_part_to_file (SaveMimePartInfo *info)
3101 GnomeVFSHandle *handle;
3103 SaveMimePartPair *pair = (SaveMimePartPair *) info->pairs->data;
3105 if (TNY_IS_CAMEL_BS_MIME_PART (pair->part) &&
3106 !tny_camel_bs_mime_part_is_fetched (TNY_CAMEL_BS_MIME_PART (pair->part))) {
3107 g_idle_add ((GSourceFunc) save_mime_part_to_file_connect_idle, info);
3111 info->result = gnome_vfs_create (&handle, pair->filename, GNOME_VFS_OPEN_WRITE, FALSE, 0644);
3112 if (info->result == GNOME_VFS_OK) {
3113 GError *error = NULL;
3114 gboolean decode_in_provider;
3116 ModestAccountMgr *mgr;
3117 const gchar *account;
3118 ModestProtocol *protocol = NULL;
3120 stream = tny_vfs_stream_new (handle);
3122 decode_in_provider = FALSE;
3123 mgr = modest_runtime_get_account_mgr ();
3124 account = modest_window_get_active_account (MODEST_WINDOW (info->window));
3125 if (modest_account_mgr_account_is_multimailbox (mgr, account, &protocol)) {
3126 if (MODEST_IS_ACCOUNT_PROTOCOL (protocol)) {
3127 decode_in_provider =
3128 modest_account_protocol_decode_part_to_stream (
3129 MODEST_ACCOUNT_PROTOCOL (protocol),
3137 if (!decode_in_provider)
3138 written = tny_mime_part_decode_to_stream (pair->part, stream, &error);
3141 g_warning ("modest: could not save attachment %s: %d (%s)\n", pair->filename, error?error->code:-1, error?error->message:"Unknown error");
3143 if ((error->domain == TNY_ERROR_DOMAIN) &&
3144 (error->code == TNY_IO_ERROR_WRITE) &&
3145 (errno == ENOSPC)) {
3146 info->result = GNOME_VFS_ERROR_NO_SPACE;
3148 info->result = GNOME_VFS_ERROR_IO;
3151 g_object_unref (G_OBJECT (stream));
3153 g_warning ("Could not create save attachment %s: %s\n",
3154 pair->filename, gnome_vfs_result_to_string (info->result));
3157 /* Go on saving remaining files */
3158 info->pairs = g_list_remove_link (info->pairs, info->pairs);
3159 if (info->pairs != NULL) {
3160 save_mime_part_to_file (info);
3162 g_idle_add ((GSourceFunc) idle_save_mime_part_show_result, info);
3169 save_mime_parts_to_file_with_checks (GtkWindow *parent,
3170 SaveMimePartInfo *info)
3172 gboolean is_ok = TRUE;
3173 gint replaced_files = 0;
3174 const GList *files = info->pairs;
3175 const GList *iter, *to_replace = NULL;
3177 for (iter = files; (iter != NULL) && (replaced_files < 2); iter = g_list_next(iter)) {
3178 SaveMimePartPair *pair = iter->data;
3179 gchar *unescaped = g_uri_unescape_string (pair->filename, NULL);
3181 if (modest_utils_file_exists (unescaped)) {
3183 if (replaced_files == 1)
3188 if (replaced_files) {
3191 if (replaced_files == 1) {
3192 SaveMimePartPair *pair = to_replace->data;
3193 const gchar *basename = strrchr (pair->filename, G_DIR_SEPARATOR) + 1;
3194 gchar *escaped_basename, *message;
3196 escaped_basename = g_uri_unescape_string (basename, NULL);
3197 message = g_strdup_printf ("%s\n%s",
3198 _FM("docm_nc_replace_file"),
3199 (escaped_basename) ? escaped_basename : "");
3200 response = modest_platform_run_confirmation_dialog (parent, message);
3202 g_free (escaped_basename);
3204 response = modest_platform_run_confirmation_dialog (parent,
3205 _FM("docm_nc_replace_multiple"));
3207 if (response != GTK_RESPONSE_OK)
3212 save_mime_part_info_free (info, TRUE);
3214 g_thread_create ((GThreadFunc)save_mime_part_to_file, info, FALSE, NULL);
3219 typedef struct _SaveAttachmentsInfo {
3220 TnyList *attachments_list;
3221 ModestMsgViewWindow *window;
3222 } SaveAttachmentsInfo;
3225 save_attachments_response (GtkDialog *dialog,
3229 TnyList *mime_parts;
3231 GList *files_to_save = NULL;
3232 gchar *current_folder;
3233 SaveAttachmentsInfo *sa_info = (SaveAttachmentsInfo *) user_data;
3235 mime_parts = TNY_LIST (sa_info->attachments_list);
3237 if (arg1 != GTK_RESPONSE_OK)
3240 chooser_uri = gtk_file_chooser_get_uri (GTK_FILE_CHOOSER (dialog));
3241 current_folder = gtk_file_chooser_get_current_folder_uri (GTK_FILE_CHOOSER (dialog));
3242 if (current_folder && *current_folder != '\0') {
3244 modest_conf_set_string (modest_runtime_get_conf (), MODEST_CONF_LATEST_SAVE_ATTACHMENT_PATH,
3245 current_folder,&err);
3247 g_debug ("Error storing latest used folder: %s", err->message);
3251 g_free (current_folder);
3253 if (!modest_utils_folder_writable (chooser_uri)) {
3254 const gchar *err_msg;
3256 #ifdef MODEST_PLATFORM_MAEMO
3257 if (modest_maemo_utils_in_usb_mode ()) {
3258 err_msg = dgettext ("hildon-status-bar-usb", "usbh_ib_mmc_usb_connected");
3260 err_msg = _FM("sfil_ib_readonly_location");
3263 err_msg = _FM("sfil_ib_readonly_location");
3265 hildon_banner_show_information (NULL, NULL, err_msg);
3269 iter = tny_list_create_iterator (mime_parts);
3270 while (!tny_iterator_is_done (iter)) {
3271 TnyMimePart *mime_part = (TnyMimePart *) tny_iterator_get_current (iter);
3273 if ((modest_tny_mime_part_is_attachment_for_modest (mime_part)) &&
3274 !tny_mime_part_is_purged (mime_part) &&
3275 (tny_mime_part_get_filename (mime_part) != NULL)) {
3276 SaveMimePartPair *pair;
3278 pair = g_slice_new0 (SaveMimePartPair);
3280 if (tny_list_get_length (mime_parts) > 1) {
3282 gnome_vfs_escape_slashes (tny_mime_part_get_filename (mime_part));
3283 pair->filename = g_build_filename (chooser_uri, escaped, NULL);
3286 pair->filename = g_strdup (chooser_uri);
3288 pair->part = mime_part;
3289 files_to_save = g_list_prepend (files_to_save, pair);
3291 tny_iterator_next (iter);
3293 g_object_unref (iter);
3296 if (files_to_save != NULL) {
3297 SaveMimePartInfo *info = g_slice_new0 (SaveMimePartInfo);
3298 info->pairs = files_to_save;
3299 info->result = TRUE;
3300 info->uri = g_strdup (chooser_uri);
3301 info->window = g_object_ref (sa_info->window);
3302 save_mime_parts_to_file_with_checks ((GtkWindow *) dialog, info);
3304 g_free (chooser_uri);
3307 /* Free and close the dialog */
3308 g_object_unref (mime_parts);
3309 g_object_unref (sa_info->window);
3310 g_slice_free (SaveAttachmentsInfo, sa_info);
3311 gtk_widget_destroy (GTK_WIDGET (dialog));
3315 modest_msg_view_window_save_attachments (ModestMsgViewWindow *window,
3316 TnyList *mime_parts)
3318 ModestMsgViewWindowPrivate *priv;
3319 GtkWidget *save_dialog = NULL;
3320 gchar *conf_folder = NULL;
3321 gchar *filename = NULL;
3322 gchar *save_multiple_str = NULL;
3323 const gchar *root_folder = "file:///";
3325 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window));
3326 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
3328 if (mime_parts == NULL) {
3329 /* In Hildon 2.2 save and delete operate over all the attachments as there's no
3330 * selection available */
3331 mime_parts = modest_msg_view_get_attachments (MODEST_MSG_VIEW (priv->msg_view));
3332 if (mime_parts && !modest_maemo_utils_select_attachments (GTK_WINDOW (window), mime_parts, FALSE)) {
3333 g_object_unref (mime_parts);
3336 if (mime_parts == NULL || tny_list_get_length (mime_parts) == 0) {
3338 g_object_unref (mime_parts);
3344 g_object_ref (mime_parts);
3347 /* prepare dialog */
3348 if (tny_list_get_length (mime_parts) == 1) {
3350 /* only one attachment selected */
3351 iter = tny_list_create_iterator (mime_parts);
3352 TnyMimePart *mime_part = (TnyMimePart *) tny_iterator_get_current (iter);
3353 g_object_unref (iter);
3354 if (!modest_tny_mime_part_is_msg (mime_part) &&
3355 modest_tny_mime_part_is_attachment_for_modest (mime_part) &&
3356 !tny_mime_part_is_purged (mime_part)) {
3357 filename = g_strdup (tny_mime_part_get_filename (mime_part));
3359 /* TODO: show any error? */
3360 g_warning ("%s: Tried to save a non-file attachment", __FUNCTION__);
3361 g_object_unref (mime_parts);
3364 g_object_unref (mime_part);
3366 gint num = tny_list_get_length (mime_parts);
3367 save_multiple_str = g_strdup_printf (dngettext("hildon-fm",
3368 "sfil_va_number_of_objects_attachment",
3369 "sfil_va_number_of_objects_attachments",
3373 save_dialog = hildon_file_chooser_dialog_new (GTK_WINDOW (window),
3374 GTK_FILE_CHOOSER_ACTION_SAVE);
3376 /* Get last used folder */
3377 conf_folder = modest_conf_get_string (modest_runtime_get_conf (),
3378 MODEST_CONF_LATEST_SAVE_ATTACHMENT_PATH, NULL);
3380 /* File chooser stops working if we select "file:///" as current folder */
3381 if (conf_folder && g_ascii_strcasecmp (root_folder, conf_folder) != 0) {
3382 g_free (conf_folder);
3386 if (conf_folder && conf_folder[0] != '\0') {
3387 gtk_file_chooser_set_current_folder_uri (GTK_FILE_CHOOSER (save_dialog), conf_folder);
3390 /* Set the default folder to documents folder */
3391 docs_folder = (gchar *) g_strdup(g_get_user_special_dir (G_USER_DIRECTORY_DOCUMENTS));
3394 docs_folder = g_build_filename (g_getenv (MYDOCS_ENV), DOCS_FOLDER, NULL);
3396 gtk_file_chooser_set_current_folder (GTK_FILE_CHOOSER (save_dialog), docs_folder);
3397 g_free (docs_folder);
3399 g_free (conf_folder);
3403 gtk_file_chooser_set_current_name (GTK_FILE_CHOOSER (save_dialog),
3408 /* if multiple, set multiple string */
3409 if (save_multiple_str) {
3410 g_object_set (G_OBJECT (save_dialog), "save-multiple", save_multiple_str, NULL);
3411 gtk_window_set_title (GTK_WINDOW (save_dialog), _FM("sfil_ti_save_objects_files"));
3412 g_free (save_multiple_str);
3415 /* We must run this asynchronously, because the hildon dialog
3416 performs a gtk_dialog_run by itself which leads to gdk
3418 SaveAttachmentsInfo *sa_info;
3419 sa_info = g_slice_new (SaveAttachmentsInfo);
3420 sa_info->attachments_list = mime_parts;
3421 sa_info->window = g_object_ref (window);
3422 g_signal_connect (save_dialog, "response",
3423 G_CALLBACK (save_attachments_response), sa_info);
3425 gtk_widget_show_all (save_dialog);
3429 show_remove_attachment_information (gpointer userdata)
3431 ModestMsgViewWindow *window = (ModestMsgViewWindow *) userdata;
3432 ModestMsgViewWindowPrivate *priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
3434 /* We're outside the main lock */
3435 gdk_threads_enter ();
3437 if (priv->remove_attachment_banner != NULL) {
3438 gtk_widget_destroy (priv->remove_attachment_banner);
3439 g_object_unref (priv->remove_attachment_banner);
3442 priv->remove_attachment_banner = g_object_ref (
3443 hildon_banner_show_animation (NULL, NULL, _("mcen_me_inbox_remove_attachments")));
3445 gdk_threads_leave ();
3451 modest_msg_view_window_remove_attachments (ModestMsgViewWindow *window, gboolean get_all)
3453 ModestMsgViewWindowPrivate *priv;
3454 TnyList *mime_parts = NULL, *tmp;
3455 gchar *confirmation_message;
3461 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window));
3462 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
3464 /* In hildon 2.2 we ignore the get_all flag as we always get all attachments. This is
3465 * because we don't have selection
3467 mime_parts = modest_msg_view_get_attachments (MODEST_MSG_VIEW (priv->msg_view));
3469 /* Remove already purged messages from mime parts list. We use
3470 a copy of the list to remove items in the original one */
3471 tmp = tny_list_copy (mime_parts);
3472 iter = tny_list_create_iterator (tmp);
3473 while (!tny_iterator_is_done (iter)) {
3474 TnyMimePart *part = TNY_MIME_PART (tny_iterator_get_current (iter));
3475 if (tny_mime_part_is_purged (part))
3476 tny_list_remove (mime_parts, (GObject *) part);
3478 g_object_unref (part);
3479 tny_iterator_next (iter);
3481 g_object_unref (tmp);
3482 g_object_unref (iter);
3484 if (!modest_maemo_utils_select_attachments (GTK_WINDOW (window), mime_parts, TRUE) ||
3485 tny_list_get_length (mime_parts) == 0) {
3486 g_object_unref (mime_parts);
3490 n_attachments = tny_list_get_length (mime_parts);
3491 if (n_attachments == 1) {
3495 iter = tny_list_create_iterator (mime_parts);
3496 part = (TnyMimePart *) tny_iterator_get_current (iter);
3497 g_object_unref (iter);
3498 if (modest_tny_mime_part_is_msg (part)) {
3500 header = tny_msg_get_header (TNY_MSG (part));
3501 filename = tny_header_dup_subject (header);
3502 g_object_unref (header);
3503 if (filename == NULL)
3504 filename = g_strdup (_("mail_va_no_subject"));
3506 filename = g_strdup (tny_mime_part_get_filename (TNY_MIME_PART (part)));
3508 confirmation_message = g_strdup_printf (_("mcen_nc_purge_file_text"), filename);
3510 g_object_unref (part);
3512 confirmation_message = g_strdup_printf (ngettext("mcen_nc_purge_file_text",
3513 "mcen_nc_purge_files_text",
3514 n_attachments), n_attachments);
3516 response = modest_platform_run_confirmation_dialog (GTK_WINDOW (window),
3517 confirmation_message);
3518 g_free (confirmation_message);
3520 if (response != GTK_RESPONSE_OK) {
3521 g_object_unref (mime_parts);
3525 priv->purge_timeout = g_timeout_add (2000, show_remove_attachment_information, window);
3527 iter = tny_list_create_iterator (mime_parts);
3528 while (!tny_iterator_is_done (iter)) {
3531 part = (TnyMimePart *) tny_iterator_get_current (iter);
3532 tny_mime_part_set_purged (TNY_MIME_PART (part));
3533 g_object_unref (part);
3534 tny_iterator_next (iter);
3536 g_object_unref (iter);
3538 msg = tny_msg_view_get_msg (TNY_MSG_VIEW (priv->msg_view));
3539 tny_msg_view_clear (TNY_MSG_VIEW (priv->msg_view));
3540 tny_msg_rewrite_cache (msg);
3541 tny_msg_view_set_msg (TNY_MSG_VIEW (priv->msg_view), msg);
3542 g_object_unref (msg);
3543 update_branding (MODEST_MSG_VIEW_WINDOW (window));
3545 g_object_unref (mime_parts);
3547 if (priv->purge_timeout > 0) {
3548 g_source_remove (priv->purge_timeout);
3549 priv->purge_timeout = 0;
3552 if (priv->remove_attachment_banner) {
3553 gtk_widget_destroy (priv->remove_attachment_banner);
3554 g_object_unref (priv->remove_attachment_banner);
3555 priv->remove_attachment_banner = NULL;
3561 update_window_title (ModestMsgViewWindow *window)
3563 ModestMsgViewWindowPrivate *priv;
3565 TnyHeader *header = NULL;
3566 gchar *subject = NULL;
3568 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
3570 /* Note that if the window is closed while we're retrieving
3571 the message, this widget could de deleted */
3572 if (!priv->msg_view)
3575 msg = tny_msg_view_get_msg (TNY_MSG_VIEW (priv->msg_view));
3577 if (priv->other_body) {
3580 description = modest_tny_mime_part_get_header_value (priv->other_body, "Content-Description");
3582 g_strstrip (description);
3583 subject = description;
3585 } else if (msg != NULL) {
3586 header = tny_msg_get_header (msg);
3587 subject = tny_header_dup_subject (header);
3588 g_object_unref (header);
3589 g_object_unref (msg);
3592 if ((subject == NULL)||(subject[0] == '\0')) {
3594 subject = g_strdup (_("mail_va_no_subject"));
3597 gtk_window_set_title (GTK_WINDOW (window), subject);
3602 on_move_focus (GtkWidget *widget,
3603 GtkDirectionType direction,
3606 g_signal_stop_emission_by_name (G_OBJECT (widget), "move-focus");
3610 fetch_image_open_stream (TnyStreamCache *self, gint64 *expected_size, gchar *uri)
3612 GnomeVFSResult result;
3613 GnomeVFSHandle *handle = NULL;
3614 GnomeVFSFileInfo *info = NULL;
3617 result = gnome_vfs_open (&handle, uri, GNOME_VFS_OPEN_READ);
3618 if (result != GNOME_VFS_OK) {
3623 info = gnome_vfs_file_info_new ();
3624 result = gnome_vfs_get_file_info_from_handle (handle, info, GNOME_VFS_FILE_INFO_DEFAULT);
3625 if (result != GNOME_VFS_OK || ! (info->valid_fields & GNOME_VFS_FILE_INFO_FIELDS_SIZE)) {
3626 /* We put a "safe" default size for going to cache */
3627 *expected_size = (300*1024);
3629 *expected_size = info->size;
3631 gnome_vfs_file_info_unref (info);
3633 stream = tny_vfs_stream_new (handle);
3642 TnyStream *output_stream;
3643 GtkWidget *msg_view;
3648 on_fetch_image_timeout_refresh_view (gpointer userdata)
3650 ModestMsgViewWindowPrivate *priv;
3652 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (userdata);
3653 update_progress_hint (MODEST_MSG_VIEW_WINDOW (userdata));
3654 if (GTK_WIDGET_DRAWABLE (priv->msg_view)) {
3655 gtk_widget_queue_draw (GTK_WIDGET (priv->msg_view));
3657 priv->fetch_image_redraw_handler = 0;
3658 g_object_unref (userdata);
3663 on_fetch_image_idle_refresh_view (gpointer userdata)
3666 FetchImageData *fidata = (FetchImageData *) userdata;
3668 gdk_threads_enter ();
3669 if (GTK_WIDGET_DRAWABLE (fidata->msg_view)) {
3670 ModestMsgViewWindowPrivate *priv;
3672 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (fidata->window);
3673 priv->fetching_images--;
3674 if (priv->fetch_image_redraw_handler == 0) {
3675 priv->fetch_image_redraw_handler = g_timeout_add (500, on_fetch_image_timeout_refresh_view, g_object_ref (fidata->window));
3679 gdk_threads_leave ();
3681 g_object_unref (fidata->msg_view);
3682 g_object_unref (fidata->window);
3683 g_slice_free (FetchImageData, fidata);
3688 on_fetch_image_thread (gpointer userdata)
3690 FetchImageData *fidata = (FetchImageData *) userdata;
3691 TnyStreamCache *cache;
3692 TnyStream *cache_stream;
3694 cache = modest_runtime_get_images_cache ();
3696 tny_stream_cache_get_stream (cache,
3698 (TnyStreamCacheOpenStreamFetcher) fetch_image_open_stream,
3699 (gpointer) fidata->uri);
3700 g_free (fidata->cache_id);
3701 g_free (fidata->uri);
3703 if (cache_stream != NULL) {
3706 while (G_LIKELY (!tny_stream_is_eos (cache_stream))) {
3709 nb_read = tny_stream_read (cache_stream, buffer, sizeof (buffer));
3710 if (G_UNLIKELY (nb_read < 0)) {
3712 } else if (G_LIKELY (nb_read > 0)) {
3713 gssize nb_written = 0;
3715 while (G_UNLIKELY (nb_written < nb_read)) {
3718 len = tny_stream_write (fidata->output_stream, buffer + nb_written,
3719 nb_read - nb_written);
3720 if (G_UNLIKELY (len < 0))
3726 tny_stream_close (cache_stream);
3727 g_object_unref (cache_stream);
3730 tny_stream_close (fidata->output_stream);
3731 g_object_unref (fidata->output_stream);
3733 g_idle_add (on_fetch_image_idle_refresh_view, fidata);
3739 on_fetch_image (ModestMsgView *msgview,
3742 ModestMsgViewWindow *window)
3744 const gchar *current_account;
3745 ModestMsgViewWindowPrivate *priv;
3746 FetchImageData *fidata;
3748 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
3750 current_account = modest_window_get_active_account (MODEST_WINDOW (window));
3752 fidata = g_slice_new0 (FetchImageData);
3753 fidata->msg_view = g_object_ref (msgview);
3754 fidata->window = g_object_ref (window);
3755 fidata->uri = g_strdup (uri);
3756 fidata->cache_id = modest_images_cache_get_id (current_account, uri);
3757 fidata->output_stream = g_object_ref (stream);
3759 priv->fetching_images++;
3760 if (g_thread_create (on_fetch_image_thread, fidata, FALSE, NULL) == NULL) {
3761 g_object_unref (fidata->output_stream);
3762 g_free (fidata->cache_id);
3763 g_free (fidata->uri);
3764 g_object_unref (fidata->msg_view);
3765 g_slice_free (FetchImageData, fidata);
3766 tny_stream_close (stream);
3767 priv->fetching_images--;
3768 update_progress_hint (window);
3771 update_progress_hint (window);
3777 setup_menu (ModestMsgViewWindow *self)
3779 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW(self));
3781 /* Settings menu buttons */
3782 modest_hildon2_window_add_to_menu (MODEST_HILDON2_WINDOW (self), _("mcen_me_viewer_find"), NULL,
3783 APP_MENU_CALLBACK (modest_msg_view_window_show_find_toolbar),
3784 MODEST_DIMMING_CALLBACK (modest_ui_dimming_rules_on_find_in_msg));
3786 modest_hildon2_window_add_to_menu (MODEST_HILDON2_WINDOW (self),
3787 dngettext(GETTEXT_PACKAGE,
3788 "mcen_me_move_message",
3789 "mcen_me_move_messages",
3792 APP_MENU_CALLBACK (modest_ui_actions_on_move_to),
3793 MODEST_DIMMING_CALLBACK (modest_ui_dimming_rules_on_move_to));
3795 modest_hildon2_window_add_to_menu (MODEST_HILDON2_WINDOW (self), _("mcen_me_inbox_mark_as_read"), NULL,
3796 APP_MENU_CALLBACK (modest_ui_actions_on_mark_as_read),
3797 MODEST_DIMMING_CALLBACK (modest_ui_dimming_rules_on_mark_as_read_msg_in_view));
3799 modest_hildon2_window_add_to_menu (MODEST_HILDON2_WINDOW (self), _("mcen_me_inbox_mark_as_unread"), NULL,
3800 APP_MENU_CALLBACK (modest_ui_actions_on_mark_as_unread),
3801 MODEST_DIMMING_CALLBACK (modest_ui_dimming_rules_on_mark_as_unread_msg_in_view));
3803 modest_hildon2_window_add_to_menu (MODEST_HILDON2_WINDOW (self), _("mcen_me_viewer_save_attachments"), NULL,
3804 APP_MENU_CALLBACK (modest_ui_actions_save_attachments),
3805 MODEST_DIMMING_CALLBACK (modest_ui_dimming_rules_on_save_attachments));
3806 modest_hildon2_window_add_to_menu (MODEST_HILDON2_WINDOW (self), _("mcen_me_inbox_remove_attachments"), NULL,
3807 APP_MENU_CALLBACK (modest_ui_actions_remove_attachments),
3808 MODEST_DIMMING_CALLBACK (modest_ui_dimming_rules_on_remove_attachments));
3810 modest_hildon2_window_add_to_menu (MODEST_HILDON2_WINDOW (self), _("mcen_me_new_message"), "<Control>n",
3811 APP_MENU_CALLBACK (modest_ui_actions_on_new_msg),
3812 MODEST_DIMMING_CALLBACK (modest_ui_dimming_rules_on_new_msg));
3813 modest_hildon2_window_add_to_menu (MODEST_HILDON2_WINDOW (self), _("mcen_me_viewer_addtocontacts"), NULL,
3814 APP_MENU_CALLBACK (modest_ui_actions_add_to_contacts),
3815 MODEST_DIMMING_CALLBACK (modest_ui_dimming_rules_on_add_to_contacts));
3817 modest_hildon2_window_add_to_menu (MODEST_HILDON2_WINDOW (self), _("mcen_ti_message_properties"), NULL,
3818 APP_MENU_CALLBACK (modest_ui_actions_on_details),
3819 MODEST_DIMMING_CALLBACK (modest_ui_dimming_rules_on_details));
3823 modest_msg_view_window_add_to_contacts (ModestMsgViewWindow *self)
3825 ModestMsgViewWindowPrivate *priv;
3826 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
3827 GSList *recipients = NULL;
3830 msg = tny_msg_view_get_msg (TNY_MSG_VIEW (priv->msg_view));
3834 header = modest_msg_view_window_get_header (self);
3837 recipients = modest_tny_msg_header_get_all_recipients_list (header);
3838 g_object_unref (header);
3840 recipients = modest_tny_msg_get_all_recipients_list (msg);
3841 g_object_unref (msg);
3845 /* Offer the user to add recipients to the address book */
3846 modest_address_book_add_address_list_with_selector (recipients, (GtkWindow *) self);
3847 g_slist_foreach (recipients, (GFunc) g_free, NULL); g_slist_free (recipients);
3852 _modest_msg_view_window_map_event (GtkWidget *widget,
3856 ModestMsgViewWindow *self = (ModestMsgViewWindow *) userdata;
3858 update_progress_hint (self);
3864 modest_msg_view_window_fetch_images (ModestMsgViewWindow *self)
3866 ModestMsgViewWindowPrivate *priv;
3867 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
3869 modest_msg_view_request_fetch_images (MODEST_MSG_VIEW (priv->msg_view));
3873 modest_msg_view_window_has_blocked_external_images (ModestMsgViewWindow *self)
3875 ModestMsgViewWindowPrivate *priv;
3876 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
3878 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self), FALSE);
3880 return modest_msg_view_has_blocked_external_images (MODEST_MSG_VIEW (priv->msg_view));
3884 modest_msg_view_window_reload (ModestMsgViewWindow *self)
3886 ModestMsgViewWindowPrivate *priv;
3887 const gchar *msg_uid;
3888 TnyHeader *header = NULL;
3889 TnyFolder *folder = NULL;
3891 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self));
3893 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
3895 header = modest_msg_view_window_get_header (MODEST_MSG_VIEW_WINDOW (self));
3899 folder = tny_header_get_folder (header);
3900 g_object_unref (header);
3905 msg_uid = modest_msg_view_window_get_message_uid (self);
3907 GtkTreeRowReference *row_reference;
3909 if (priv->row_reference && gtk_tree_row_reference_valid (priv->row_reference)) {
3910 row_reference = priv->row_reference;
3912 row_reference = NULL;
3914 if (!message_reader (self, priv, NULL, msg_uid, folder, row_reference))
3915 g_warning ("Shouldn't happen, trying to reload a message failed");
3918 g_object_unref (folder);
3922 update_branding (ModestMsgViewWindow *self)
3924 const gchar *account;
3925 const gchar *mailbox;
3926 ModestAccountMgr *mgr;
3927 ModestProtocol *protocol = NULL;
3928 gchar *service_name = NULL;
3929 const GdkPixbuf *service_icon = NULL;
3930 ModestMsgViewWindowPrivate *priv;
3932 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
3934 account = modest_window_get_active_account (MODEST_WINDOW (self));
3935 mailbox = modest_window_get_active_mailbox (MODEST_WINDOW (self));
3937 mgr = modest_runtime_get_account_mgr ();
3939 if (modest_account_mgr_account_is_multimailbox (mgr, account, &protocol)) {
3940 if (MODEST_IS_ACCOUNT_PROTOCOL (protocol)) {
3941 service_name = modest_account_protocol_get_service_name (MODEST_ACCOUNT_PROTOCOL (protocol),
3943 service_icon = modest_account_protocol_get_service_icon (MODEST_ACCOUNT_PROTOCOL (protocol),
3944 account, mailbox, MODEST_ICON_SIZE_SMALL);
3948 modest_msg_view_set_branding (MODEST_MSG_VIEW (priv->msg_view), service_name, service_icon);
3949 g_free (service_name);
3953 sync_flags (ModestMsgViewWindow *self)
3955 TnyHeader *header = NULL;
3957 header = modest_msg_view_window_get_header (self);
3959 TnyMsg *msg = modest_msg_view_window_get_message (self);
3961 header = tny_msg_get_header (msg);
3962 g_object_unref (msg);
3967 TnyFolder *folder = tny_header_get_folder (header);
3970 ModestMailOperation *mail_op;
3972 /* Sync folder, we need this to save the seen flag */
3973 mail_op = modest_mail_operation_new (NULL);
3974 modest_mail_operation_queue_add (modest_runtime_get_mail_operation_queue (),
3976 modest_mail_operation_sync_folder (mail_op, folder, FALSE, NULL, NULL);
3977 g_object_unref (mail_op);
3978 g_object_unref (folder);
3980 g_object_unref (header);