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 "modest-marshal.h"
37 #include "modest-platform.h"
38 #include <modest-utils.h>
39 #include <modest-maemo-utils.h>
40 #include <modest-tny-msg.h>
41 #include <modest-msg-view-window.h>
42 #include <modest-main-window-ui.h>
43 #include "modest-msg-view-window-ui-dimming.h"
44 #include <modest-widget-memory.h>
45 #include <modest-progress-object.h>
46 #include <modest-runtime.h>
47 #include <modest-window-priv.h>
48 #include <modest-tny-folder.h>
49 #include <modest-text-utils.h>
50 #include <modest-account-mgr-helpers.h>
51 #include <hildon/hildon-pannable-area.h>
52 #include <hildon/hildon-picker-dialog.h>
53 #include <hildon/hildon-app-menu.h>
54 #include "modest-defs.h"
55 #include "modest-hildon-includes.h"
56 #include "modest-ui-dimming-manager.h"
57 #include <gdk/gdkkeysyms.h>
58 #include <modest-tny-account.h>
59 #include <modest-mime-part-view.h>
60 #include <modest-isearch-view.h>
61 #include <modest-tny-mime-part.h>
62 #include <modest-address-book.h>
65 #include <glib/gstdio.h>
66 #include <modest-debug.h>
67 #include <modest-header-window.h>
69 #define MYDOCS_ENV "MYDOCSDIR"
70 #define DOCS_FOLDER ".documents"
72 typedef struct _ModestMsgViewWindowPrivate ModestMsgViewWindowPrivate;
73 struct _ModestMsgViewWindowPrivate {
76 GtkWidget *main_scroll;
77 GtkWidget *find_toolbar;
80 /* Progress observers */
81 GSList *progress_widgets;
84 GtkWidget *prev_toolitem;
85 GtkWidget *next_toolitem;
86 gboolean progress_hint;
88 /* Optimized view enabled */
89 gboolean optimized_view;
91 /* Whether this was created via the *_new_for_search_result() function. */
92 gboolean is_search_result;
94 /* Whether the message is in outbox */
97 /* A reference to the @model of the header view
98 * to allow selecting previous/next messages,
99 * if the message is currently selected in the header view.
101 const gchar *header_folder_id;
102 GtkTreeModel *header_model;
103 GtkTreeRowReference *row_reference;
104 GtkTreeRowReference *next_row_reference;
106 gulong clipboard_change_handler;
107 gulong queue_change_handler;
108 gulong account_removed_handler;
109 gulong row_changed_handler;
110 gulong row_deleted_handler;
111 gulong row_inserted_handler;
112 gulong rows_reordered_handler;
115 GtkWidget *remove_attachment_banner;
122 static void modest_msg_view_window_class_init (ModestMsgViewWindowClass *klass);
123 static void modest_msg_view_window_init (ModestMsgViewWindow *obj);
124 static void modest_header_view_observer_init(
125 ModestHeaderViewObserverIface *iface_class);
126 static void modest_msg_view_window_finalize (GObject *obj);
127 static void modest_msg_view_window_toggle_find_toolbar (GtkToggleAction *obj,
129 static void modest_msg_view_window_find_toolbar_close (GtkWidget *widget,
130 ModestMsgViewWindow *obj);
131 static void modest_msg_view_window_find_toolbar_search (GtkWidget *widget,
132 ModestMsgViewWindow *obj);
134 static void modest_msg_view_window_disconnect_signals (ModestWindow *self);
136 static gdouble modest_msg_view_window_get_zoom (ModestWindow *window);
137 static gboolean modest_msg_view_window_key_event (GtkWidget *window,
140 static gboolean modest_msg_view_window_toggle_menu (HildonWindow *window,
143 static void modest_msg_view_window_update_priority (ModestMsgViewWindow *window);
145 static void modest_msg_view_window_show_toolbar (ModestWindow *window,
146 gboolean show_toolbar);
148 static void modest_msg_view_window_clipboard_owner_change (GtkClipboard *clipboard,
150 ModestMsgViewWindow *window);
152 static void modest_msg_view_window_on_row_changed (GtkTreeModel *header_model,
155 ModestMsgViewWindow *window);
157 static void modest_msg_view_window_on_row_deleted (GtkTreeModel *header_model,
159 ModestMsgViewWindow *window);
161 static void modest_msg_view_window_on_row_inserted (GtkTreeModel *header_model,
162 GtkTreePath *tree_path,
163 GtkTreeIter *tree_iter,
164 ModestMsgViewWindow *window);
166 static void modest_msg_view_window_on_row_reordered (GtkTreeModel *header_model,
170 ModestMsgViewWindow *window);
172 static void modest_msg_view_window_update_model_replaced (ModestHeaderViewObserver *window,
174 const gchar *tny_folder_id);
176 static void on_queue_changed (ModestMailOperationQueue *queue,
177 ModestMailOperation *mail_op,
178 ModestMailOperationQueueNotification type,
179 ModestMsgViewWindow *self);
181 static void on_account_removed (TnyAccountStore *account_store,
185 static void on_move_focus (GtkWidget *widget,
186 GtkDirectionType direction,
189 static void view_msg_cb (ModestMailOperation *mail_op,
196 static void set_progress_hint (ModestMsgViewWindow *self,
199 static void update_window_title (ModestMsgViewWindow *window);
201 static gboolean set_toolbar_transfer_mode (ModestMsgViewWindow *self);
202 static void init_window (ModestMsgViewWindow *obj);
204 static gboolean msg_is_visible (TnyHeader *header, gboolean check_outbox);
206 static void check_dimming_rules_after_change (ModestMsgViewWindow *window);
208 static gboolean on_fetch_image (ModestMsgView *msgview,
211 ModestMsgViewWindow *window);
213 static gboolean modest_msg_view_window_scroll_child (ModestMsgViewWindow *self,
214 GtkScrollType scroll_type,
217 static gboolean message_reader (ModestMsgViewWindow *window,
218 ModestMsgViewWindowPrivate *priv,
220 GtkTreeRowReference *row_reference);
222 static void add_to_menu (ModestMsgViewWindow *self,
226 ModestDimmingRulesGroup *group,
227 GCallback dimming_callback);
228 static void setup_menu (ModestMsgViewWindow *self,
229 ModestDimmingRulesGroup *group);
231 /* list my signals */
238 static const GtkToggleActionEntry msg_view_toggle_action_entries [] = {
239 { "FindInMessage", MODEST_TOOLBAR_ICON_FIND, N_("qgn_toolb_gene_find"), NULL, NULL, G_CALLBACK (modest_msg_view_window_toggle_find_toolbar), FALSE },
242 typedef struct _ModestMsgViewWindowPrivate ModestMsgViewWindowPrivate;
243 struct _ModestMsgViewWindowPrivate {
246 GtkWidget *main_scroll;
247 GtkWidget *find_toolbar;
250 /* Progress observers */
251 GtkWidget *progress_bar;
252 GSList *progress_widgets;
255 GtkWidget *progress_toolitem;
256 GtkWidget *cancel_toolitem;
257 GtkWidget *prev_toolitem;
258 GtkWidget *next_toolitem;
259 ModestToolBarModes current_toolbar_mode;
261 /* Optimized view enabled */
262 gboolean optimized_view;
264 /* Whether this was created via the *_new_for_search_result() function. */
265 gboolean is_search_result;
267 /* Whether the message is in outbox */
270 /* A reference to the @model of the header view
271 * to allow selecting previous/next messages,
272 * if the message is currently selected in the header view.
274 const gchar *header_folder_id;
275 GtkTreeModel *header_model;
276 GtkTreeRowReference *row_reference;
277 GtkTreeRowReference *next_row_reference;
279 gulong clipboard_change_handler;
280 gulong queue_change_handler;
281 gulong account_removed_handler;
282 gulong row_changed_handler;
283 gulong row_deleted_handler;
284 gulong row_inserted_handler;
285 gulong rows_reordered_handler;
288 GtkWidget *remove_attachment_banner;
290 guint progress_bar_timeout;
295 >>>>>>> fe21497... * Fixes NB#92694, migrated the viewer menus to 1.5 specs version:src/hildon2/modest-msg-view-window.c
298 #define MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE((o), \
299 MODEST_TYPE_MSG_VIEW_WINDOW, \
300 ModestMsgViewWindowPrivate))
302 static GtkWindowClass *parent_class = NULL;
304 /* uncomment the following if you have defined any signals */
305 static guint signals[LAST_SIGNAL] = {0};
308 modest_msg_view_window_get_type (void)
310 static GType my_type = 0;
312 static const GTypeInfo my_info = {
313 sizeof(ModestMsgViewWindowClass),
314 NULL, /* base init */
315 NULL, /* base finalize */
316 (GClassInitFunc) modest_msg_view_window_class_init,
317 NULL, /* class finalize */
318 NULL, /* class data */
319 sizeof(ModestMsgViewWindow),
321 (GInstanceInitFunc) modest_msg_view_window_init,
324 my_type = g_type_register_static (MODEST_TYPE_WINDOW,
325 "ModestMsgViewWindow",
328 static const GInterfaceInfo modest_header_view_observer_info =
330 (GInterfaceInitFunc) modest_header_view_observer_init,
331 NULL, /* interface_finalize */
332 NULL /* interface_data */
335 g_type_add_interface_static (my_type,
336 MODEST_TYPE_HEADER_VIEW_OBSERVER,
337 &modest_header_view_observer_info);
343 save_state (ModestWindow *self)
345 modest_widget_memory_save (modest_runtime_get_conf (),
347 MODEST_CONF_MSG_VIEW_WINDOW_KEY);
350 <<<<<<< HEAD:src/hildon2/modest-msg-view-window.c
353 restore_settings (ModestMsgViewWindow *self)
357 conf = modest_runtime_get_conf ();
358 modest_widget_memory_restore (conf,
360 MODEST_CONF_MSG_VIEW_WINDOW_KEY);
364 gboolean modest_msg_view_window_scroll_child (ModestMsgViewWindow *self,
365 GtkScrollType scroll_type,
369 ModestMsgViewWindowPrivate *priv;
370 gboolean return_value;
372 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(self);
373 g_signal_emit_by_name (priv->main_scroll, "scroll-child", scroll_type, horizontal, &return_value);
378 add_scroll_binding (GtkBindingSet *binding_set,
380 GtkScrollType scroll)
382 guint keypad_keyval = keyval - GDK_Left + GDK_KP_Left;
384 gtk_binding_entry_add_signal (binding_set, keyval, 0,
386 GTK_TYPE_SCROLL_TYPE, scroll,
387 G_TYPE_BOOLEAN, FALSE);
388 gtk_binding_entry_add_signal (binding_set, keypad_keyval, 0,
390 GTK_TYPE_SCROLL_TYPE, scroll,
391 G_TYPE_BOOLEAN, FALSE);
395 modest_msg_view_window_class_init (ModestMsgViewWindowClass *klass)
397 GObjectClass *gobject_class;
398 HildonWindowClass *hildon_window_class;
399 ModestWindowClass *modest_window_class;
400 GtkBindingSet *binding_set;
402 gobject_class = (GObjectClass*) klass;
403 hildon_window_class = (HildonWindowClass *) klass;
404 modest_window_class = (ModestWindowClass *) klass;
406 parent_class = g_type_class_peek_parent (klass);
407 gobject_class->finalize = modest_msg_view_window_finalize;
409 hildon_window_class->toggle_menu = modest_msg_view_window_toggle_menu;
411 modest_window_class->set_zoom_func = modest_msg_view_window_set_zoom;
412 modest_window_class->get_zoom_func = modest_msg_view_window_get_zoom;
413 modest_window_class->show_toolbar_func = modest_msg_view_window_show_toolbar;
414 modest_window_class->disconnect_signals_func = modest_msg_view_window_disconnect_signals;
416 modest_window_class->save_state_func = save_state;
418 klass->scroll_child = modest_msg_view_window_scroll_child;
420 signals[MSG_CHANGED_SIGNAL] =
421 g_signal_new ("msg-changed",
422 G_TYPE_FROM_CLASS (gobject_class),
424 G_STRUCT_OFFSET (ModestMsgViewWindowClass, msg_changed),
426 modest_marshal_VOID__POINTER_POINTER,
427 G_TYPE_NONE, 2, G_TYPE_POINTER, G_TYPE_POINTER);
429 signals[SCROLL_CHILD_SIGNAL] =
430 g_signal_new ("scroll-child",
431 G_TYPE_FROM_CLASS (gobject_class),
432 G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
433 G_STRUCT_OFFSET (ModestMsgViewWindowClass, scroll_child),
435 modest_marshal_BOOLEAN__ENUM_BOOLEAN,
436 G_TYPE_BOOLEAN, 2, GTK_TYPE_SCROLL_TYPE, G_TYPE_BOOLEAN);
438 binding_set = gtk_binding_set_by_class (klass);
439 add_scroll_binding (binding_set, GDK_Up, GTK_SCROLL_STEP_UP);
440 add_scroll_binding (binding_set, GDK_Down, GTK_SCROLL_STEP_DOWN);
441 add_scroll_binding (binding_set, GDK_Page_Up, GTK_SCROLL_PAGE_UP);
442 add_scroll_binding (binding_set, GDK_Page_Down, GTK_SCROLL_PAGE_DOWN);
443 add_scroll_binding (binding_set, GDK_Home, GTK_SCROLL_START);
444 add_scroll_binding (binding_set, GDK_End, GTK_SCROLL_END);
446 g_type_class_add_private (gobject_class, sizeof(ModestMsgViewWindowPrivate));
450 static void modest_header_view_observer_init(
451 ModestHeaderViewObserverIface *iface_class)
453 iface_class->update_func = modest_msg_view_window_update_model_replaced;
457 modest_msg_view_window_init (ModestMsgViewWindow *obj)
459 ModestMsgViewWindowPrivate *priv;
460 ModestWindowPrivate *parent_priv = NULL;
461 GtkActionGroup *action_group = NULL;
462 GError *error = NULL;
463 GdkPixbuf *window_icon;
465 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(obj);
466 parent_priv = MODEST_WINDOW_GET_PRIVATE(obj);
467 parent_priv->ui_manager = gtk_ui_manager_new();
469 action_group = gtk_action_group_new ("ModestMsgViewWindowActions");
470 gtk_action_group_set_translation_domain (action_group, GETTEXT_PACKAGE);
472 /* Add common actions */
473 gtk_action_group_add_actions (action_group,
474 modest_action_entries,
475 G_N_ELEMENTS (modest_action_entries),
477 gtk_action_group_add_toggle_actions (action_group,
478 msg_view_toggle_action_entries,
479 G_N_ELEMENTS (msg_view_toggle_action_entries),
482 gtk_ui_manager_insert_action_group (parent_priv->ui_manager, action_group, 0);
483 g_object_unref (action_group);
485 /* Load the UI definition */
486 gtk_ui_manager_add_ui_from_file (parent_priv->ui_manager, MODEST_UIDIR "modest-msg-view-window-ui.xml",
489 g_printerr ("modest: could not merge modest-msg-view-window-ui.xml: %s\n", error->message);
490 g_error_free (error);
495 /* Add accelerators */
496 gtk_window_add_accel_group (GTK_WINDOW (obj),
497 gtk_ui_manager_get_accel_group (parent_priv->ui_manager));
499 priv->is_search_result = FALSE;
500 priv->is_outbox = FALSE;
502 priv->msg_view = NULL;
503 priv->header_model = NULL;
504 priv->header_folder_id = NULL;
505 priv->clipboard_change_handler = 0;
506 priv->queue_change_handler = 0;
507 priv->account_removed_handler = 0;
508 priv->row_changed_handler = 0;
509 priv->row_deleted_handler = 0;
510 priv->row_inserted_handler = 0;
511 priv->rows_reordered_handler = 0;
512 priv->progress_hint = FALSE;
514 priv->optimized_view = FALSE;
515 priv->purge_timeout = 0;
516 priv->remove_attachment_banner = NULL;
517 priv->msg_uid = NULL;
519 priv->sighandlers = NULL;
522 init_window (MODEST_MSG_VIEW_WINDOW(obj));
524 /* Set window icon */
525 window_icon = modest_platform_get_icon (MODEST_APP_MSG_VIEW_ICON, MODEST_ICON_SIZE_BIG);
527 gtk_window_set_icon (GTK_WINDOW (obj), window_icon);
528 g_object_unref (window_icon);
531 hildon_program_add_window (hildon_program_get_instance(),
538 set_toolbar_transfer_mode (ModestMsgViewWindow *self)
540 ModestMsgViewWindowPrivate *priv = NULL;
542 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self), FALSE);
544 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(self);
546 set_progress_hint (self, TRUE);
552 set_progress_hint (ModestMsgViewWindow *self,
555 ModestWindowPrivate *parent_priv;
556 ModestMsgViewWindowPrivate *priv;
558 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self));
560 parent_priv = MODEST_WINDOW_GET_PRIVATE(self);
561 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(self);
563 /* Sets current progress hint */
564 priv->progress_hint = enabled;
566 if (GTK_WIDGET_VISIBLE (self)) {
567 hildon_gtk_window_set_progress_indicator (GTK_WINDOW (self), enabled?1:0);
574 init_window (ModestMsgViewWindow *obj)
576 GtkWidget *main_vbox;
577 ModestMsgViewWindowPrivate *priv;
579 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(obj);
581 priv->msg_view = GTK_WIDGET (tny_platform_factory_new_msg_view (modest_tny_platform_factory_get_instance ()));
582 modest_msg_view_set_shadow_type (MODEST_MSG_VIEW (priv->msg_view), GTK_SHADOW_NONE);
583 main_vbox = gtk_vbox_new (FALSE, 6);
584 #ifdef MODEST_TOOLKIT_HILDON2
585 priv->main_scroll = hildon_pannable_area_new ();
586 gtk_container_add (GTK_CONTAINER (priv->main_scroll), priv->msg_view);
588 #ifdef MODEST_USE_MOZEMBED
589 priv->main_scroll = priv->msg_view;
590 gtk_widget_set_size_request (priv->msg_view, -1, 1600);
592 priv->main_scroll = gtk_scrolled_window_new (NULL, NULL);
593 gtk_container_add (GTK_CONTAINER (priv->main_scroll), priv->msg_view);
595 gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (priv->main_scroll), GTK_POLICY_NEVER, GTK_POLICY_AUTOMATIC);
596 gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW (priv->main_scroll), GTK_SHADOW_NONE);
597 modest_maemo_set_thumbable_scrollbar (GTK_SCROLLED_WINDOW(priv->main_scroll), TRUE);
600 gtk_box_pack_start (GTK_BOX(main_vbox), priv->main_scroll, TRUE, TRUE, 0);
601 gtk_container_add (GTK_CONTAINER(obj), main_vbox);
603 priv->find_toolbar = hildon_find_toolbar_new (NULL);
604 hildon_window_add_toolbar (HILDON_WINDOW (obj), GTK_TOOLBAR (priv->find_toolbar));
605 gtk_widget_set_no_show_all (priv->find_toolbar, TRUE);
607 gtk_widget_show_all (GTK_WIDGET(main_vbox));
611 modest_msg_view_window_disconnect_signals (ModestWindow *self)
613 ModestMsgViewWindowPrivate *priv;
614 GtkWidget *header_view = NULL;
615 GtkWindow *parent_window = NULL;
617 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
619 if (gtk_clipboard_get (GDK_SELECTION_PRIMARY) &&
620 g_signal_handler_is_connected (gtk_clipboard_get (GDK_SELECTION_PRIMARY),
621 priv->clipboard_change_handler))
622 g_signal_handler_disconnect (gtk_clipboard_get (GDK_SELECTION_PRIMARY),
623 priv->clipboard_change_handler);
625 if (g_signal_handler_is_connected (G_OBJECT (modest_runtime_get_mail_operation_queue ()),
626 priv->queue_change_handler))
627 g_signal_handler_disconnect (G_OBJECT (modest_runtime_get_mail_operation_queue ()),
628 priv->queue_change_handler);
630 if (g_signal_handler_is_connected (G_OBJECT (modest_runtime_get_account_store ()),
631 priv->account_removed_handler))
632 g_signal_handler_disconnect (G_OBJECT (modest_runtime_get_account_store ()),
633 priv->account_removed_handler);
635 if (priv->header_model) {
636 if (g_signal_handler_is_connected(G_OBJECT (priv->header_model),
637 priv->row_changed_handler))
638 g_signal_handler_disconnect(G_OBJECT (priv->header_model),
639 priv->row_changed_handler);
641 if (g_signal_handler_is_connected(G_OBJECT (priv->header_model),
642 priv->row_deleted_handler))
643 g_signal_handler_disconnect(G_OBJECT (priv->header_model),
644 priv->row_deleted_handler);
646 if (g_signal_handler_is_connected(G_OBJECT (priv->header_model),
647 priv->row_inserted_handler))
648 g_signal_handler_disconnect(G_OBJECT (priv->header_model),
649 priv->row_inserted_handler);
651 if (g_signal_handler_is_connected(G_OBJECT (priv->header_model),
652 priv->rows_reordered_handler))
653 g_signal_handler_disconnect(G_OBJECT (priv->header_model),
654 priv->rows_reordered_handler);
657 modest_signal_mgr_disconnect_all_and_destroy (priv->sighandlers);
658 priv->sighandlers = NULL;
660 parent_window = gtk_window_get_transient_for (GTK_WINDOW (self));
661 if (parent_window && MODEST_IS_HEADER_WINDOW (parent_window)) {
662 header_view = GTK_WIDGET (modest_header_window_get_header_view (MODEST_HEADER_WINDOW (parent_window)));
664 modest_header_view_remove_observer(MODEST_HEADER_VIEW (header_view),
665 MODEST_HEADER_VIEW_OBSERVER(self));
671 modest_msg_view_window_finalize (GObject *obj)
673 ModestMsgViewWindowPrivate *priv;
675 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (obj);
677 /* Sanity check: shouldn't be needed, the window mgr should
678 call this function before */
679 modest_msg_view_window_disconnect_signals (MODEST_WINDOW (obj));
681 if (priv->header_model != NULL) {
682 g_object_unref (priv->header_model);
683 priv->header_model = NULL;
686 if (priv->remove_attachment_banner) {
687 gtk_widget_destroy (priv->remove_attachment_banner);
688 g_object_unref (priv->remove_attachment_banner);
689 priv->remove_attachment_banner = NULL;
692 if (priv->purge_timeout > 0) {
693 g_source_remove (priv->purge_timeout);
694 priv->purge_timeout = 0;
697 if (priv->row_reference) {
698 gtk_tree_row_reference_free (priv->row_reference);
699 priv->row_reference = NULL;
702 if (priv->next_row_reference) {
703 gtk_tree_row_reference_free (priv->next_row_reference);
704 priv->next_row_reference = NULL;
708 g_free (priv->msg_uid);
709 priv->msg_uid = NULL;
712 G_OBJECT_CLASS(parent_class)->finalize (obj);
716 select_next_valid_row (GtkTreeModel *model,
717 GtkTreeRowReference **row_reference,
721 GtkTreeIter tmp_iter;
723 GtkTreePath *next = NULL;
724 gboolean retval = FALSE, finished;
726 g_return_val_if_fail (gtk_tree_row_reference_valid (*row_reference), FALSE);
728 path = gtk_tree_row_reference_get_path (*row_reference);
729 gtk_tree_model_get_iter (model, &tmp_iter, path);
730 gtk_tree_row_reference_free (*row_reference);
731 *row_reference = NULL;
735 TnyHeader *header = NULL;
737 if (gtk_tree_model_iter_next (model, &tmp_iter)) {
738 gtk_tree_model_get (model, &tmp_iter,
739 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
743 if (msg_is_visible (header, is_outbox)) {
744 next = gtk_tree_model_get_path (model, &tmp_iter);
745 *row_reference = gtk_tree_row_reference_new (model, next);
746 gtk_tree_path_free (next);
750 g_object_unref (header);
753 } else if (cycle && gtk_tree_model_get_iter_first (model, &tmp_iter)) {
754 next = gtk_tree_model_get_path (model, &tmp_iter);
756 /* Ensure that we are not selecting the same */
757 if (gtk_tree_path_compare (path, next) != 0) {
758 gtk_tree_model_get (model, &tmp_iter,
759 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
762 if (msg_is_visible (header, is_outbox)) {
763 *row_reference = gtk_tree_row_reference_new (model, next);
767 g_object_unref (header);
771 /* If we ended up in the same message
772 then there is no valid next
776 gtk_tree_path_free (next);
778 /* If there are no more messages and we don't
779 want to start again in the first one then
780 there is no valid next message */
786 gtk_tree_path_free (path);
791 /* TODO: This should be in _init(), with the parameters as properties. */
793 modest_msg_view_window_construct (ModestMsgViewWindow *self,
794 const gchar *modest_account_name,
795 const gchar *msg_uid)
798 ModestMsgViewWindowPrivate *priv = NULL;
799 ModestWindowPrivate *parent_priv = NULL;
800 ModestDimmingRulesGroup *menu_rules_group = NULL;
801 ModestDimmingRulesGroup *toolbar_rules_group = NULL;
802 ModestDimmingRulesGroup *clipboard_rules_group = NULL;
804 obj = G_OBJECT (self);
805 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(obj);
806 parent_priv = MODEST_WINDOW_GET_PRIVATE(obj);
808 priv->msg_uid = g_strdup (msg_uid);
811 parent_priv->menubar = NULL;
812 parent_priv->ui_dimming_manager = modest_ui_dimming_manager_new();
814 menu_rules_group = modest_dimming_rules_group_new (MODEST_DIMMING_RULES_MENU, FALSE);
815 toolbar_rules_group = modest_dimming_rules_group_new (MODEST_DIMMING_RULES_TOOLBAR, TRUE);
816 clipboard_rules_group = modest_dimming_rules_group_new (MODEST_DIMMING_RULES_CLIPBOARD, FALSE);
818 setup_menu (self, menu_rules_group);
819 /* Add common dimming rules */
820 modest_dimming_rules_group_add_rules (menu_rules_group,
821 modest_msg_view_menu_dimming_entries,
822 G_N_ELEMENTS (modest_msg_view_menu_dimming_entries),
823 MODEST_WINDOW (self));
824 modest_dimming_rules_group_add_rules (toolbar_rules_group,
825 modest_msg_view_toolbar_dimming_entries,
826 G_N_ELEMENTS (modest_msg_view_toolbar_dimming_entries),
827 MODEST_WINDOW (self));
828 modest_dimming_rules_group_add_rules (clipboard_rules_group,
829 modest_msg_view_clipboard_dimming_entries,
830 G_N_ELEMENTS (modest_msg_view_clipboard_dimming_entries),
831 MODEST_WINDOW (self));
833 /* Insert dimming rules group for this window */
834 modest_ui_dimming_manager_insert_rules_group (parent_priv->ui_dimming_manager, menu_rules_group);
835 modest_ui_dimming_manager_insert_rules_group (parent_priv->ui_dimming_manager, toolbar_rules_group);
836 modest_ui_dimming_manager_insert_rules_group (parent_priv->ui_dimming_manager, clipboard_rules_group);
837 g_object_unref (menu_rules_group);
838 g_object_unref (toolbar_rules_group);
839 g_object_unref (clipboard_rules_group);
841 /* g_signal_connect (G_OBJECT(obj), "delete-event", G_CALLBACK(on_delete_event), obj); */
843 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);
844 g_signal_connect (G_OBJECT(priv->msg_view), "activate_link",
845 G_CALLBACK (modest_ui_actions_on_msg_link_clicked), obj);
846 g_signal_connect (G_OBJECT(priv->msg_view), "link_hover",
847 G_CALLBACK (modest_ui_actions_on_msg_link_hover), obj);
848 g_signal_connect (G_OBJECT(priv->msg_view), "attachment_clicked",
849 G_CALLBACK (modest_ui_actions_on_msg_attachment_clicked), obj);
850 g_signal_connect (G_OBJECT(priv->msg_view), "recpt_activated",
851 G_CALLBACK (modest_ui_actions_on_msg_recpt_activated), obj);
852 g_signal_connect (G_OBJECT(priv->msg_view), "show_details",
853 G_CALLBACK (modest_ui_actions_on_details), obj);
854 g_signal_connect (G_OBJECT(priv->msg_view), "link_contextual",
855 G_CALLBACK (modest_ui_actions_on_msg_link_contextual), obj);
856 g_signal_connect (G_OBJECT (priv->msg_view), "fetch_image",
857 G_CALLBACK (on_fetch_image), obj);
859 g_signal_connect (G_OBJECT (obj), "key-release-event",
860 G_CALLBACK (modest_msg_view_window_key_event),
863 g_signal_connect (G_OBJECT (obj), "key-press-event",
864 G_CALLBACK (modest_msg_view_window_key_event),
867 g_signal_connect (G_OBJECT (obj), "move-focus",
868 G_CALLBACK (on_move_focus), obj);
870 /* Mail Operation Queue */
871 priv->queue_change_handler = g_signal_connect (G_OBJECT (modest_runtime_get_mail_operation_queue ()),
873 G_CALLBACK (on_queue_changed),
876 /* Account manager */
877 priv->account_removed_handler = g_signal_connect (G_OBJECT (modest_runtime_get_account_store ()),
879 G_CALLBACK(on_account_removed),
882 modest_window_set_active_account (MODEST_WINDOW(obj), modest_account_name);
884 g_signal_connect (G_OBJECT (priv->find_toolbar), "close", G_CALLBACK (modest_msg_view_window_find_toolbar_close), obj);
885 g_signal_connect (G_OBJECT (priv->find_toolbar), "search", G_CALLBACK (modest_msg_view_window_find_toolbar_search), obj);
886 priv->last_search = NULL;
888 modest_msg_view_window_show_toolbar (MODEST_WINDOW (obj), TRUE);
890 /* Init the clipboard actions dim status */
891 modest_msg_view_grab_focus(MODEST_MSG_VIEW (priv->msg_view));
893 update_window_title (MODEST_MSG_VIEW_WINDOW (obj));
898 /* FIXME: parameter checks */
900 modest_msg_view_window_new_with_header_model (TnyMsg *msg,
901 const gchar *modest_account_name,
902 const gchar *msg_uid,
904 GtkTreeRowReference *row_reference)
906 ModestMsgViewWindow *window = NULL;
907 ModestMsgViewWindowPrivate *priv = NULL;
908 TnyFolder *header_folder = NULL;
909 ModestHeaderView *header_view = NULL;
910 ModestWindow *main_window = NULL;
911 ModestWindowMgr *mgr = NULL;
914 modest_tny_mime_part_to_string (TNY_MIME_PART (msg), 0);
917 mgr = modest_runtime_get_window_mgr ();
918 window = MODEST_MSG_VIEW_WINDOW (modest_window_mgr_get_msg_view_window (mgr));
919 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), NULL);
921 modest_msg_view_window_construct (window, modest_account_name, msg_uid);
923 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
925 /* Remember the message list's TreeModel so we can detect changes
926 * and change the list selection when necessary: */
928 main_window = modest_window_mgr_get_main_window(mgr, FALSE); /* don't create */
930 header_view = MODEST_HEADER_VIEW(modest_main_window_get_child_widget(
931 MODEST_MAIN_WINDOW(main_window),
932 MODEST_MAIN_WINDOW_WIDGET_TYPE_HEADER_VIEW));
935 if (header_view != NULL){
936 header_folder = modest_header_view_get_folder(header_view);
937 /* This could happen if the header folder was
938 unseleted before opening this msg window (for
939 example if the user selects an account in the
940 folder view of the main window */
942 priv->is_outbox = (modest_tny_folder_guess_folder_type (header_folder) == TNY_FOLDER_TYPE_OUTBOX);
943 priv->header_folder_id = tny_folder_get_id(header_folder);
944 g_assert(priv->header_folder_id != NULL);
945 g_object_unref(header_folder);
949 /* Setup row references and connect signals */
950 priv->header_model = g_object_ref (model);
953 priv->row_reference = gtk_tree_row_reference_copy (row_reference);
954 priv->next_row_reference = gtk_tree_row_reference_copy (row_reference);
955 select_next_valid_row (model, &(priv->next_row_reference), TRUE, priv->is_outbox);
957 priv->row_reference = NULL;
958 priv->next_row_reference = NULL;
961 /* Connect signals */
962 priv->row_changed_handler =
963 g_signal_connect (GTK_TREE_MODEL(model), "row-changed",
964 G_CALLBACK(modest_msg_view_window_on_row_changed),
966 priv->row_deleted_handler =
967 g_signal_connect (GTK_TREE_MODEL(model), "row-deleted",
968 G_CALLBACK(modest_msg_view_window_on_row_deleted),
970 priv->row_inserted_handler =
971 g_signal_connect (GTK_TREE_MODEL(model), "row-inserted",
972 G_CALLBACK(modest_msg_view_window_on_row_inserted),
974 priv->rows_reordered_handler =
975 g_signal_connect(GTK_TREE_MODEL(model), "rows-reordered",
976 G_CALLBACK(modest_msg_view_window_on_row_reordered),
979 if (header_view != NULL){
980 modest_header_view_add_observer(header_view,
981 MODEST_HEADER_VIEW_OBSERVER(window));
984 tny_msg_view_set_msg (TNY_MSG_VIEW (priv->msg_view), msg);
985 update_window_title (MODEST_MSG_VIEW_WINDOW (window));
987 /* gtk_widget_show_all (GTK_WIDGET (window)); */
988 modest_msg_view_window_update_priority (window);
989 /* Check dimming rules */
990 modest_ui_actions_check_toolbar_dimming_rules (MODEST_WINDOW (window));
991 modest_ui_actions_check_menu_dimming_rules (MODEST_WINDOW (window));
992 modest_window_check_dimming_rules_group (MODEST_WINDOW (window), MODEST_DIMMING_RULES_CLIPBOARD);
994 return MODEST_WINDOW(window);
998 modest_msg_view_window_new_from_header_view (ModestHeaderView *header_view,
999 const gchar *modest_account_name,
1000 const gchar *msg_uid,
1001 GtkTreeRowReference *row_reference)
1003 ModestMsgViewWindow *window = NULL;
1004 ModestMsgViewWindowPrivate *priv = NULL;
1005 TnyFolder *header_folder = NULL;
1006 ModestWindowMgr *mgr = NULL;
1010 mgr = modest_runtime_get_window_mgr ();
1011 window = MODEST_MSG_VIEW_WINDOW (modest_window_mgr_get_msg_view_window (mgr));
1012 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), NULL);
1014 modest_msg_view_window_construct (window, modest_account_name, msg_uid);
1016 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1018 /* Remember the message list's TreeModel so we can detect changes
1019 * and change the list selection when necessary: */
1021 if (header_view != NULL){
1022 header_folder = modest_header_view_get_folder(header_view);
1023 /* This could happen if the header folder was
1024 unseleted before opening this msg window (for
1025 example if the user selects an account in the
1026 folder view of the main window */
1027 if (header_folder) {
1028 priv->is_outbox = (modest_tny_folder_guess_folder_type (header_folder) == TNY_FOLDER_TYPE_OUTBOX);
1029 priv->header_folder_id = tny_folder_get_id(header_folder);
1030 g_assert(priv->header_folder_id != NULL);
1031 g_object_unref(header_folder);
1035 /* Setup row references and connect signals */
1036 priv->header_model = gtk_tree_view_get_model (GTK_TREE_VIEW (header_view));
1038 if (row_reference) {
1039 priv->row_reference = gtk_tree_row_reference_copy (row_reference);
1040 priv->next_row_reference = gtk_tree_row_reference_copy (row_reference);
1041 select_next_valid_row (priv->header_model, &(priv->next_row_reference), TRUE, priv->is_outbox);
1043 priv->row_reference = NULL;
1044 priv->next_row_reference = NULL;
1047 /* Connect signals */
1048 priv->row_changed_handler =
1049 g_signal_connect (GTK_TREE_MODEL(priv->header_model), "row-changed",
1050 G_CALLBACK(modest_msg_view_window_on_row_changed),
1052 priv->row_deleted_handler =
1053 g_signal_connect (GTK_TREE_MODEL(priv->header_model), "row-deleted",
1054 G_CALLBACK(modest_msg_view_window_on_row_deleted),
1056 priv->row_inserted_handler =
1057 g_signal_connect (GTK_TREE_MODEL(priv->header_model), "row-inserted",
1058 G_CALLBACK(modest_msg_view_window_on_row_inserted),
1060 priv->rows_reordered_handler =
1061 g_signal_connect(GTK_TREE_MODEL(priv->header_model), "rows-reordered",
1062 G_CALLBACK(modest_msg_view_window_on_row_reordered),
1065 if (header_view != NULL){
1066 modest_header_view_add_observer(header_view,
1067 MODEST_HEADER_VIEW_OBSERVER(window));
1070 tny_msg_view_set_msg (TNY_MSG_VIEW (priv->msg_view), NULL);
1072 path = gtk_tree_row_reference_get_path (row_reference);
1073 if (gtk_tree_model_get_iter (priv->header_model, &iter, path)) {
1075 gtk_tree_model_get (priv->header_model, &iter,
1076 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
1078 message_reader (window, priv, header, row_reference);
1080 gtk_tree_path_free (path);
1082 /* Check dimming rules */
1083 modest_ui_actions_check_toolbar_dimming_rules (MODEST_WINDOW (window));
1084 modest_ui_actions_check_menu_dimming_rules (MODEST_WINDOW (window));
1085 modest_window_check_dimming_rules_group (MODEST_WINDOW (window), MODEST_DIMMING_RULES_CLIPBOARD);
1087 return MODEST_WINDOW(window);
1091 modest_msg_view_window_new_for_search_result (TnyMsg *msg,
1092 const gchar *modest_account_name,
1093 const gchar *msg_uid)
1095 ModestMsgViewWindow *window = NULL;
1096 ModestMsgViewWindowPrivate *priv = NULL;
1097 ModestWindowMgr *mgr = NULL;
1099 mgr = modest_runtime_get_window_mgr ();
1100 window = MODEST_MSG_VIEW_WINDOW (modest_window_mgr_get_msg_view_window (mgr));
1101 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), NULL);
1102 modest_msg_view_window_construct (window, modest_account_name, msg_uid);
1104 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1106 /* Remember that this is a search result,
1107 * so we can disable some UI appropriately: */
1108 priv->is_search_result = TRUE;
1110 tny_msg_view_set_msg (TNY_MSG_VIEW (priv->msg_view), msg);
1112 update_window_title (window);
1113 /* gtk_widget_show_all (GTK_WIDGET (window));*/
1114 modest_msg_view_window_update_priority (window);
1116 /* Check dimming rules */
1117 modest_ui_actions_check_toolbar_dimming_rules (MODEST_WINDOW (window));
1118 modest_ui_actions_check_menu_dimming_rules (MODEST_WINDOW (window));
1119 modest_window_check_dimming_rules_group (MODEST_WINDOW (window), MODEST_DIMMING_RULES_CLIPBOARD);
1121 return MODEST_WINDOW(window);
1125 modest_msg_view_window_new_for_attachment (TnyMsg *msg,
1126 const gchar *modest_account_name,
1127 const gchar *msg_uid)
1129 GObject *obj = NULL;
1130 ModestMsgViewWindowPrivate *priv;
1131 ModestWindowMgr *mgr = NULL;
1133 g_return_val_if_fail (msg, NULL);
1134 mgr = modest_runtime_get_window_mgr ();
1135 obj = G_OBJECT (modest_window_mgr_get_msg_view_window (mgr));
1136 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (obj);
1137 modest_msg_view_window_construct (MODEST_MSG_VIEW_WINDOW (obj),
1138 modest_account_name, msg_uid);
1140 tny_msg_view_set_msg (TNY_MSG_VIEW (priv->msg_view), msg);
1141 update_window_title (MODEST_MSG_VIEW_WINDOW (obj));
1143 /* gtk_widget_show_all (GTK_WIDGET (obj)); */
1145 /* Check dimming rules */
1146 modest_ui_actions_check_toolbar_dimming_rules (MODEST_WINDOW (obj));
1147 modest_ui_actions_check_menu_dimming_rules (MODEST_WINDOW (obj));
1148 modest_window_check_dimming_rules_group (MODEST_WINDOW (obj), MODEST_DIMMING_RULES_CLIPBOARD);
1150 return MODEST_WINDOW(obj);
1154 modest_msg_view_window_on_row_changed (GtkTreeModel *header_model,
1157 ModestMsgViewWindow *window)
1159 check_dimming_rules_after_change (window);
1163 modest_msg_view_window_on_row_deleted(GtkTreeModel *header_model,
1165 ModestMsgViewWindow *window)
1167 check_dimming_rules_after_change (window);
1169 /* The window could have dissapeared */
1172 check_dimming_rules_after_change (ModestMsgViewWindow *window)
1174 modest_ui_actions_check_menu_dimming_rules (MODEST_WINDOW (window));
1175 modest_ui_actions_check_toolbar_dimming_rules (MODEST_WINDOW (window));
1179 /* On insertions we check if the folder still has the message we are
1180 * showing or do not. If do not, we do nothing. Which means we are still
1181 * not attached to any header folder and thus next/prev buttons are
1182 * still dimmed. Once the message that is shown by msg-view is found, the
1183 * new model of header-view will be attached and the references will be set.
1184 * On each further insertions dimming rules will be checked. However
1185 * this requires extra CPU time at least works.
1186 * (An message might be deleted from TnyFolder and thus will not be
1187 * inserted into the model again for example if it is removed by the
1188 * imap server and the header view is refreshed.)
1191 modest_msg_view_window_on_row_inserted (GtkTreeModel *model,
1192 GtkTreePath *tree_path,
1193 GtkTreeIter *tree_iter,
1194 ModestMsgViewWindow *window)
1196 ModestMsgViewWindowPrivate *priv = NULL;
1197 TnyHeader *header = NULL;
1199 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window));
1200 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1202 g_assert (model == priv->header_model);
1204 /* Check if the newly inserted message is the same we are actually
1205 * showing. IF not, we should remain detached from the header model
1206 * and thus prev and next toolbar buttons should remain dimmed. */
1207 gtk_tree_model_get (model, tree_iter,
1208 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
1211 if (TNY_IS_HEADER (header)) {
1214 uid = modest_tny_folder_get_header_unique_id (header);
1215 if (!g_str_equal(priv->msg_uid, uid)) {
1216 check_dimming_rules_after_change (window);
1218 g_object_unref (G_OBJECT(header));
1222 g_object_unref(G_OBJECT(header));
1225 if (priv->row_reference) {
1226 gtk_tree_row_reference_free (priv->row_reference);
1229 /* Setup row_reference for the actual msg. */
1230 priv->row_reference = gtk_tree_row_reference_new (priv->header_model, tree_path);
1231 if (priv->row_reference == NULL) {
1232 g_warning("No reference for msg header item.");
1236 /* Now set up next_row_reference. */
1237 if (priv->next_row_reference) {
1238 gtk_tree_row_reference_free (priv->next_row_reference);
1241 priv->next_row_reference = gtk_tree_row_reference_copy (priv->row_reference);
1242 select_next_valid_row (priv->header_model,
1243 &(priv->next_row_reference), FALSE, priv->is_outbox);
1245 /* Connect the remaining callbacks to become able to detect
1246 * changes in header-view. */
1247 priv->row_changed_handler =
1248 g_signal_connect (priv->header_model, "row-changed",
1249 G_CALLBACK (modest_msg_view_window_on_row_changed),
1251 priv->row_deleted_handler =
1252 g_signal_connect (priv->header_model, "row-deleted",
1253 G_CALLBACK (modest_msg_view_window_on_row_deleted),
1255 priv->rows_reordered_handler =
1256 g_signal_connect (priv->header_model, "rows-reordered",
1257 G_CALLBACK (modest_msg_view_window_on_row_reordered),
1260 check_dimming_rules_after_change (window);
1264 modest_msg_view_window_on_row_reordered (GtkTreeModel *header_model,
1268 ModestMsgViewWindow *window)
1270 ModestMsgViewWindowPrivate *priv = NULL;
1271 gboolean already_changed = FALSE;
1273 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(window);
1275 /* If the current row was reordered select the proper next
1276 valid row. The same if the next row reference changes */
1277 if (priv->row_reference &&
1278 gtk_tree_row_reference_valid (priv->row_reference)) {
1280 path = gtk_tree_row_reference_get_path (priv->row_reference);
1281 if (gtk_tree_path_compare (path, arg1) == 0) {
1282 if (priv->next_row_reference) {
1283 gtk_tree_row_reference_free (priv->next_row_reference);
1285 priv->next_row_reference = gtk_tree_row_reference_copy (priv->row_reference);
1286 select_next_valid_row (header_model, &(priv->next_row_reference), FALSE, priv->is_outbox);
1287 already_changed = TRUE;
1289 gtk_tree_path_free (path);
1291 if (!already_changed &&
1292 priv->next_row_reference &&
1293 gtk_tree_row_reference_valid (priv->next_row_reference)) {
1295 path = gtk_tree_row_reference_get_path (priv->next_row_reference);
1296 if (gtk_tree_path_compare (path, arg1) == 0) {
1297 if (priv->next_row_reference) {
1298 gtk_tree_row_reference_free (priv->next_row_reference);
1300 priv->next_row_reference = gtk_tree_row_reference_copy (priv->row_reference);
1301 select_next_valid_row (header_model, &(priv->next_row_reference), FALSE, priv->is_outbox);
1303 gtk_tree_path_free (path);
1305 check_dimming_rules_after_change (window);
1308 /* The modest_msg_view_window_update_model_replaced implements update
1309 * function for ModestHeaderViewObserver. Checks whether the TnyFolder
1310 * actually belongs to the header-view is the same as the TnyFolder of
1311 * the message of msg-view or not. If they are different, there is
1312 * nothing to do. If they are the same, then the model has replaced and
1313 * the reference in msg-view shall be replaced from the old model to
1314 * the new model. In this case the view will be detached from it's
1315 * header folder. From this point the next/prev buttons are dimmed.
1318 modest_msg_view_window_update_model_replaced (ModestHeaderViewObserver *observer,
1319 GtkTreeModel *model,
1320 const gchar *tny_folder_id)
1322 ModestMsgViewWindowPrivate *priv = NULL;
1323 ModestMsgViewWindow *window = NULL;
1325 g_assert(MODEST_IS_HEADER_VIEW_OBSERVER(observer));
1326 g_assert(MODEST_IS_MSG_VIEW_WINDOW(observer));
1328 window = MODEST_MSG_VIEW_WINDOW(observer);
1329 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(window);
1331 /* If there is an other folder in the header-view then we do
1332 * not care about it's model (msg list). Else if the
1333 * header-view shows the folder the msg shown by us is in, we
1334 * shall replace our model reference and make some check. */
1335 if(model == NULL || tny_folder_id == NULL ||
1336 (priv->header_folder_id && !g_str_equal(tny_folder_id, priv->header_folder_id)))
1339 /* Model is changed(replaced), so we should forget the old
1340 * one. Because there might be other references and there
1341 * might be some change on the model even if we unreferenced
1342 * it, we need to disconnect our signals here. */
1343 if (priv->header_model) {
1344 if (g_signal_handler_is_connected(G_OBJECT (priv->header_model),
1345 priv->row_changed_handler))
1346 g_signal_handler_disconnect(G_OBJECT (priv->header_model),
1347 priv->row_changed_handler);
1348 if (g_signal_handler_is_connected(G_OBJECT (priv->header_model),
1349 priv->row_deleted_handler))
1350 g_signal_handler_disconnect(G_OBJECT (priv->header_model),
1351 priv->row_deleted_handler);
1352 if (g_signal_handler_is_connected(G_OBJECT (priv->header_model),
1353 priv->row_inserted_handler))
1354 g_signal_handler_disconnect(G_OBJECT (priv->header_model),
1355 priv->row_inserted_handler);
1356 if (g_signal_handler_is_connected(G_OBJECT (priv->header_model),
1357 priv->rows_reordered_handler))
1358 g_signal_handler_disconnect(G_OBJECT (priv->header_model),
1359 priv->rows_reordered_handler);
1362 if (priv->row_reference)
1363 gtk_tree_row_reference_free (priv->row_reference);
1364 if (priv->next_row_reference)
1365 gtk_tree_row_reference_free (priv->next_row_reference);
1366 g_object_unref(priv->header_model);
1369 priv->row_changed_handler = 0;
1370 priv->row_deleted_handler = 0;
1371 priv->row_inserted_handler = 0;
1372 priv->rows_reordered_handler = 0;
1373 priv->next_row_reference = NULL;
1374 priv->row_reference = NULL;
1375 priv->header_model = NULL;
1378 priv->header_model = g_object_ref (model);
1380 /* Also we must connect to the new model for row insertions.
1381 * Only for insertions now. We will need other ones only after
1382 * the msg is show by msg-view is added to the new model. */
1383 priv->row_inserted_handler =
1384 g_signal_connect (priv->header_model, "row-inserted",
1385 G_CALLBACK(modest_msg_view_window_on_row_inserted),
1388 modest_ui_actions_check_menu_dimming_rules(MODEST_WINDOW(window));
1389 modest_ui_actions_check_toolbar_dimming_rules(MODEST_WINDOW(window));
1393 modest_msg_view_window_toolbar_on_transfer_mode (ModestMsgViewWindow *self)
1395 ModestMsgViewWindowPrivate *priv= NULL;
1397 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self), FALSE);
1398 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
1400 return priv->progress_hint;
1404 modest_msg_view_window_get_header (ModestMsgViewWindow *self)
1406 ModestMsgViewWindowPrivate *priv= NULL;
1408 TnyHeader *header = NULL;
1409 GtkTreePath *path = NULL;
1412 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self), NULL);
1413 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
1415 /* If the message was not obtained from a treemodel,
1416 * for instance if it was opened directly by the search UI:
1418 if (priv->header_model == NULL ||
1419 priv->row_reference == NULL ||
1420 !gtk_tree_row_reference_valid (priv->row_reference)) {
1421 msg = modest_msg_view_window_get_message (self);
1423 header = tny_msg_get_header (msg);
1424 g_object_unref (msg);
1429 /* Get iter of the currently selected message in the header view: */
1430 path = gtk_tree_row_reference_get_path (priv->row_reference);
1431 g_return_val_if_fail (path != NULL, NULL);
1432 gtk_tree_model_get_iter (priv->header_model,
1436 /* Get current message header */
1437 gtk_tree_model_get (priv->header_model, &iter,
1438 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
1441 gtk_tree_path_free (path);
1446 modest_msg_view_window_get_message (ModestMsgViewWindow *self)
1448 ModestMsgViewWindowPrivate *priv;
1450 g_return_val_if_fail (self, NULL);
1452 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(self);
1454 return tny_msg_view_get_msg (TNY_MSG_VIEW (priv->msg_view));
1458 modest_msg_view_window_get_message_uid (ModestMsgViewWindow *self)
1460 ModestMsgViewWindowPrivate *priv;
1462 g_return_val_if_fail (self, NULL);
1464 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
1466 return (const gchar*) priv->msg_uid;
1470 modest_msg_view_window_toggle_find_toolbar (GtkToggleAction *toggle,
1473 ModestMsgViewWindow *window = MODEST_MSG_VIEW_WINDOW (data);
1474 ModestMsgViewWindowPrivate *priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1475 ModestWindowPrivate *parent_priv = MODEST_WINDOW_GET_PRIVATE (window);
1479 is_active = gtk_toggle_action_get_active (toggle);
1482 gtk_widget_show (priv->find_toolbar);
1483 hildon_find_toolbar_highlight_entry (HILDON_FIND_TOOLBAR (priv->find_toolbar), TRUE);
1485 gtk_widget_hide (priv->find_toolbar);
1486 modest_msg_view_grab_focus (MODEST_MSG_VIEW (priv->msg_view));
1489 /* update the toggle buttons status */
1490 action = gtk_ui_manager_get_action (parent_priv->ui_manager, "/ToolBar/FindInMessage");
1492 modest_utils_toggle_action_set_active_block_notify (GTK_TOGGLE_ACTION (action), is_active);
1497 modest_msg_view_window_find_toolbar_close (GtkWidget *widget,
1498 ModestMsgViewWindow *obj)
1500 GtkToggleAction *toggle;
1501 ModestWindowPrivate *parent_priv;
1502 ModestMsgViewWindowPrivate *priv;
1504 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (obj);
1505 parent_priv = MODEST_WINDOW_GET_PRIVATE (obj);
1507 toggle = GTK_TOGGLE_ACTION (gtk_ui_manager_get_action (parent_priv->ui_manager, "/ToolBar/FindInMessage"));
1508 gtk_toggle_action_set_active (toggle, FALSE);
1509 modest_msg_view_grab_focus (MODEST_MSG_VIEW (priv->msg_view));
1513 modest_msg_view_window_find_toolbar_search (GtkWidget *widget,
1514 ModestMsgViewWindow *obj)
1516 gchar *current_search;
1517 ModestMsgViewWindowPrivate *priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (obj);
1519 if (modest_mime_part_view_is_empty (MODEST_MIME_PART_VIEW (priv->msg_view))) {
1520 hildon_banner_show_information (NULL, NULL, _("mail_ib_nothing_to_find"));
1524 g_object_get (G_OBJECT (widget), "prefix", ¤t_search, NULL);
1526 if ((current_search == NULL) || (strcmp (current_search, "") == 0)) {
1527 g_free (current_search);
1528 hildon_banner_show_information (NULL, NULL, dgettext("hildon-common-strings", "ecdg_ib_find_rep_enter_text"));
1532 if ((priv->last_search == NULL) || (strcmp (priv->last_search, current_search) != 0)) {
1534 g_free (priv->last_search);
1535 priv->last_search = g_strdup (current_search);
1536 result = modest_isearch_view_search (MODEST_ISEARCH_VIEW (priv->msg_view),
1539 hildon_banner_show_information (NULL, NULL, dgettext("hildon-libs", "ckct_ib_find_no_matches"));
1540 g_free (priv->last_search);
1541 priv->last_search = NULL;
1543 modest_msg_view_grab_focus (MODEST_MSG_VIEW (priv->msg_view));
1544 hildon_find_toolbar_highlight_entry (HILDON_FIND_TOOLBAR (priv->find_toolbar), TRUE);
1547 if (!modest_isearch_view_search_next (MODEST_ISEARCH_VIEW (priv->msg_view))) {
1548 hildon_banner_show_information (NULL, NULL, dgettext("hildon-libs", "ckct_ib_find_search_complete"));
1549 g_free (priv->last_search);
1550 priv->last_search = NULL;
1552 modest_msg_view_grab_focus (MODEST_MSG_VIEW (priv->msg_view));
1553 hildon_find_toolbar_highlight_entry (HILDON_FIND_TOOLBAR (priv->find_toolbar), TRUE);
1557 g_free (current_search);
1562 modest_msg_view_window_set_zoom (ModestWindow *window,
1565 ModestMsgViewWindowPrivate *priv;
1566 ModestWindowPrivate *parent_priv;
1568 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window));
1570 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1571 parent_priv = MODEST_WINDOW_GET_PRIVATE (window);
1572 modest_zoomable_set_zoom (MODEST_ZOOMABLE (priv->msg_view), zoom);
1577 modest_msg_view_window_get_zoom (ModestWindow *window)
1579 ModestMsgViewWindowPrivate *priv;
1581 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), 1.0);
1583 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1584 return modest_zoomable_get_zoom (MODEST_ZOOMABLE (priv->msg_view));
1588 modest_msg_view_window_zoom_plus (ModestWindow *window)
1591 ModestMsgViewWindowPrivate *priv;
1593 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), 1.0);
1594 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1596 zoom_level = modest_zoomable_get_zoom (MODEST_ZOOMABLE (priv->msg_view));
1598 if (zoom_level >= 2.0) {
1599 hildon_banner_show_information (NULL, NULL, dgettext("hildon-common-strings", "ckct_ib_max_zoom_level_reached"));
1601 } else if (zoom_level >= 1.5) {
1603 } else if (zoom_level >= 1.2) {
1605 } else if (zoom_level >= 1.0) {
1607 } else if (zoom_level >= 0.8) {
1609 } else if (zoom_level >= 0.5) {
1615 /* set zoom level */
1616 modest_zoomable_set_zoom (MODEST_ZOOMABLE (priv->msg_view), zoom_level);
1623 modest_msg_view_window_zoom_minus (ModestWindow *window)
1626 ModestMsgViewWindowPrivate *priv;
1628 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), 1.0);
1629 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1631 zoom_level = modest_zoomable_get_zoom (MODEST_ZOOMABLE (priv->msg_view));
1633 if (zoom_level <= 0.5) {
1634 hildon_banner_show_information (NULL, NULL, dgettext("hildon-common-strings", "ckct_ib_min_zoom_level_reached"));
1636 } else if (zoom_level <= 0.8) {
1638 } else if (zoom_level <= 1.0) {
1640 } else if (zoom_level <= 1.2) {
1642 } else if (zoom_level <= 1.5) {
1644 } else if (zoom_level <= 2.0) {
1650 /* set zoom level */
1651 modest_zoomable_set_zoom (MODEST_ZOOMABLE (priv->msg_view), zoom_level);
1658 modest_msg_view_window_key_event (GtkWidget *window,
1664 focus = gtk_window_get_focus (GTK_WINDOW (window));
1666 /* for the find toolbar case */
1667 if (focus && GTK_IS_ENTRY (focus)) {
1668 if (event->keyval == GDK_BackSpace) {
1670 copy = gdk_event_copy ((GdkEvent *) event);
1671 gtk_widget_event (focus, copy);
1672 gdk_event_free (copy);
1677 if (event->keyval == GDK_Up || event->keyval == GDK_KP_Up ||
1678 event->keyval == GDK_Down || event->keyval == GDK_KP_Down ||
1679 event->keyval == GDK_Page_Up || event->keyval == GDK_KP_Page_Up ||
1680 event->keyval == GDK_Page_Down || event->keyval == GDK_KP_Page_Down ||
1681 event->keyval == GDK_Home || event->keyval == GDK_KP_Home ||
1682 event->keyval == GDK_End || event->keyval == GDK_KP_End) {
1683 /* ModestMsgViewWindowPrivate *priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window); */
1684 /* gboolean return_value; */
1686 if (event->type == GDK_KEY_PRESS) {
1687 GtkScrollType scroll_type;
1689 switch (event->keyval) {
1692 scroll_type = GTK_SCROLL_STEP_UP; break;
1695 scroll_type = GTK_SCROLL_STEP_DOWN; break;
1697 case GDK_KP_Page_Up:
1698 scroll_type = GTK_SCROLL_PAGE_UP; break;
1700 case GDK_KP_Page_Down:
1701 scroll_type = GTK_SCROLL_PAGE_DOWN; break;
1704 scroll_type = GTK_SCROLL_START; break;
1707 scroll_type = GTK_SCROLL_END; break;
1708 default: scroll_type = GTK_SCROLL_NONE;
1711 /* g_signal_emit_by_name (G_OBJECT (priv->main_scroll), "scroll-child", */
1712 /* scroll_type, FALSE, &return_value); */
1723 modest_msg_view_window_last_message_selected (ModestMsgViewWindow *window)
1726 ModestMsgViewWindowPrivate *priv;
1727 GtkTreeIter tmp_iter;
1728 gboolean is_last_selected;
1730 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), TRUE);
1731 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1733 /*if no model (so no rows at all), then virtually we are the last*/
1734 if (!priv->header_model || !priv->row_reference)
1737 if (!gtk_tree_row_reference_valid (priv->row_reference))
1740 path = gtk_tree_row_reference_get_path (priv->row_reference);
1744 is_last_selected = TRUE;
1745 while (is_last_selected) {
1747 gtk_tree_path_next (path);
1748 if (!gtk_tree_model_get_iter (priv->header_model, &tmp_iter, path))
1750 gtk_tree_model_get (priv->header_model, &tmp_iter,
1751 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
1754 if (msg_is_visible (header, priv->is_outbox))
1755 is_last_selected = FALSE;
1756 g_object_unref(G_OBJECT(header));
1759 gtk_tree_path_free (path);
1760 return is_last_selected;
1764 modest_msg_view_window_has_headers_model (ModestMsgViewWindow *window)
1766 ModestMsgViewWindowPrivate *priv;
1768 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), TRUE);
1769 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1771 return priv->header_model != NULL;
1775 modest_msg_view_window_is_search_result (ModestMsgViewWindow *window)
1777 ModestMsgViewWindowPrivate *priv;
1779 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), TRUE);
1780 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1782 return priv->is_search_result;
1786 msg_is_visible (TnyHeader *header, gboolean check_outbox)
1788 if ((tny_header_get_flags(header) & TNY_HEADER_FLAG_DELETED))
1790 if (!check_outbox) {
1793 ModestTnySendQueueStatus status;
1794 status = modest_tny_all_send_queues_get_msg_status (header);
1795 return ((status != MODEST_TNY_SEND_QUEUE_FAILED) &&
1796 (status != MODEST_TNY_SEND_QUEUE_SENDING));
1801 modest_msg_view_window_first_message_selected (ModestMsgViewWindow *window)
1804 ModestMsgViewWindowPrivate *priv;
1805 gboolean is_first_selected;
1806 GtkTreeIter tmp_iter;
1808 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), TRUE);
1809 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1811 /*if no model (so no rows at all), then virtually we are the first*/
1812 if (!priv->header_model || !priv->row_reference)
1815 if (!gtk_tree_row_reference_valid (priv->row_reference))
1818 path = gtk_tree_row_reference_get_path (priv->row_reference);
1822 is_first_selected = TRUE;
1823 while (is_first_selected) {
1825 if(!gtk_tree_path_prev (path))
1827 /* Here the 'if' is needless for logic, but let make sure
1828 * iter is valid for gtk_tree_model_get. */
1829 if (!gtk_tree_model_get_iter (priv->header_model, &tmp_iter, path))
1831 gtk_tree_model_get (priv->header_model, &tmp_iter,
1832 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
1835 if (msg_is_visible (header, priv->is_outbox))
1836 is_first_selected = FALSE;
1837 g_object_unref(G_OBJECT(header));
1840 gtk_tree_path_free (path);
1841 return is_first_selected;
1846 GtkTreeRowReference *row_reference;
1850 message_reader_performer (gboolean canceled,
1852 GtkWindow *parent_window,
1853 TnyAccount *account,
1856 ModestMailOperation *mail_op = NULL;
1857 MsgReaderInfo *info;
1859 info = (MsgReaderInfo *) user_data;
1860 if (canceled || err) {
1864 /* Register the header - it'll be unregistered in the callback */
1865 modest_window_mgr_register_header (modest_runtime_get_window_mgr (), info->header, NULL);
1867 /* New mail operation */
1868 mail_op = modest_mail_operation_new_with_error_handling (G_OBJECT(parent_window),
1869 modest_ui_actions_disk_operations_error_handler,
1872 modest_mail_operation_queue_add (modest_runtime_get_mail_operation_queue (), mail_op);
1873 modest_mail_operation_get_msg (mail_op, info->header, TRUE, view_msg_cb, info->row_reference);
1874 g_object_unref (mail_op);
1876 /* Update dimming rules */
1877 modest_ui_actions_check_toolbar_dimming_rules (MODEST_WINDOW (parent_window));
1878 modest_ui_actions_check_menu_dimming_rules (MODEST_WINDOW (parent_window));
1881 /* Frees. The row_reference will be freed by the view_msg_cb callback */
1882 g_object_unref (info->header);
1883 g_slice_free (MsgReaderInfo, info);
1888 * Reads the message whose summary item is @header. It takes care of
1889 * several things, among others:
1891 * If the message was not previously downloaded then ask the user
1892 * before downloading. If there is no connection launch the connection
1893 * dialog. Update toolbar dimming rules.
1895 * Returns: TRUE if the mail operation was started, otherwise if the
1896 * user do not want to download the message, or if the user do not
1897 * want to connect, then the operation is not issued
1900 message_reader (ModestMsgViewWindow *window,
1901 ModestMsgViewWindowPrivate *priv,
1903 GtkTreeRowReference *row_reference)
1905 ModestWindowMgr *mgr;
1906 TnyAccount *account;
1908 MsgReaderInfo *info;
1910 g_return_val_if_fail (row_reference != NULL, FALSE);
1912 mgr = modest_runtime_get_window_mgr ();
1913 /* Msg download completed */
1914 if (!(tny_header_get_flags (header) & TNY_HEADER_FLAG_CACHED)) {
1915 /* Ask the user if he wants to download the message if
1917 if (!tny_device_is_online (modest_runtime_get_device())) {
1918 GtkResponseType response;
1920 response = modest_platform_run_confirmation_dialog (GTK_WINDOW (window),
1921 _("mcen_nc_get_msg"));
1922 if (response == GTK_RESPONSE_CANCEL)
1925 folder = tny_header_get_folder (header);
1926 info = g_slice_new (MsgReaderInfo);
1927 info->header = g_object_ref (header);
1928 info->row_reference = gtk_tree_row_reference_copy (row_reference);
1930 /* Offer the connection dialog if necessary */
1931 modest_platform_connect_if_remote_and_perform ((GtkWindow *) window,
1933 TNY_FOLDER_STORE (folder),
1934 message_reader_performer,
1936 g_object_unref (folder);
1941 folder = tny_header_get_folder (header);
1942 account = tny_folder_get_account (folder);
1943 info = g_slice_new (MsgReaderInfo);
1944 info->header = g_object_ref (header);
1945 info->row_reference = gtk_tree_row_reference_copy (row_reference);
1947 message_reader_performer (FALSE, NULL, (GtkWindow *) window, account, info);
1948 g_object_unref (account);
1949 g_object_unref (folder);
1955 modest_msg_view_window_select_next_message (ModestMsgViewWindow *window)
1957 ModestMsgViewWindowPrivate *priv;
1958 GtkTreePath *path= NULL;
1959 GtkTreeIter tmp_iter;
1961 gboolean retval = TRUE;
1962 GtkTreeRowReference *row_reference = NULL;
1964 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), FALSE);
1965 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1967 if (!priv->row_reference)
1970 /* Update the next row reference if it's not valid. This could
1971 happen if for example the header which it was pointing to,
1972 was deleted. The best place to do it is in the row-deleted
1973 handler but the tinymail model do not work like the glib
1974 tree models and reports the deletion when the row is still
1976 if (!gtk_tree_row_reference_valid (priv->next_row_reference)) {
1977 if (gtk_tree_row_reference_valid (priv->row_reference)) {
1978 priv->next_row_reference = gtk_tree_row_reference_copy (priv->row_reference);
1979 select_next_valid_row (priv->header_model, &(priv->next_row_reference), FALSE, priv->is_outbox);
1982 if (priv->next_row_reference)
1983 path = gtk_tree_row_reference_get_path (priv->next_row_reference);
1987 row_reference = gtk_tree_row_reference_copy (priv->next_row_reference);
1989 gtk_tree_model_get_iter (priv->header_model,
1992 gtk_tree_path_free (path);
1994 gtk_tree_model_get (priv->header_model, &tmp_iter,
1995 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
1998 /* Read the message & show it */
1999 if (!message_reader (window, priv, header, row_reference)) {
2002 gtk_tree_row_reference_free (row_reference);
2005 g_object_unref (header);
2011 modest_msg_view_window_select_previous_message (ModestMsgViewWindow *window)
2013 ModestMsgViewWindowPrivate *priv = NULL;
2015 gboolean finished = FALSE;
2016 gboolean retval = FALSE;
2018 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), FALSE);
2019 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
2021 /* Return inmediatly if there is no header model */
2022 if (!priv->header_model || !priv->row_reference)
2025 path = gtk_tree_row_reference_get_path (priv->row_reference);
2026 while (!finished && gtk_tree_path_prev (path)) {
2030 gtk_tree_model_get_iter (priv->header_model, &iter, path);
2031 gtk_tree_model_get (priv->header_model, &iter,
2032 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
2036 if (msg_is_visible (header, priv->is_outbox)) {
2037 GtkTreeRowReference *row_reference;
2038 row_reference = gtk_tree_row_reference_new (priv->header_model, path);
2039 /* Read the message & show it */
2040 retval = message_reader (window, priv, header, row_reference);
2041 gtk_tree_row_reference_free (row_reference);
2045 g_object_unref (header);
2049 gtk_tree_path_free (path);
2054 view_msg_cb (ModestMailOperation *mail_op,
2061 ModestMsgViewWindow *self = NULL;
2062 ModestMsgViewWindowPrivate *priv = NULL;
2063 GtkTreeRowReference *row_reference = NULL;
2065 /* Unregister the header (it was registered before creating the mail operation) */
2066 modest_window_mgr_unregister_header (modest_runtime_get_window_mgr (), header);
2068 row_reference = (GtkTreeRowReference *) user_data;
2070 gtk_tree_row_reference_free (row_reference);
2074 /* If there was any error */
2075 if (!modest_ui_actions_msg_retrieval_check (mail_op, header, msg)) {
2076 gtk_tree_row_reference_free (row_reference);
2080 /* Get the window */
2081 self = (ModestMsgViewWindow *) modest_mail_operation_get_source (mail_op);
2082 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self));
2083 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
2085 /* Update the row reference */
2086 if (priv->row_reference != NULL) {
2087 gtk_tree_row_reference_free (priv->row_reference);
2088 priv->row_reference = gtk_tree_row_reference_copy (row_reference);
2089 if (priv->next_row_reference != NULL) {
2090 gtk_tree_row_reference_free (priv->next_row_reference);
2092 priv->next_row_reference = gtk_tree_row_reference_copy (priv->row_reference);
2093 select_next_valid_row (priv->header_model, &(priv->next_row_reference), TRUE, priv->is_outbox);
2096 /* Mark header as read */
2097 if (!(tny_header_get_flags (header) & TNY_HEADER_FLAG_SEEN))
2098 tny_header_set_flag (header, TNY_HEADER_FLAG_SEEN);
2100 /* Set new message */
2101 if (priv->msg_view != NULL && TNY_IS_MSG_VIEW (priv->msg_view)) {
2102 tny_msg_view_set_msg (TNY_MSG_VIEW (priv->msg_view), msg);
2103 modest_msg_view_window_update_priority (self);
2104 update_window_title (MODEST_MSG_VIEW_WINDOW (self));
2105 modest_msg_view_grab_focus (MODEST_MSG_VIEW (priv->msg_view));
2108 /* Set the new message uid of the window */
2109 if (priv->msg_uid) {
2110 g_free (priv->msg_uid);
2111 priv->msg_uid = modest_tny_folder_get_header_unique_id (header);
2114 /* Notify the observers */
2115 g_signal_emit (G_OBJECT (self), signals[MSG_CHANGED_SIGNAL],
2116 0, priv->header_model, priv->row_reference);
2119 g_object_unref (self);
2120 gtk_tree_row_reference_free (row_reference);
2124 modest_msg_view_window_get_folder_type (ModestMsgViewWindow *window)
2126 ModestMsgViewWindowPrivate *priv;
2128 TnyFolderType folder_type;
2130 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
2132 folder_type = TNY_FOLDER_TYPE_UNKNOWN;
2134 msg = tny_msg_view_get_msg (TNY_MSG_VIEW (priv->msg_view));
2138 folder = tny_msg_get_folder (msg);
2140 folder_type = modest_tny_folder_guess_folder_type (folder);
2141 g_object_unref (folder);
2143 g_object_unref (msg);
2151 modest_msg_view_window_update_priority (ModestMsgViewWindow *window)
2153 ModestMsgViewWindowPrivate *priv;
2154 TnyHeader *header = NULL;
2155 TnyHeaderFlags flags = 0;
2157 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
2159 if (priv->header_model && priv->row_reference) {
2161 GtkTreePath *path = NULL;
2163 path = gtk_tree_row_reference_get_path (priv->row_reference);
2164 g_return_if_fail (path != NULL);
2165 gtk_tree_model_get_iter (priv->header_model,
2167 gtk_tree_row_reference_get_path (priv->row_reference));
2169 gtk_tree_model_get (priv->header_model, &iter, TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
2171 gtk_tree_path_free (path);
2174 msg = tny_msg_view_get_msg (TNY_MSG_VIEW (priv->msg_view));
2176 header = tny_msg_get_header (msg);
2177 g_object_unref (msg);
2182 flags = tny_header_get_flags (header);
2183 g_object_unref(G_OBJECT(header));
2186 modest_msg_view_set_priority (MODEST_MSG_VIEW(priv->msg_view), flags);
2191 toolbar_resize (ModestMsgViewWindow *self)
2193 ModestMsgViewWindowPrivate *priv = NULL;
2194 ModestWindowPrivate *parent_priv = NULL;
2196 gint static_button_size;
2197 ModestWindowMgr *mgr;
2199 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self));
2200 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
2201 parent_priv = MODEST_WINDOW_GET_PRIVATE(self);
2203 mgr = modest_runtime_get_window_mgr ();
2204 static_button_size = modest_window_mgr_get_fullscreen_mode (mgr)?118:108;
2206 if (parent_priv->toolbar) {
2207 /* left size buttons */
2208 widget = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/ToolbarMessageReply");
2209 gtk_tool_item_set_expand (GTK_TOOL_ITEM (widget), FALSE);
2210 gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (widget), FALSE);
2211 gtk_widget_set_size_request (GTK_WIDGET (widget), static_button_size, -1);
2212 widget = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/ToolbarMessageMoveTo");
2213 gtk_tool_item_set_expand (GTK_TOOL_ITEM (widget), FALSE);
2214 gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (widget), FALSE);
2215 gtk_widget_set_size_request (GTK_WIDGET (widget), static_button_size, -1);
2216 widget = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/ToolbarDeleteMessage");
2217 gtk_tool_item_set_expand (GTK_TOOL_ITEM (widget), FALSE);
2218 gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (widget), FALSE);
2219 gtk_widget_set_size_request (GTK_WIDGET (widget), static_button_size, -1);
2220 widget = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/FindInMessage");
2221 gtk_tool_item_set_expand (GTK_TOOL_ITEM (widget), FALSE);
2222 gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (widget), FALSE);
2223 gtk_widget_set_size_request (GTK_WIDGET (widget), static_button_size, -1);
2225 gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (priv->next_toolitem), TRUE);
2226 gtk_tool_item_set_expand (GTK_TOOL_ITEM (priv->next_toolitem), TRUE);
2227 gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (priv->prev_toolitem), TRUE);
2228 gtk_tool_item_set_expand (GTK_TOOL_ITEM (priv->prev_toolitem), TRUE);
2234 modest_msg_view_window_show_toolbar (ModestWindow *self,
2235 gboolean show_toolbar)
2237 ModestMsgViewWindowPrivate *priv = NULL;
2238 ModestWindowPrivate *parent_priv;
2239 GtkWidget *reply_button = NULL, *menu = NULL;
2241 parent_priv = MODEST_WINDOW_GET_PRIVATE(self);
2242 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(self);
2244 /* Set optimized view status */
2245 priv->optimized_view = !show_toolbar;
2247 if (!parent_priv->toolbar) {
2248 parent_priv->toolbar = gtk_ui_manager_get_widget (parent_priv->ui_manager,
2250 gtk_widget_set_no_show_all (parent_priv->toolbar, TRUE);
2252 priv->next_toolitem = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/ToolbarMessageNext");
2253 priv->prev_toolitem = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/ToolbarMessageBack");
2254 toolbar_resize (MODEST_MSG_VIEW_WINDOW (self));
2257 hildon_window_add_toolbar (HILDON_WINDOW (self),
2258 GTK_TOOLBAR (parent_priv->toolbar));
2260 /* Set reply button tap and hold menu */
2261 reply_button = gtk_ui_manager_get_widget (parent_priv->ui_manager,
2262 "/ToolBar/ToolbarMessageReply");
2263 menu = gtk_ui_manager_get_widget (parent_priv->ui_manager,
2264 "/ToolbarReplyCSM");
2265 if (menu && reply_button)
2266 gtk_widget_tap_and_hold_setup (GTK_WIDGET (reply_button), menu, NULL, 0);
2270 /* Quick hack: this prevents toolbar icons "dance" when progress bar show status is changed */
2271 /* TODO: resize mode migth be GTK_RESIZE_QUEUE, in order to avoid unneccesary shows */
2272 gtk_container_set_resize_mode (GTK_CONTAINER(parent_priv->toolbar), GTK_RESIZE_IMMEDIATE);
2274 gtk_widget_show (GTK_WIDGET (parent_priv->toolbar));
2275 if (modest_msg_view_window_transfer_mode_enabled (MODEST_MSG_VIEW_WINDOW (self)))
2276 set_progress_hint (MODEST_MSG_VIEW_WINDOW (self), TRUE);
2278 set_progress_hint (MODEST_MSG_VIEW_WINDOW (self), FALSE);
2281 gtk_widget_set_no_show_all (parent_priv->toolbar, TRUE);
2282 gtk_widget_hide (GTK_WIDGET (parent_priv->toolbar));
2287 modest_msg_view_window_clipboard_owner_change (GtkClipboard *clipboard,
2289 ModestMsgViewWindow *window)
2291 if (!GTK_WIDGET_VISIBLE (window))
2294 modest_window_check_dimming_rules_group (MODEST_WINDOW (window), MODEST_DIMMING_RULES_CLIPBOARD);
2298 modest_msg_view_window_transfer_mode_enabled (ModestMsgViewWindow *self)
2300 ModestMsgViewWindowPrivate *priv;
2302 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self), FALSE);
2303 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(self);
2305 return priv->progress_hint;
2309 observers_empty (ModestMsgViewWindow *self)
2312 ModestMsgViewWindowPrivate *priv;
2313 gboolean is_empty = TRUE;
2314 guint pending_ops = 0;
2316 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(self);
2317 tmp = priv->progress_widgets;
2319 /* Check all observers */
2320 while (tmp && is_empty) {
2321 pending_ops = modest_progress_object_num_pending_operations (MODEST_PROGRESS_OBJECT(tmp->data));
2322 is_empty = pending_ops == 0;
2324 tmp = g_slist_next(tmp);
2331 on_account_removed (TnyAccountStore *account_store,
2332 TnyAccount *account,
2335 /* Do nothing if it's a transport account, because we only
2336 show the messages of a store account */
2337 if (tny_account_get_account_type(account) == TNY_ACCOUNT_TYPE_STORE) {
2338 const gchar *parent_acc = NULL;
2339 const gchar *our_acc = NULL;
2341 our_acc = modest_window_get_active_account (MODEST_WINDOW (user_data));
2342 parent_acc = modest_tny_account_get_parent_modest_account_name_for_server_account (account);
2344 /* Close this window if I'm showing a message of the removed account */
2345 if (strcmp (parent_acc, our_acc) == 0)
2346 modest_ui_actions_on_close_window (NULL, MODEST_WINDOW (user_data));
2351 on_mail_operation_started (ModestMailOperation *mail_op,
2354 ModestMsgViewWindow *self;
2355 ModestMailOperationTypeOperation op_type;
2357 ModestMsgViewWindowPrivate *priv;
2358 GObject *source = NULL;
2360 self = MODEST_MSG_VIEW_WINDOW (user_data);
2361 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
2362 op_type = modest_mail_operation_get_type_operation (mail_op);
2363 tmp = priv->progress_widgets;
2364 source = modest_mail_operation_get_source(mail_op);
2365 if (G_OBJECT (self) == source) {
2366 if (op_type == MODEST_MAIL_OPERATION_TYPE_RECEIVE ) {
2367 set_toolbar_transfer_mode(self);
2369 modest_progress_object_add_operation (
2370 MODEST_PROGRESS_OBJECT (tmp->data),
2372 tmp = g_slist_next (tmp);
2376 g_object_unref (source);
2380 on_mail_operation_finished (ModestMailOperation *mail_op,
2383 ModestMsgViewWindow *self;
2384 ModestMailOperationTypeOperation op_type;
2386 ModestMsgViewWindowPrivate *priv;
2388 self = MODEST_MSG_VIEW_WINDOW (user_data);
2389 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
2390 op_type = modest_mail_operation_get_type_operation (mail_op);
2391 tmp = priv->progress_widgets;
2393 if (op_type == MODEST_MAIL_OPERATION_TYPE_RECEIVE ) {
2395 modest_progress_object_remove_operation (MODEST_PROGRESS_OBJECT (tmp->data),
2397 tmp = g_slist_next (tmp);
2400 /* If no more operations are being observed, NORMAL mode is enabled again */
2401 if (observers_empty (self)) {
2402 set_progress_hint (self, FALSE);
2405 /* Update dimming rules. We have to do this right here
2406 and not in view_msg_cb because at that point the
2407 transfer mode is still enabled so the dimming rule
2408 won't let the user delete the message that has been
2409 readed for example */
2410 modest_ui_actions_check_toolbar_dimming_rules (MODEST_WINDOW (self));
2411 modest_ui_actions_check_menu_dimming_rules (MODEST_WINDOW (self));
2416 on_queue_changed (ModestMailOperationQueue *queue,
2417 ModestMailOperation *mail_op,
2418 ModestMailOperationQueueNotification type,
2419 ModestMsgViewWindow *self)
2421 ModestMsgViewWindowPrivate *priv;
2423 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
2425 /* If this operations was created by another window, do nothing */
2426 if (!modest_mail_operation_is_mine (mail_op, G_OBJECT(self)))
2429 if (type == MODEST_MAIL_OPERATION_QUEUE_OPERATION_ADDED) {
2430 priv->sighandlers = modest_signal_mgr_connect (priv->sighandlers,
2432 "operation-started",
2433 G_CALLBACK (on_mail_operation_started),
2435 priv->sighandlers = modest_signal_mgr_connect (priv->sighandlers,
2437 "operation-finished",
2438 G_CALLBACK (on_mail_operation_finished),
2440 } else if (type == MODEST_MAIL_OPERATION_QUEUE_OPERATION_REMOVED) {
2441 priv->sighandlers = modest_signal_mgr_disconnect (priv->sighandlers,
2443 "operation-started");
2444 priv->sighandlers = modest_signal_mgr_disconnect (priv->sighandlers,
2446 "operation-finished");
2451 modest_msg_view_window_get_attachments (ModestMsgViewWindow *win)
2453 ModestMsgViewWindowPrivate *priv;
2454 TnyList *selected_attachments = NULL;
2456 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (win), NULL);
2457 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (win);
2459 selected_attachments = modest_msg_view_get_selected_attachments (MODEST_MSG_VIEW (priv->msg_view));
2461 return selected_attachments;
2467 guint banner_idle_id;
2468 } DecodeAsyncHelper;
2471 decode_async_banner_idle (gpointer user_data)
2473 DecodeAsyncHelper *helper = (DecodeAsyncHelper *) user_data;
2475 helper->banner_idle_id = 0;
2476 helper->banner = hildon_banner_show_animation (NULL, NULL, _("mail_me_opening"));
2477 g_object_ref (helper->banner);
2483 on_decode_to_stream_async_handler (TnyMimePart *mime_part,
2489 DecodeAsyncHelper *helper = (DecodeAsyncHelper *) user_data;
2491 if (helper->banner_idle_id > 0) {
2492 g_source_remove (helper->banner_idle_id);
2493 helper->banner_idle_id = 0;
2495 if (helper->banner) {
2496 gtk_widget_destroy (helper->banner);
2498 if (cancelled || err) {
2499 modest_platform_information_banner (NULL, NULL,
2500 _("mail_ib_file_operation_failed"));
2504 /* make the file read-only */
2505 g_chmod(helper->filepath, 0444);
2507 /* Activate the file */
2508 modest_platform_activate_file (helper->filepath, tny_mime_part_get_content_type (mime_part));
2512 g_free (helper->filepath);
2513 g_object_unref (helper->banner);
2514 g_slice_free (DecodeAsyncHelper, helper);
2518 modest_msg_view_window_view_attachment (ModestMsgViewWindow *window,
2519 TnyMimePart *mime_part)
2521 ModestMsgViewWindowPrivate *priv;
2522 const gchar *msg_uid;
2523 gchar *attachment_uid = NULL;
2524 gint attachment_index = 0;
2525 TnyList *attachments;
2527 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window));
2528 g_return_if_fail (TNY_IS_MIME_PART (mime_part) || (mime_part == NULL));
2529 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
2531 msg_uid = modest_msg_view_window_get_message_uid (MODEST_MSG_VIEW_WINDOW (window));
2532 attachments = modest_msg_view_get_attachments (MODEST_MSG_VIEW (priv->msg_view));
2533 attachment_index = modest_list_index (attachments, (GObject *) mime_part);
2534 g_object_unref (attachments);
2536 if (msg_uid && attachment_index >= 0) {
2537 attachment_uid = g_strdup_printf ("%s/%d", msg_uid, attachment_index);
2540 if (mime_part == NULL) {
2541 gboolean error = FALSE;
2542 TnyList *selected_attachments = modest_msg_view_get_selected_attachments (MODEST_MSG_VIEW (priv->msg_view));
2543 if (selected_attachments == NULL || tny_list_get_length (selected_attachments) == 0) {
2545 } else if (tny_list_get_length (selected_attachments) > 1) {
2546 hildon_banner_show_information (NULL, NULL, _("mcen_ib_unable_to_display_more"));
2550 iter = tny_list_create_iterator (selected_attachments);
2551 mime_part = (TnyMimePart *) tny_iterator_get_current (iter);
2552 g_object_unref (iter);
2554 g_object_unref (selected_attachments);
2559 g_object_ref (mime_part);
2562 if (tny_mime_part_is_purged (mime_part)) {
2563 g_object_unref (mime_part);
2567 if (!modest_tny_mime_part_is_msg (mime_part)) {
2568 gchar *filepath = NULL;
2569 const gchar *att_filename = tny_mime_part_get_filename (mime_part);
2570 gboolean show_error_banner = FALSE;
2571 TnyFsStream *temp_stream = NULL;
2572 temp_stream = modest_utils_create_temp_stream (att_filename, attachment_uid,
2575 if (temp_stream != NULL) {
2576 DecodeAsyncHelper *helper = g_slice_new (DecodeAsyncHelper);
2577 helper->filepath = g_strdup (filepath);
2578 helper->banner = NULL;
2579 helper->banner_idle_id = g_timeout_add (1000, decode_async_banner_idle, helper);
2580 tny_mime_part_decode_to_stream_async (mime_part, TNY_STREAM (temp_stream),
2581 on_decode_to_stream_async_handler,
2584 g_object_unref (temp_stream);
2585 /* NOTE: files in the temporary area will be automatically
2586 * cleaned after some time if they are no longer in use */
2589 const gchar *content_type;
2590 /* the file may already exist but it isn't writable,
2591 * let's try to open it anyway */
2592 content_type = tny_mime_part_get_content_type (mime_part);
2593 modest_platform_activate_file (filepath, content_type);
2595 g_warning ("%s: modest_utils_create_temp_stream failed", __FUNCTION__);
2596 show_error_banner = TRUE;
2601 if (show_error_banner)
2602 modest_platform_information_banner (NULL, NULL, _("mail_ib_file_operation_failed"));
2604 /* message attachment */
2605 TnyHeader *header = NULL;
2606 ModestWindowMgr *mgr;
2607 ModestWindow *msg_win = NULL;
2610 header = tny_msg_get_header (TNY_MSG (mime_part));
2611 mgr = modest_runtime_get_window_mgr ();
2612 found = modest_window_mgr_find_registered_header (mgr, header, &msg_win);
2615 /* if it's found, but there is no msg_win, it's probably in the process of being created;
2616 * thus, we don't do anything */
2617 g_warning ("window for is already being created");
2619 /* it's not found, so create a new window for it */
2620 modest_window_mgr_register_header (mgr, header, attachment_uid); /* register the uid before building the window */
2621 gchar *account = g_strdup (modest_window_get_active_account (MODEST_WINDOW (window)));
2623 account = modest_account_mgr_get_default_account (modest_runtime_get_account_mgr ());
2624 msg_win = modest_msg_view_window_new_for_attachment (TNY_MSG (mime_part), account, attachment_uid);
2625 modest_window_set_zoom (MODEST_WINDOW (msg_win),
2626 modest_window_get_zoom (MODEST_WINDOW (window)));
2627 modest_window_mgr_register_window (mgr, msg_win, MODEST_WINDOW (window));
2628 gtk_widget_show_all (GTK_WIDGET (msg_win));
2631 g_object_unref (mime_part);
2644 GnomeVFSResult result;
2647 static void save_mime_part_info_free (SaveMimePartInfo *info, gboolean with_struct);
2648 static gboolean idle_save_mime_part_show_result (SaveMimePartInfo *info);
2649 static gpointer save_mime_part_to_file (SaveMimePartInfo *info);
2650 static void save_mime_parts_to_file_with_checks (SaveMimePartInfo *info);
2653 save_mime_part_info_free (SaveMimePartInfo *info, gboolean with_struct)
2657 for (node = info->pairs; node != NULL; node = g_list_next (node)) {
2658 SaveMimePartPair *pair = (SaveMimePartPair *) node->data;
2659 g_free (pair->filename);
2660 g_object_unref (pair->part);
2661 g_slice_free (SaveMimePartPair, pair);
2663 g_list_free (info->pairs);
2666 gtk_widget_destroy (info->banner);
2667 g_slice_free (SaveMimePartInfo, info);
2672 idle_save_mime_part_show_result (SaveMimePartInfo *info)
2674 if (info->pairs != NULL) {
2675 save_mime_part_to_file (info);
2677 /* This is a GDK lock because we are an idle callback and
2678 * hildon_banner_show_information is or does Gtk+ code */
2680 gdk_threads_enter (); /* CHECKED */
2681 save_mime_part_info_free (info, TRUE);
2682 if (info->result == GNOME_VFS_OK) {
2683 hildon_banner_show_information (NULL, NULL, _CS("sfil_ib_saved"));
2684 } else if (info->result == GNOME_VFS_ERROR_NO_SPACE) {
2685 hildon_banner_show_information (NULL, NULL, dgettext("ke-recv",
2686 "cerm_device_memory_full"));
2688 hildon_banner_show_information (NULL, NULL, _("mail_ib_file_operation_failed"));
2690 gdk_threads_leave (); /* CHECKED */
2697 save_mime_part_to_file (SaveMimePartInfo *info)
2699 GnomeVFSHandle *handle;
2701 SaveMimePartPair *pair = (SaveMimePartPair *) info->pairs->data;
2703 info->result = gnome_vfs_create (&handle, pair->filename, GNOME_VFS_OPEN_WRITE, FALSE, 0644);
2704 if (info->result == GNOME_VFS_OK) {
2705 GError *error = NULL;
2706 stream = tny_vfs_stream_new (handle);
2707 if (tny_mime_part_decode_to_stream (pair->part, stream, &error) < 0) {
2708 g_warning ("modest: could not save attachment %s: %d (%s)\n", pair->filename, error?error->code:-1, error?error->message:"Unknown error");
2710 info->result = GNOME_VFS_ERROR_IO;
2712 g_object_unref (G_OBJECT (stream));
2713 g_object_unref (pair->part);
2714 g_slice_free (SaveMimePartPair, pair);
2715 info->pairs = g_list_delete_link (info->pairs, info->pairs);
2717 g_warning ("modest: could not create save attachment %s: %s\n", pair->filename, gnome_vfs_result_to_string (info->result));
2718 save_mime_part_info_free (info, FALSE);
2721 g_idle_add ((GSourceFunc) idle_save_mime_part_show_result, info);
2726 save_mime_parts_to_file_with_checks (SaveMimePartInfo *info)
2728 gboolean is_ok = TRUE;
2729 gint replaced_files = 0;
2730 const GList *files = info->pairs;
2733 for (iter = files; (iter != NULL) && (replaced_files < 2); iter = g_list_next(iter)) {
2734 SaveMimePartPair *pair = iter->data;
2735 if (modest_utils_file_exists (pair->filename)) {
2739 if (replaced_files) {
2740 GtkWidget *confirm_overwrite_dialog;
2741 const gchar *message = (replaced_files == 1) ?
2742 _FM("docm_nc_replace_file") : _FM("docm_nc_replace_multiple");
2743 confirm_overwrite_dialog = hildon_note_new_confirmation (NULL, message);
2744 if (gtk_dialog_run (GTK_DIALOG (confirm_overwrite_dialog)) != GTK_RESPONSE_OK) {
2747 gtk_widget_destroy (confirm_overwrite_dialog);
2751 save_mime_part_info_free (info, TRUE);
2753 GtkWidget *banner = hildon_banner_show_animation (NULL, NULL,
2754 _CS("sfil_ib_saving"));
2755 info->banner = banner;
2756 g_thread_create ((GThreadFunc)save_mime_part_to_file, info, FALSE, NULL);
2762 save_attachments_response (GtkDialog *dialog,
2766 TnyList *mime_parts;
2768 GList *files_to_save = NULL;
2770 mime_parts = TNY_LIST (user_data);
2772 if (arg1 != GTK_RESPONSE_OK)
2775 chooser_uri = gtk_file_chooser_get_uri (GTK_FILE_CHOOSER (dialog));
2777 if (!modest_utils_folder_writable (chooser_uri)) {
2778 hildon_banner_show_information
2779 (NULL, NULL, dgettext("hildon-fm", "sfil_ib_readonly_location"));
2783 iter = tny_list_create_iterator (mime_parts);
2784 while (!tny_iterator_is_done (iter)) {
2785 TnyMimePart *mime_part = (TnyMimePart *) tny_iterator_get_current (iter);
2787 if ((modest_tny_mime_part_is_attachment_for_modest (mime_part)) &&
2788 !tny_mime_part_is_purged (mime_part) &&
2789 (tny_mime_part_get_filename (mime_part) != NULL)) {
2790 SaveMimePartPair *pair;
2792 pair = g_slice_new0 (SaveMimePartPair);
2794 if (tny_list_get_length (mime_parts) > 1) {
2796 gnome_vfs_escape_slashes (tny_mime_part_get_filename (mime_part));
2797 pair->filename = g_build_filename (chooser_uri, escaped, NULL);
2800 pair->filename = g_strdup (chooser_uri);
2802 pair->part = mime_part;
2803 files_to_save = g_list_prepend (files_to_save, pair);
2805 tny_iterator_next (iter);
2807 g_object_unref (iter);
2809 g_free (chooser_uri);
2811 if (files_to_save != NULL) {
2812 SaveMimePartInfo *info = g_slice_new0 (SaveMimePartInfo);
2813 info->pairs = files_to_save;
2814 info->result = TRUE;
2815 save_mime_parts_to_file_with_checks (info);
2819 /* Free and close the dialog */
2820 g_object_unref (mime_parts);
2821 gtk_widget_destroy (GTK_WIDGET (dialog));
2825 modest_msg_view_window_save_attachments (ModestMsgViewWindow *window, TnyList *mime_parts)
2827 ModestMsgViewWindowPrivate *priv;
2828 GtkWidget *save_dialog = NULL;
2829 gchar *folder = NULL;
2830 gchar *filename = NULL;
2831 gchar *save_multiple_str = NULL;
2833 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window));
2834 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
2836 if (mime_parts == NULL) {
2837 mime_parts = modest_msg_view_get_selected_attachments (MODEST_MSG_VIEW (priv->msg_view));
2838 if (mime_parts == NULL || tny_list_get_length (mime_parts) == 0)
2841 g_object_ref (mime_parts);
2844 /* prepare dialog */
2845 if (tny_list_get_length (mime_parts) == 1) {
2847 /* only one attachment selected */
2848 iter = tny_list_create_iterator (mime_parts);
2849 TnyMimePart *mime_part = (TnyMimePart *) tny_iterator_get_current (iter);
2850 g_object_unref (iter);
2851 if (!modest_tny_mime_part_is_msg (mime_part) &&
2852 modest_tny_mime_part_is_attachment_for_modest (mime_part) &&
2853 !tny_mime_part_is_purged (mime_part)) {
2854 filename = g_strdup (tny_mime_part_get_filename (mime_part));
2856 /* TODO: show any error? */
2857 g_warning ("Tried to save a non-file attachment");
2858 g_object_unref (mime_parts);
2861 g_object_unref (mime_part);
2863 save_multiple_str = g_strdup_printf (_FM("sfil_va_number_of_objects_attachments"),
2864 tny_list_get_length (mime_parts));
2867 save_dialog = hildon_file_chooser_dialog_new (GTK_WINDOW (window),
2868 GTK_FILE_CHOOSER_ACTION_SAVE);
2871 folder = g_build_filename (g_get_home_dir (), g_getenv (MYDOCS_ENV), DOCS_FOLDER, NULL);
2872 gtk_file_chooser_set_current_folder (GTK_FILE_CHOOSER (save_dialog), folder);
2877 gtk_file_chooser_set_current_name (GTK_FILE_CHOOSER (save_dialog),
2882 /* if multiple, set multiple string */
2883 if (save_multiple_str) {
2884 g_object_set (G_OBJECT (save_dialog), "save-multiple", save_multiple_str, NULL);
2885 gtk_window_set_title (GTK_WINDOW (save_dialog), _FM("sfil_ti_save_objects_files"));
2888 /* We must run this asynchronously, because the hildon dialog
2889 performs a gtk_dialog_run by itself which leads to gdk
2891 g_signal_connect (save_dialog, "response",
2892 G_CALLBACK (save_attachments_response), mime_parts);
2894 gtk_widget_show_all (save_dialog);
2898 show_remove_attachment_information (gpointer userdata)
2900 ModestMsgViewWindow *window = (ModestMsgViewWindow *) userdata;
2901 ModestMsgViewWindowPrivate *priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
2903 /* We're outside the main lock */
2904 gdk_threads_enter ();
2906 if (priv->remove_attachment_banner != NULL) {
2907 gtk_widget_destroy (priv->remove_attachment_banner);
2908 g_object_unref (priv->remove_attachment_banner);
2911 priv->remove_attachment_banner = g_object_ref (
2912 hildon_banner_show_animation (NULL, NULL, _("mcen_me_inbox_remove_attachments")));
2914 gdk_threads_leave ();
2920 modest_msg_view_window_remove_attachments (ModestMsgViewWindow *window, gboolean get_all)
2922 ModestMsgViewWindowPrivate *priv;
2923 TnyList *mime_parts = NULL;
2924 gchar *confirmation_message;
2930 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window));
2931 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
2934 mime_parts = modest_msg_view_get_attachments (MODEST_MSG_VIEW (priv->msg_view));
2936 mime_parts = modest_msg_view_get_selected_attachments (MODEST_MSG_VIEW (priv->msg_view));
2938 /* Remove already purged messages from mime parts list */
2939 iter = tny_list_create_iterator (mime_parts);
2940 while (!tny_iterator_is_done (iter)) {
2941 TnyMimePart *part = TNY_MIME_PART (tny_iterator_get_current (iter));
2942 tny_iterator_next (iter);
2943 if (tny_mime_part_is_purged (part)) {
2944 tny_list_remove (mime_parts, (GObject *) part);
2946 g_object_unref (part);
2948 g_object_unref (iter);
2950 if (tny_list_get_length (mime_parts) == 0) {
2951 g_object_unref (mime_parts);
2955 n_attachments = tny_list_get_length (mime_parts);
2956 if (n_attachments == 1) {
2960 iter = tny_list_create_iterator (mime_parts);
2961 part = (TnyMimePart *) tny_iterator_get_current (iter);
2962 g_object_unref (iter);
2963 if (modest_tny_mime_part_is_msg (part)) {
2965 header = tny_msg_get_header (TNY_MSG (part));
2966 filename = tny_header_dup_subject (header);
2967 g_object_unref (header);
2968 if (filename == NULL)
2969 filename = g_strdup (_("mail_va_no_subject"));
2971 filename = g_strdup (tny_mime_part_get_filename (TNY_MIME_PART (part)));
2973 confirmation_message = g_strdup_printf (_("mcen_nc_purge_file_text"), filename);
2975 g_object_unref (part);
2977 confirmation_message = g_strdup_printf (ngettext("mcen_nc_purge_file_text",
2978 "mcen_nc_purge_files_text",
2979 n_attachments), n_attachments);
2981 response = modest_platform_run_confirmation_dialog (GTK_WINDOW (window),
2982 confirmation_message);
2983 g_free (confirmation_message);
2985 if (response != GTK_RESPONSE_OK) {
2986 g_object_unref (mime_parts);
2990 priv->purge_timeout = g_timeout_add (2000, show_remove_attachment_information, window);
2992 iter = tny_list_create_iterator (mime_parts);
2993 while (!tny_iterator_is_done (iter)) {
2996 part = (TnyMimePart *) tny_iterator_get_current (iter);
2997 tny_mime_part_set_purged (TNY_MIME_PART (part));
2998 g_object_unref (part);
2999 tny_iterator_next (iter);
3001 g_object_unref (iter);
3003 msg = tny_msg_view_get_msg (TNY_MSG_VIEW (priv->msg_view));
3004 tny_msg_view_clear (TNY_MSG_VIEW (priv->msg_view));
3005 tny_msg_rewrite_cache (msg);
3006 tny_msg_view_set_msg (TNY_MSG_VIEW (priv->msg_view), msg);
3007 g_object_unref (msg);
3009 g_object_unref (mime_parts);
3011 if (priv->purge_timeout > 0) {
3012 g_source_remove (priv->purge_timeout);
3013 priv->purge_timeout = 0;
3016 if (priv->remove_attachment_banner) {
3017 gtk_widget_destroy (priv->remove_attachment_banner);
3018 g_object_unref (priv->remove_attachment_banner);
3019 priv->remove_attachment_banner = NULL;
3027 update_window_title (ModestMsgViewWindow *window)
3029 ModestMsgViewWindowPrivate *priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
3031 TnyHeader *header = NULL;
3032 gchar *subject = NULL;
3034 msg = tny_msg_view_get_msg (TNY_MSG_VIEW (priv->msg_view));
3037 header = tny_msg_get_header (msg);
3038 subject = tny_header_dup_subject (header);
3039 g_object_unref (header);
3040 g_object_unref (msg);
3043 if ((subject == NULL)||(subject[0] == '\0')) {
3045 subject = g_strdup (_("mail_va_no_subject"));
3048 gtk_window_set_title (GTK_WINDOW (window), subject);
3052 static void on_move_focus (GtkWidget *widget,
3053 GtkDirectionType direction,
3056 g_signal_stop_emission_by_name (G_OBJECT (widget), "move-focus");
3060 fetch_image_open_stream (TnyStreamCache *self, gint64 *expected_size, gchar *uri)
3062 GnomeVFSResult result;
3063 GnomeVFSHandle *handle = NULL;
3064 GnomeVFSFileInfo *info = NULL;
3067 result = gnome_vfs_open (&handle, uri, GNOME_VFS_OPEN_READ);
3068 if (result != GNOME_VFS_OK) {
3073 info = gnome_vfs_file_info_new ();
3074 result = gnome_vfs_get_file_info_from_handle (handle, info, GNOME_VFS_FILE_INFO_DEFAULT);
3075 if (result != GNOME_VFS_OK || ! (info->valid_fields & GNOME_VFS_FILE_INFO_FIELDS_SIZE)) {
3076 /* We put a "safe" default size for going to cache */
3077 *expected_size = (300*1024);
3079 *expected_size = info->size;
3081 gnome_vfs_file_info_unref (info);
3083 stream = tny_vfs_stream_new (handle);
3092 TnyStream *output_stream;
3093 GtkWidget *msg_view;
3097 on_fetch_image_idle_refresh_view (gpointer userdata)
3100 FetchImageData *fidata = (FetchImageData *) userdata;
3101 g_message ("REFRESH VIEW");
3102 if (GTK_WIDGET_DRAWABLE (fidata->msg_view)) {
3103 g_message ("QUEUING DRAW");
3104 gtk_widget_queue_draw (fidata->msg_view);
3106 g_object_unref (fidata->msg_view);
3107 g_slice_free (FetchImageData, fidata);
3112 on_fetch_image_thread (gpointer userdata)
3114 FetchImageData *fidata = (FetchImageData *) userdata;
3115 TnyStreamCache *cache;
3116 TnyStream *cache_stream;
3118 cache = modest_runtime_get_images_cache ();
3119 cache_stream = tny_stream_cache_get_stream (cache, fidata->cache_id, (TnyStreamCacheOpenStreamFetcher) fetch_image_open_stream, (gpointer) fidata->uri);
3120 g_free (fidata->cache_id);
3121 g_free (fidata->uri);
3123 if (cache_stream != NULL) {
3124 tny_stream_write_to_stream (cache_stream, fidata->output_stream);
3125 tny_stream_close (cache_stream);
3126 g_object_unref (cache_stream);
3129 tny_stream_close (fidata->output_stream);
3130 g_object_unref (fidata->output_stream);
3133 gdk_threads_enter ();
3134 g_idle_add (on_fetch_image_idle_refresh_view, fidata);
3135 gdk_threads_leave ();
3141 on_fetch_image (ModestMsgView *msgview,
3144 ModestMsgViewWindow *window)
3146 const gchar *current_account;
3147 ModestMsgViewWindowPrivate *priv;
3148 FetchImageData *fidata;
3150 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
3152 current_account = modest_window_get_active_account (MODEST_WINDOW (window));
3154 fidata = g_slice_new0 (FetchImageData);
3155 fidata->msg_view = g_object_ref (msgview);
3156 fidata->uri = g_strdup (uri);
3157 fidata->cache_id = modest_images_cache_get_id (current_account, uri);
3158 fidata->output_stream = g_object_ref (stream);
3160 if (g_thread_create (on_fetch_image_thread, fidata, FALSE, NULL) == NULL) {
3161 g_object_unref (fidata->output_stream);
3162 g_free (fidata->cache_id);
3163 g_free (fidata->uri);
3164 g_object_unref (fidata->msg_view);
3165 g_slice_free (FetchImageData, fidata);
3166 tny_stream_close (stream);
3174 modest_msg_view_window_add_to_contacts (ModestMsgViewWindow *self)
3176 ModestMsgViewWindowPrivate *priv;
3177 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
3178 GSList *recipients = NULL;
3180 gboolean contacts_to_add = FALSE;
3182 msg = tny_msg_view_get_msg (TNY_MSG_VIEW (priv->msg_view));
3183 if (msg == NULL) return;
3184 recipients = modest_tny_msg_get_all_recipients_list (msg);
3186 if (recipients != NULL) {
3187 GtkWidget *picker_dialog;
3188 GtkWidget *selector;
3191 selector = hildon_touch_selector_new_text ();
3192 for (node = recipients; node != NULL; node = g_slist_next (node)) {
3193 if (!modest_address_book_has_address ((const gchar *) node->data)) {
3194 hildon_touch_selector_append_text (HILDON_TOUCH_SELECTOR (selector),
3195 (const gchar *) node->data);
3196 contacts_to_add = TRUE;
3200 if (contacts_to_add) {
3202 picker_dialog = hildon_picker_dialog_new (GTK_WINDOW (self));
3203 gtk_window_set_title (GTK_WINDOW (picker_dialog), _("mcen_me_viewer_addtocontacts"));
3205 hildon_picker_dialog_set_selector (HILDON_PICKER_DIALOG (picker_dialog),
3206 HILDON_TOUCH_SELECTOR (selector));
3208 gtk_dialog_run (GTK_DIALOG (picker_dialog));
3209 selected = hildon_touch_selector_get_current_text (HILDON_TOUCH_SELECTOR (selector));
3210 gtk_widget_destroy (picker_dialog);
3213 modest_address_book_add_address (selected);
3218 g_object_unref (selector);
3223 if (recipients) {g_slist_foreach (recipients, (GFunc) g_free, NULL); g_slist_free (recipients);}
3224 g_object_unref (msg);
3228 add_to_menu (ModestMsgViewWindow *self,
3229 HildonAppMenu *menu,
3232 ModestDimmingRulesGroup *dimming_group,
3233 GCallback dimming_callback)
3237 button = gtk_button_new_with_label (label);
3238 g_signal_connect_after (G_OBJECT (button), "clicked",
3239 callback, (gpointer) self);
3240 modest_dimming_rules_group_add_widget_rule (dimming_group,
3243 MODEST_WINDOW (self));
3244 hildon_app_menu_append (menu, GTK_BUTTON (button));
3248 setup_menu (ModestMsgViewWindow *self, ModestDimmingRulesGroup *group)
3250 ModestMsgViewWindowPrivate *priv = NULL;
3251 GtkWidget *app_menu;
3253 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW(self));
3255 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
3257 app_menu = hildon_app_menu_new ();
3259 /* Settings menu buttons */
3260 add_to_menu (self, HILDON_APP_MENU (app_menu), _("mcen_me_inbox_reply"),
3261 G_CALLBACK (modest_ui_actions_on_reply),
3262 group, G_CALLBACK (modest_ui_dimming_rules_on_reply_msg));
3263 add_to_menu (self, HILDON_APP_MENU (app_menu), _("mcen_me_inbox_replytoall"),
3264 G_CALLBACK (modest_ui_actions_on_reply_all),
3265 group, G_CALLBACK (modest_ui_dimming_rules_on_reply_msg));
3266 add_to_menu (self, HILDON_APP_MENU (app_menu), _("mcen_me_inbox_forward"),
3267 G_CALLBACK (modest_ui_actions_on_forward),
3268 group, G_CALLBACK (modest_ui_dimming_rules_on_reply_msg));
3269 add_to_menu (self, HILDON_APP_MENU (app_menu), _("mcen_me_viewer_newemail"),
3270 G_CALLBACK (modest_ui_actions_on_new_msg),
3271 group, G_CALLBACK (modest_ui_dimming_rules_on_new_msg));
3272 add_to_menu (self, HILDON_APP_MENU (app_menu), _("mcen_me_inbox_messagedetails"),
3273 G_CALLBACK (modest_ui_actions_on_details),
3274 group, G_CALLBACK (modest_ui_dimming_rules_on_details));
3275 add_to_menu (self, HILDON_APP_MENU (app_menu), _("mcen_me_viewer_save_attachments"),
3276 G_CALLBACK (modest_ui_actions_save_attachments),
3277 group, G_CALLBACK (modest_ui_dimming_rules_on_save_attachments));
3278 add_to_menu (self, HILDON_APP_MENU (app_menu), _("mcen_me_inbox_remove_attachments"),
3279 G_CALLBACK (modest_ui_actions_remove_attachments),
3280 group, G_CALLBACK (modest_ui_dimming_rules_on_remove_attachments));
3281 add_to_menu (self, HILDON_APP_MENU (app_menu), _("mcen_me_viewer_addtocontacts"),
3282 G_CALLBACK (modest_ui_actions_on_add_to_contacts),
3283 group, G_CALLBACK (modest_ui_dimming_rules_on_add_to_contacts));
3285 hildon_stackable_window_set_main_menu (HILDON_STACKABLE_WINDOW (self),
3286 HILDON_APP_MENU (app_menu));
3290 modest_msg_view_window_toggle_menu (HildonWindow *window,
3294 modest_ui_actions_check_menu_dimming_rules (MODEST_WINDOW (window));
3296 return HILDON_WINDOW_CLASS (parent_class)->toggle_menu (window, button, time);