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-app-menu.h>
53 #include "modest-defs.h"
54 #include "modest-hildon-includes.h"
55 #include "modest-ui-dimming-manager.h"
56 #include <gdk/gdkkeysyms.h>
57 #include <modest-tny-account.h>
58 #include <modest-mime-part-view.h>
59 #include <modest-isearch-view.h>
60 #include <modest-tny-mime-part.h>
63 #include <glib/gstdio.h>
64 #include <modest-debug.h>
65 #include <modest-header-window.h>
67 #define DEFAULT_FOLDER "MyDocs/.documents"
69 typedef struct _ModestMsgViewWindowPrivate ModestMsgViewWindowPrivate;
70 struct _ModestMsgViewWindowPrivate {
73 GtkWidget *main_scroll;
74 GtkWidget *find_toolbar;
77 /* Progress observers */
78 GSList *progress_widgets;
81 GtkWidget *prev_toolitem;
82 GtkWidget *next_toolitem;
83 gboolean progress_hint;
85 /* Optimized view enabled */
86 gboolean optimized_view;
88 /* Whether this was created via the *_new_for_search_result() function. */
89 gboolean is_search_result;
91 /* Whether the message is in outbox */
94 /* A reference to the @model of the header view
95 * to allow selecting previous/next messages,
96 * if the message is currently selected in the header view.
98 const gchar *header_folder_id;
99 GtkTreeModel *header_model;
100 GtkTreeRowReference *row_reference;
101 GtkTreeRowReference *next_row_reference;
103 gulong clipboard_change_handler;
104 gulong queue_change_handler;
105 gulong account_removed_handler;
106 gulong row_changed_handler;
107 gulong row_deleted_handler;
108 gulong row_inserted_handler;
109 gulong rows_reordered_handler;
112 GtkWidget *remove_attachment_banner;
119 static void modest_msg_view_window_class_init (ModestMsgViewWindowClass *klass);
120 static void modest_msg_view_window_init (ModestMsgViewWindow *obj);
121 static void modest_header_view_observer_init(
122 ModestHeaderViewObserverIface *iface_class);
123 static void modest_msg_view_window_finalize (GObject *obj);
124 static void modest_msg_view_window_toggle_find_toolbar (GtkToggleAction *obj,
126 static void modest_msg_view_window_find_toolbar_close (GtkWidget *widget,
127 ModestMsgViewWindow *obj);
128 static void modest_msg_view_window_find_toolbar_search (GtkWidget *widget,
129 ModestMsgViewWindow *obj);
131 static void modest_msg_view_window_disconnect_signals (ModestWindow *self);
133 static gdouble modest_msg_view_window_get_zoom (ModestWindow *window);
134 static gboolean modest_msg_view_window_key_event (GtkWidget *window,
137 static gboolean modest_msg_view_window_toggle_menu (HildonWindow *window,
140 static void modest_msg_view_window_update_priority (ModestMsgViewWindow *window);
142 static void modest_msg_view_window_show_toolbar (ModestWindow *window,
143 gboolean show_toolbar);
145 static void modest_msg_view_window_clipboard_owner_change (GtkClipboard *clipboard,
147 ModestMsgViewWindow *window);
149 static void modest_msg_view_window_on_row_changed (GtkTreeModel *header_model,
152 ModestMsgViewWindow *window);
154 static void modest_msg_view_window_on_row_deleted (GtkTreeModel *header_model,
156 ModestMsgViewWindow *window);
158 static void modest_msg_view_window_on_row_inserted (GtkTreeModel *header_model,
159 GtkTreePath *tree_path,
160 GtkTreeIter *tree_iter,
161 ModestMsgViewWindow *window);
163 static void modest_msg_view_window_on_row_reordered (GtkTreeModel *header_model,
167 ModestMsgViewWindow *window);
169 static void modest_msg_view_window_update_model_replaced (ModestHeaderViewObserver *window,
171 const gchar *tny_folder_id);
173 static void on_queue_changed (ModestMailOperationQueue *queue,
174 ModestMailOperation *mail_op,
175 ModestMailOperationQueueNotification type,
176 ModestMsgViewWindow *self);
178 static void on_account_removed (TnyAccountStore *account_store,
182 static void on_move_focus (GtkWidget *widget,
183 GtkDirectionType direction,
186 static void view_msg_cb (ModestMailOperation *mail_op,
193 static void set_progress_hint (ModestMsgViewWindow *self,
196 static void update_window_title (ModestMsgViewWindow *window);
198 static gboolean set_toolbar_transfer_mode (ModestMsgViewWindow *self);
199 static void init_window (ModestMsgViewWindow *obj);
201 static gboolean msg_is_visible (TnyHeader *header, gboolean check_outbox);
203 static void check_dimming_rules_after_change (ModestMsgViewWindow *window);
205 static gboolean on_fetch_image (ModestMsgView *msgview,
208 ModestMsgViewWindow *window);
210 static gboolean modest_msg_view_window_scroll_child (ModestMsgViewWindow *self,
211 GtkScrollType scroll_type,
214 static gboolean message_reader (ModestMsgViewWindow *window,
215 ModestMsgViewWindowPrivate *priv,
217 GtkTreeRowReference *row_reference);
219 static void add_to_menu (ModestMsgViewWindow *self,
223 ModestDimmingRulesGroup *group,
224 GCallback dimming_callback);
225 static void setup_menu (ModestMsgViewWindow *self,
226 ModestDimmingRulesGroup *group);
228 /* list my signals */
235 static const GtkToggleActionEntry msg_view_toggle_action_entries [] = {
236 { "FindInMessage", MODEST_TOOLBAR_ICON_FIND, N_("qgn_toolb_gene_find"), NULL, NULL, G_CALLBACK (modest_msg_view_window_toggle_find_toolbar), FALSE },
237 <<<<<<< HEAD:src/hildon2/modest-msg-view-window.c
238 { "ToolsFindInMessage", NULL, N_("mcen_me_viewer_find"), "<CTRL>F", 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);
363 static gboolean modest_msg_view_window_scroll_child (ModestMsgViewWindow *self,
364 GtkScrollType scroll_type,
369 gboolean modest_msg_view_window_scroll_child (ModestMsgViewWindow *self,
370 GtkScrollType scroll_type,
373 >>>>>>> fe21497... * Fixes NB#92694, migrated the viewer menus to 1.5 specs version:src/hildon2/modest-msg-view-window.c
375 ModestMsgViewWindowPrivate *priv;
376 gboolean return_value;
378 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(self);
379 g_signal_emit_by_name (priv->main_scroll, "scroll-child", scroll_type, horizontal, &return_value);
384 add_scroll_binding (GtkBindingSet *binding_set,
386 GtkScrollType scroll)
388 guint keypad_keyval = keyval - GDK_Left + GDK_KP_Left;
390 gtk_binding_entry_add_signal (binding_set, keyval, 0,
392 GTK_TYPE_SCROLL_TYPE, scroll,
393 G_TYPE_BOOLEAN, FALSE);
394 gtk_binding_entry_add_signal (binding_set, keypad_keyval, 0,
396 GTK_TYPE_SCROLL_TYPE, scroll,
397 G_TYPE_BOOLEAN, FALSE);
401 modest_msg_view_window_class_init (ModestMsgViewWindowClass *klass)
403 GObjectClass *gobject_class;
404 HildonWindowClass *hildon_window_class;
405 ModestWindowClass *modest_window_class;
406 GtkBindingSet *binding_set;
408 gobject_class = (GObjectClass*) klass;
409 hildon_window_class = (HildonWindowClass *) klass;
410 modest_window_class = (ModestWindowClass *) klass;
412 parent_class = g_type_class_peek_parent (klass);
413 gobject_class->finalize = modest_msg_view_window_finalize;
415 hildon_window_class->toggle_menu = modest_msg_view_window_toggle_menu;
417 modest_window_class->set_zoom_func = modest_msg_view_window_set_zoom;
418 modest_window_class->get_zoom_func = modest_msg_view_window_get_zoom;
419 modest_window_class->show_toolbar_func = modest_msg_view_window_show_toolbar;
420 modest_window_class->disconnect_signals_func = modest_msg_view_window_disconnect_signals;
422 modest_window_class->save_state_func = save_state;
424 klass->scroll_child = modest_msg_view_window_scroll_child;
426 signals[MSG_CHANGED_SIGNAL] =
427 g_signal_new ("msg-changed",
428 G_TYPE_FROM_CLASS (gobject_class),
430 G_STRUCT_OFFSET (ModestMsgViewWindowClass, msg_changed),
432 modest_marshal_VOID__POINTER_POINTER,
433 G_TYPE_NONE, 2, G_TYPE_POINTER, G_TYPE_POINTER);
435 signals[SCROLL_CHILD_SIGNAL] =
436 g_signal_new ("scroll-child",
437 G_TYPE_FROM_CLASS (gobject_class),
438 G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
439 G_STRUCT_OFFSET (ModestMsgViewWindowClass, scroll_child),
441 modest_marshal_BOOLEAN__ENUM_BOOLEAN,
442 G_TYPE_BOOLEAN, 2, GTK_TYPE_SCROLL_TYPE, G_TYPE_BOOLEAN);
444 binding_set = gtk_binding_set_by_class (klass);
445 add_scroll_binding (binding_set, GDK_Up, GTK_SCROLL_STEP_UP);
446 add_scroll_binding (binding_set, GDK_Down, GTK_SCROLL_STEP_DOWN);
447 add_scroll_binding (binding_set, GDK_Page_Up, GTK_SCROLL_PAGE_UP);
448 add_scroll_binding (binding_set, GDK_Page_Down, GTK_SCROLL_PAGE_DOWN);
449 add_scroll_binding (binding_set, GDK_Home, GTK_SCROLL_START);
450 add_scroll_binding (binding_set, GDK_End, GTK_SCROLL_END);
452 g_type_class_add_private (gobject_class, sizeof(ModestMsgViewWindowPrivate));
456 static void modest_header_view_observer_init(
457 ModestHeaderViewObserverIface *iface_class)
459 iface_class->update_func = modest_msg_view_window_update_model_replaced;
463 modest_msg_view_window_init (ModestMsgViewWindow *obj)
465 ModestMsgViewWindowPrivate *priv;
466 ModestWindowPrivate *parent_priv = NULL;
467 GtkActionGroup *action_group = NULL;
468 GError *error = NULL;
469 GdkPixbuf *window_icon;
471 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(obj);
472 parent_priv = MODEST_WINDOW_GET_PRIVATE(obj);
473 parent_priv->ui_manager = gtk_ui_manager_new();
475 action_group = gtk_action_group_new ("ModestMsgViewWindowActions");
476 gtk_action_group_set_translation_domain (action_group, GETTEXT_PACKAGE);
478 /* Add common actions */
479 gtk_action_group_add_actions (action_group,
480 modest_action_entries,
481 G_N_ELEMENTS (modest_action_entries),
483 gtk_action_group_add_toggle_actions (action_group,
484 msg_view_toggle_action_entries,
485 G_N_ELEMENTS (msg_view_toggle_action_entries),
488 gtk_ui_manager_insert_action_group (parent_priv->ui_manager, action_group, 0);
489 g_object_unref (action_group);
491 /* Load the UI definition */
492 gtk_ui_manager_add_ui_from_file (parent_priv->ui_manager, MODEST_UIDIR "modest-msg-view-window-ui.xml",
495 g_printerr ("modest: could not merge modest-msg-view-window-ui.xml: %s\n", error->message);
496 g_error_free (error);
501 /* Add accelerators */
502 gtk_window_add_accel_group (GTK_WINDOW (obj),
503 gtk_ui_manager_get_accel_group (parent_priv->ui_manager));
505 priv->is_search_result = FALSE;
506 priv->is_outbox = FALSE;
508 priv->msg_view = NULL;
509 priv->header_model = NULL;
510 priv->header_folder_id = NULL;
511 priv->clipboard_change_handler = 0;
512 priv->queue_change_handler = 0;
513 priv->account_removed_handler = 0;
514 priv->row_changed_handler = 0;
515 priv->row_deleted_handler = 0;
516 priv->row_inserted_handler = 0;
517 priv->rows_reordered_handler = 0;
518 priv->progress_hint = FALSE;
520 priv->optimized_view = FALSE;
521 priv->purge_timeout = 0;
522 priv->remove_attachment_banner = NULL;
523 priv->msg_uid = NULL;
525 priv->sighandlers = NULL;
528 init_window (MODEST_MSG_VIEW_WINDOW(obj));
530 /* Set window icon */
531 window_icon = modest_platform_get_icon (MODEST_APP_MSG_VIEW_ICON, MODEST_ICON_SIZE_BIG);
533 gtk_window_set_icon (GTK_WINDOW (obj), window_icon);
534 g_object_unref (window_icon);
537 hildon_program_add_window (hildon_program_get_instance(),
544 set_toolbar_transfer_mode (ModestMsgViewWindow *self)
546 ModestMsgViewWindowPrivate *priv = NULL;
548 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self), FALSE);
550 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(self);
552 set_progress_hint (self, TRUE);
558 set_progress_hint (ModestMsgViewWindow *self,
561 ModestWindowPrivate *parent_priv;
562 ModestMsgViewWindowPrivate *priv;
564 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self));
566 parent_priv = MODEST_WINDOW_GET_PRIVATE(self);
567 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(self);
569 /* Sets current progress hint */
570 priv->progress_hint = enabled;
572 if (GTK_WIDGET_VISIBLE (self)) {
573 hildon_gtk_window_set_progress_indicator (GTK_WINDOW (self), enabled?1:0);
580 init_window (ModestMsgViewWindow *obj)
582 GtkWidget *main_vbox;
583 ModestMsgViewWindowPrivate *priv;
585 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(obj);
587 priv->msg_view = GTK_WIDGET (tny_platform_factory_new_msg_view (modest_tny_platform_factory_get_instance ()));
588 modest_msg_view_set_shadow_type (MODEST_MSG_VIEW (priv->msg_view), GTK_SHADOW_NONE);
589 main_vbox = gtk_vbox_new (FALSE, 6);
590 #ifdef MODEST_TOOLKIT_HILDON2
591 priv->main_scroll = hildon_pannable_area_new ();
592 gtk_container_add (GTK_CONTAINER (priv->main_scroll), priv->msg_view);
594 #ifdef MODEST_USE_MOZEMBED
595 priv->main_scroll = priv->msg_view;
596 gtk_widget_set_size_request (priv->msg_view, -1, 1600);
598 priv->main_scroll = gtk_scrolled_window_new (NULL, NULL);
599 gtk_container_add (GTK_CONTAINER (priv->main_scroll), priv->msg_view);
601 gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (priv->main_scroll), GTK_POLICY_NEVER, GTK_POLICY_AUTOMATIC);
602 gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW (priv->main_scroll), GTK_SHADOW_NONE);
603 modest_maemo_set_thumbable_scrollbar (GTK_SCROLLED_WINDOW(priv->main_scroll), TRUE);
606 gtk_box_pack_start (GTK_BOX(main_vbox), priv->main_scroll, TRUE, TRUE, 0);
607 gtk_container_add (GTK_CONTAINER(obj), main_vbox);
609 priv->find_toolbar = hildon_find_toolbar_new (NULL);
610 hildon_window_add_toolbar (HILDON_WINDOW (obj), GTK_TOOLBAR (priv->find_toolbar));
611 gtk_widget_set_no_show_all (priv->find_toolbar, TRUE);
613 gtk_widget_show_all (GTK_WIDGET(main_vbox));
617 modest_msg_view_window_disconnect_signals (ModestWindow *self)
619 ModestMsgViewWindowPrivate *priv;
620 GtkWidget *header_view = NULL;
621 GtkWindow *parent_window = NULL;
623 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
625 if (gtk_clipboard_get (GDK_SELECTION_PRIMARY) &&
626 g_signal_handler_is_connected (gtk_clipboard_get (GDK_SELECTION_PRIMARY),
627 priv->clipboard_change_handler))
628 g_signal_handler_disconnect (gtk_clipboard_get (GDK_SELECTION_PRIMARY),
629 priv->clipboard_change_handler);
631 if (g_signal_handler_is_connected (G_OBJECT (modest_runtime_get_mail_operation_queue ()),
632 priv->queue_change_handler))
633 g_signal_handler_disconnect (G_OBJECT (modest_runtime_get_mail_operation_queue ()),
634 priv->queue_change_handler);
636 if (g_signal_handler_is_connected (G_OBJECT (modest_runtime_get_account_store ()),
637 priv->account_removed_handler))
638 g_signal_handler_disconnect (G_OBJECT (modest_runtime_get_account_store ()),
639 priv->account_removed_handler);
641 if (priv->header_model) {
642 if (g_signal_handler_is_connected(G_OBJECT (priv->header_model),
643 priv->row_changed_handler))
644 g_signal_handler_disconnect(G_OBJECT (priv->header_model),
645 priv->row_changed_handler);
647 if (g_signal_handler_is_connected(G_OBJECT (priv->header_model),
648 priv->row_deleted_handler))
649 g_signal_handler_disconnect(G_OBJECT (priv->header_model),
650 priv->row_deleted_handler);
652 if (g_signal_handler_is_connected(G_OBJECT (priv->header_model),
653 priv->row_inserted_handler))
654 g_signal_handler_disconnect(G_OBJECT (priv->header_model),
655 priv->row_inserted_handler);
657 if (g_signal_handler_is_connected(G_OBJECT (priv->header_model),
658 priv->rows_reordered_handler))
659 g_signal_handler_disconnect(G_OBJECT (priv->header_model),
660 priv->rows_reordered_handler);
663 modest_signal_mgr_disconnect_all_and_destroy (priv->sighandlers);
664 priv->sighandlers = NULL;
666 parent_window = gtk_window_get_transient_for (GTK_WINDOW (self));
667 if (parent_window && MODEST_IS_HEADER_WINDOW (parent_window)) {
668 header_view = GTK_WIDGET (modest_header_window_get_header_view (MODEST_HEADER_WINDOW (parent_window)));
670 modest_header_view_remove_observer(MODEST_HEADER_VIEW (header_view),
671 MODEST_HEADER_VIEW_OBSERVER(self));
677 modest_msg_view_window_finalize (GObject *obj)
679 ModestMsgViewWindowPrivate *priv;
681 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (obj);
683 /* Sanity check: shouldn't be needed, the window mgr should
684 call this function before */
685 modest_msg_view_window_disconnect_signals (MODEST_WINDOW (obj));
687 if (priv->header_model != NULL) {
688 g_object_unref (priv->header_model);
689 priv->header_model = NULL;
692 if (priv->remove_attachment_banner) {
693 gtk_widget_destroy (priv->remove_attachment_banner);
694 g_object_unref (priv->remove_attachment_banner);
695 priv->remove_attachment_banner = NULL;
698 if (priv->purge_timeout > 0) {
699 g_source_remove (priv->purge_timeout);
700 priv->purge_timeout = 0;
703 if (priv->row_reference) {
704 gtk_tree_row_reference_free (priv->row_reference);
705 priv->row_reference = NULL;
708 if (priv->next_row_reference) {
709 gtk_tree_row_reference_free (priv->next_row_reference);
710 priv->next_row_reference = NULL;
714 g_free (priv->msg_uid);
715 priv->msg_uid = NULL;
718 G_OBJECT_CLASS(parent_class)->finalize (obj);
722 select_next_valid_row (GtkTreeModel *model,
723 GtkTreeRowReference **row_reference,
727 GtkTreeIter tmp_iter;
729 GtkTreePath *next = NULL;
730 gboolean retval = FALSE, finished;
732 g_return_val_if_fail (gtk_tree_row_reference_valid (*row_reference), FALSE);
734 path = gtk_tree_row_reference_get_path (*row_reference);
735 gtk_tree_model_get_iter (model, &tmp_iter, path);
736 gtk_tree_row_reference_free (*row_reference);
737 *row_reference = NULL;
741 TnyHeader *header = NULL;
743 if (gtk_tree_model_iter_next (model, &tmp_iter)) {
744 gtk_tree_model_get (model, &tmp_iter,
745 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
749 if (msg_is_visible (header, is_outbox)) {
750 next = gtk_tree_model_get_path (model, &tmp_iter);
751 *row_reference = gtk_tree_row_reference_new (model, next);
752 gtk_tree_path_free (next);
756 g_object_unref (header);
759 } else if (cycle && gtk_tree_model_get_iter_first (model, &tmp_iter)) {
760 next = gtk_tree_model_get_path (model, &tmp_iter);
762 /* Ensure that we are not selecting the same */
763 if (gtk_tree_path_compare (path, next) != 0) {
764 gtk_tree_model_get (model, &tmp_iter,
765 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
768 if (msg_is_visible (header, is_outbox)) {
769 *row_reference = gtk_tree_row_reference_new (model, next);
773 g_object_unref (header);
777 /* If we ended up in the same message
778 then there is no valid next
782 gtk_tree_path_free (next);
784 /* If there are no more messages and we don't
785 want to start again in the first one then
786 there is no valid next message */
792 gtk_tree_path_free (path);
797 /* TODO: This should be in _init(), with the parameters as properties. */
799 modest_msg_view_window_construct (ModestMsgViewWindow *self,
800 const gchar *modest_account_name,
801 const gchar *msg_uid)
804 ModestMsgViewWindowPrivate *priv = NULL;
805 ModestWindowPrivate *parent_priv = NULL;
806 ModestDimmingRulesGroup *menu_rules_group = NULL;
807 ModestDimmingRulesGroup *toolbar_rules_group = NULL;
808 ModestDimmingRulesGroup *clipboard_rules_group = NULL;
810 obj = G_OBJECT (self);
811 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(obj);
812 parent_priv = MODEST_WINDOW_GET_PRIVATE(obj);
814 priv->msg_uid = g_strdup (msg_uid);
817 parent_priv->menubar = NULL;
818 parent_priv->ui_dimming_manager = modest_ui_dimming_manager_new();
820 menu_rules_group = modest_dimming_rules_group_new (MODEST_DIMMING_RULES_MENU, FALSE);
821 toolbar_rules_group = modest_dimming_rules_group_new (MODEST_DIMMING_RULES_TOOLBAR, TRUE);
822 clipboard_rules_group = modest_dimming_rules_group_new (MODEST_DIMMING_RULES_CLIPBOARD, FALSE);
824 setup_menu (self, menu_rules_group);
825 /* Add common dimming rules */
826 modest_dimming_rules_group_add_rules (menu_rules_group,
827 modest_msg_view_menu_dimming_entries,
828 G_N_ELEMENTS (modest_msg_view_menu_dimming_entries),
829 MODEST_WINDOW (self));
830 modest_dimming_rules_group_add_rules (toolbar_rules_group,
831 modest_msg_view_toolbar_dimming_entries,
832 G_N_ELEMENTS (modest_msg_view_toolbar_dimming_entries),
833 MODEST_WINDOW (self));
834 modest_dimming_rules_group_add_rules (clipboard_rules_group,
835 modest_msg_view_clipboard_dimming_entries,
836 G_N_ELEMENTS (modest_msg_view_clipboard_dimming_entries),
837 MODEST_WINDOW (self));
839 /* Insert dimming rules group for this window */
840 modest_ui_dimming_manager_insert_rules_group (parent_priv->ui_dimming_manager, menu_rules_group);
841 modest_ui_dimming_manager_insert_rules_group (parent_priv->ui_dimming_manager, toolbar_rules_group);
842 modest_ui_dimming_manager_insert_rules_group (parent_priv->ui_dimming_manager, clipboard_rules_group);
843 g_object_unref (menu_rules_group);
844 g_object_unref (toolbar_rules_group);
845 g_object_unref (clipboard_rules_group);
847 /* g_signal_connect (G_OBJECT(obj), "delete-event", G_CALLBACK(on_delete_event), obj); */
849 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);
850 g_signal_connect (G_OBJECT(priv->msg_view), "activate_link",
851 G_CALLBACK (modest_ui_actions_on_msg_link_clicked), obj);
852 g_signal_connect (G_OBJECT(priv->msg_view), "link_hover",
853 G_CALLBACK (modest_ui_actions_on_msg_link_hover), obj);
854 g_signal_connect (G_OBJECT(priv->msg_view), "attachment_clicked",
855 G_CALLBACK (modest_ui_actions_on_msg_attachment_clicked), obj);
856 g_signal_connect (G_OBJECT(priv->msg_view), "recpt_activated",
857 G_CALLBACK (modest_ui_actions_on_msg_recpt_activated), obj);
858 g_signal_connect (G_OBJECT(priv->msg_view), "show_details",
859 G_CALLBACK (modest_ui_actions_on_details), obj);
860 g_signal_connect (G_OBJECT(priv->msg_view), "link_contextual",
861 G_CALLBACK (modest_ui_actions_on_msg_link_contextual), obj);
862 g_signal_connect (G_OBJECT (priv->msg_view), "fetch_image",
863 G_CALLBACK (on_fetch_image), obj);
865 g_signal_connect (G_OBJECT (obj), "key-release-event",
866 G_CALLBACK (modest_msg_view_window_key_event),
869 g_signal_connect (G_OBJECT (obj), "key-press-event",
870 G_CALLBACK (modest_msg_view_window_key_event),
873 g_signal_connect (G_OBJECT (obj), "move-focus",
874 G_CALLBACK (on_move_focus), obj);
876 /* Mail Operation Queue */
877 priv->queue_change_handler = g_signal_connect (G_OBJECT (modest_runtime_get_mail_operation_queue ()),
879 G_CALLBACK (on_queue_changed),
882 /* Account manager */
883 priv->account_removed_handler = g_signal_connect (G_OBJECT (modest_runtime_get_account_store ()),
885 G_CALLBACK(on_account_removed),
888 modest_window_set_active_account (MODEST_WINDOW(obj), modest_account_name);
890 g_signal_connect (G_OBJECT (priv->find_toolbar), "close", G_CALLBACK (modest_msg_view_window_find_toolbar_close), obj);
891 g_signal_connect (G_OBJECT (priv->find_toolbar), "search", G_CALLBACK (modest_msg_view_window_find_toolbar_search), obj);
892 priv->last_search = NULL;
894 modest_msg_view_window_show_toolbar (MODEST_WINDOW (obj), TRUE);
896 /* Init the clipboard actions dim status */
897 modest_msg_view_grab_focus(MODEST_MSG_VIEW (priv->msg_view));
899 update_window_title (MODEST_MSG_VIEW_WINDOW (obj));
904 /* FIXME: parameter checks */
906 modest_msg_view_window_new_with_header_model (TnyMsg *msg,
907 const gchar *modest_account_name,
908 const gchar *msg_uid,
910 GtkTreeRowReference *row_reference)
912 ModestMsgViewWindow *window = NULL;
913 ModestMsgViewWindowPrivate *priv = NULL;
914 TnyFolder *header_folder = NULL;
915 ModestHeaderView *header_view = NULL;
916 ModestWindow *main_window = NULL;
917 ModestWindowMgr *mgr = NULL;
920 modest_tny_mime_part_to_string (TNY_MIME_PART (msg), 0);
923 mgr = modest_runtime_get_window_mgr ();
924 window = MODEST_MSG_VIEW_WINDOW (modest_window_mgr_get_msg_view_window (mgr));
925 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), NULL);
927 modest_msg_view_window_construct (window, modest_account_name, msg_uid);
929 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
931 /* Remember the message list's TreeModel so we can detect changes
932 * and change the list selection when necessary: */
934 main_window = modest_window_mgr_get_main_window(mgr, FALSE); /* don't create */
936 header_view = MODEST_HEADER_VIEW(modest_main_window_get_child_widget(
937 MODEST_MAIN_WINDOW(main_window),
938 MODEST_MAIN_WINDOW_WIDGET_TYPE_HEADER_VIEW));
941 if (header_view != NULL){
942 header_folder = modest_header_view_get_folder(header_view);
943 /* This could happen if the header folder was
944 unseleted before opening this msg window (for
945 example if the user selects an account in the
946 folder view of the main window */
948 priv->is_outbox = (modest_tny_folder_guess_folder_type (header_folder) == TNY_FOLDER_TYPE_OUTBOX);
949 priv->header_folder_id = tny_folder_get_id(header_folder);
950 g_assert(priv->header_folder_id != NULL);
951 g_object_unref(header_folder);
955 /* Setup row references and connect signals */
956 priv->header_model = g_object_ref (model);
959 priv->row_reference = gtk_tree_row_reference_copy (row_reference);
960 priv->next_row_reference = gtk_tree_row_reference_copy (row_reference);
961 select_next_valid_row (model, &(priv->next_row_reference), TRUE, priv->is_outbox);
963 priv->row_reference = NULL;
964 priv->next_row_reference = NULL;
967 /* Connect signals */
968 priv->row_changed_handler =
969 g_signal_connect (GTK_TREE_MODEL(model), "row-changed",
970 G_CALLBACK(modest_msg_view_window_on_row_changed),
972 priv->row_deleted_handler =
973 g_signal_connect (GTK_TREE_MODEL(model), "row-deleted",
974 G_CALLBACK(modest_msg_view_window_on_row_deleted),
976 priv->row_inserted_handler =
977 g_signal_connect (GTK_TREE_MODEL(model), "row-inserted",
978 G_CALLBACK(modest_msg_view_window_on_row_inserted),
980 priv->rows_reordered_handler =
981 g_signal_connect(GTK_TREE_MODEL(model), "rows-reordered",
982 G_CALLBACK(modest_msg_view_window_on_row_reordered),
985 if (header_view != NULL){
986 modest_header_view_add_observer(header_view,
987 MODEST_HEADER_VIEW_OBSERVER(window));
990 tny_msg_view_set_msg (TNY_MSG_VIEW (priv->msg_view), msg);
991 update_window_title (MODEST_MSG_VIEW_WINDOW (window));
993 /* gtk_widget_show_all (GTK_WIDGET (window)); */
994 modest_msg_view_window_update_priority (window);
995 /* Check dimming rules */
996 modest_ui_actions_check_toolbar_dimming_rules (MODEST_WINDOW (window));
997 modest_ui_actions_check_menu_dimming_rules (MODEST_WINDOW (window));
998 modest_window_check_dimming_rules_group (MODEST_WINDOW (window), MODEST_DIMMING_RULES_CLIPBOARD);
1000 return MODEST_WINDOW(window);
1004 modest_msg_view_window_new_from_header_view (ModestHeaderView *header_view,
1005 const gchar *modest_account_name,
1006 const gchar *msg_uid,
1007 GtkTreeRowReference *row_reference)
1009 ModestMsgViewWindow *window = NULL;
1010 ModestMsgViewWindowPrivate *priv = NULL;
1011 TnyFolder *header_folder = NULL;
1012 ModestWindowMgr *mgr = NULL;
1016 mgr = modest_runtime_get_window_mgr ();
1017 window = MODEST_MSG_VIEW_WINDOW (modest_window_mgr_get_msg_view_window (mgr));
1018 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), NULL);
1020 modest_msg_view_window_construct (window, modest_account_name, msg_uid);
1022 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1024 /* Remember the message list's TreeModel so we can detect changes
1025 * and change the list selection when necessary: */
1027 if (header_view != NULL){
1028 header_folder = modest_header_view_get_folder(header_view);
1029 /* This could happen if the header folder was
1030 unseleted before opening this msg window (for
1031 example if the user selects an account in the
1032 folder view of the main window */
1033 if (header_folder) {
1034 priv->is_outbox = (modest_tny_folder_guess_folder_type (header_folder) == TNY_FOLDER_TYPE_OUTBOX);
1035 priv->header_folder_id = tny_folder_get_id(header_folder);
1036 g_assert(priv->header_folder_id != NULL);
1037 g_object_unref(header_folder);
1041 /* Setup row references and connect signals */
1042 priv->header_model = gtk_tree_view_get_model (GTK_TREE_VIEW (header_view));
1044 if (row_reference) {
1045 priv->row_reference = gtk_tree_row_reference_copy (row_reference);
1046 priv->next_row_reference = gtk_tree_row_reference_copy (row_reference);
1047 select_next_valid_row (priv->header_model, &(priv->next_row_reference), TRUE, priv->is_outbox);
1049 priv->row_reference = NULL;
1050 priv->next_row_reference = NULL;
1053 /* Connect signals */
1054 priv->row_changed_handler =
1055 g_signal_connect (GTK_TREE_MODEL(priv->header_model), "row-changed",
1056 G_CALLBACK(modest_msg_view_window_on_row_changed),
1058 priv->row_deleted_handler =
1059 g_signal_connect (GTK_TREE_MODEL(priv->header_model), "row-deleted",
1060 G_CALLBACK(modest_msg_view_window_on_row_deleted),
1062 priv->row_inserted_handler =
1063 g_signal_connect (GTK_TREE_MODEL(priv->header_model), "row-inserted",
1064 G_CALLBACK(modest_msg_view_window_on_row_inserted),
1066 priv->rows_reordered_handler =
1067 g_signal_connect(GTK_TREE_MODEL(priv->header_model), "rows-reordered",
1068 G_CALLBACK(modest_msg_view_window_on_row_reordered),
1071 if (header_view != NULL){
1072 modest_header_view_add_observer(header_view,
1073 MODEST_HEADER_VIEW_OBSERVER(window));
1076 tny_msg_view_set_msg (TNY_MSG_VIEW (priv->msg_view), NULL);
1078 path = gtk_tree_row_reference_get_path (row_reference);
1079 if (gtk_tree_model_get_iter (priv->header_model, &iter, path)) {
1081 gtk_tree_model_get (priv->header_model, &iter,
1082 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
1084 message_reader (window, priv, header, row_reference);
1086 gtk_tree_path_free (path);
1088 /* Check dimming rules */
1089 modest_ui_actions_check_toolbar_dimming_rules (MODEST_WINDOW (window));
1090 modest_ui_actions_check_menu_dimming_rules (MODEST_WINDOW (window));
1091 modest_window_check_dimming_rules_group (MODEST_WINDOW (window), MODEST_DIMMING_RULES_CLIPBOARD);
1093 return MODEST_WINDOW(window);
1097 modest_msg_view_window_new_for_search_result (TnyMsg *msg,
1098 const gchar *modest_account_name,
1099 const gchar *msg_uid)
1101 ModestMsgViewWindow *window = NULL;
1102 ModestMsgViewWindowPrivate *priv = NULL;
1103 ModestWindowMgr *mgr = NULL;
1105 mgr = modest_runtime_get_window_mgr ();
1106 window = MODEST_MSG_VIEW_WINDOW (modest_window_mgr_get_msg_view_window (mgr));
1107 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), NULL);
1108 modest_msg_view_window_construct (window, modest_account_name, msg_uid);
1110 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1112 /* Remember that this is a search result,
1113 * so we can disable some UI appropriately: */
1114 priv->is_search_result = TRUE;
1116 tny_msg_view_set_msg (TNY_MSG_VIEW (priv->msg_view), msg);
1118 update_window_title (window);
1119 /* gtk_widget_show_all (GTK_WIDGET (window));*/
1120 modest_msg_view_window_update_priority (window);
1122 /* Check dimming rules */
1123 modest_ui_actions_check_toolbar_dimming_rules (MODEST_WINDOW (window));
1124 modest_ui_actions_check_menu_dimming_rules (MODEST_WINDOW (window));
1125 modest_window_check_dimming_rules_group (MODEST_WINDOW (window), MODEST_DIMMING_RULES_CLIPBOARD);
1127 return MODEST_WINDOW(window);
1131 modest_msg_view_window_new_for_attachment (TnyMsg *msg,
1132 const gchar *modest_account_name,
1133 const gchar *msg_uid)
1135 GObject *obj = NULL;
1136 ModestMsgViewWindowPrivate *priv;
1137 ModestWindowMgr *mgr = NULL;
1139 g_return_val_if_fail (msg, NULL);
1140 mgr = modest_runtime_get_window_mgr ();
1141 obj = G_OBJECT (modest_window_mgr_get_msg_view_window (mgr));
1142 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (obj);
1143 modest_msg_view_window_construct (MODEST_MSG_VIEW_WINDOW (obj),
1144 modest_account_name, msg_uid);
1146 tny_msg_view_set_msg (TNY_MSG_VIEW (priv->msg_view), msg);
1147 update_window_title (MODEST_MSG_VIEW_WINDOW (obj));
1149 /* gtk_widget_show_all (GTK_WIDGET (obj)); */
1151 /* Check dimming rules */
1152 modest_ui_actions_check_toolbar_dimming_rules (MODEST_WINDOW (obj));
1153 modest_ui_actions_check_menu_dimming_rules (MODEST_WINDOW (obj));
1154 modest_window_check_dimming_rules_group (MODEST_WINDOW (obj), MODEST_DIMMING_RULES_CLIPBOARD);
1156 return MODEST_WINDOW(obj);
1160 modest_msg_view_window_on_row_changed (GtkTreeModel *header_model,
1163 ModestMsgViewWindow *window)
1165 check_dimming_rules_after_change (window);
1169 modest_msg_view_window_on_row_deleted(GtkTreeModel *header_model,
1171 ModestMsgViewWindow *window)
1173 check_dimming_rules_after_change (window);
1175 /* The window could have dissapeared */
1178 check_dimming_rules_after_change (ModestMsgViewWindow *window)
1180 modest_ui_actions_check_menu_dimming_rules (MODEST_WINDOW (window));
1181 modest_ui_actions_check_toolbar_dimming_rules (MODEST_WINDOW (window));
1185 /* On insertions we check if the folder still has the message we are
1186 * showing or do not. If do not, we do nothing. Which means we are still
1187 * not attached to any header folder and thus next/prev buttons are
1188 * still dimmed. Once the message that is shown by msg-view is found, the
1189 * new model of header-view will be attached and the references will be set.
1190 * On each further insertions dimming rules will be checked. However
1191 * this requires extra CPU time at least works.
1192 * (An message might be deleted from TnyFolder and thus will not be
1193 * inserted into the model again for example if it is removed by the
1194 * imap server and the header view is refreshed.)
1197 modest_msg_view_window_on_row_inserted (GtkTreeModel *model,
1198 GtkTreePath *tree_path,
1199 GtkTreeIter *tree_iter,
1200 ModestMsgViewWindow *window)
1202 ModestMsgViewWindowPrivate *priv = NULL;
1203 TnyHeader *header = NULL;
1205 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window));
1206 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1208 g_assert (model == priv->header_model);
1210 /* Check if the newly inserted message is the same we are actually
1211 * showing. IF not, we should remain detached from the header model
1212 * and thus prev and next toolbar buttons should remain dimmed. */
1213 gtk_tree_model_get (model, tree_iter,
1214 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
1217 if (TNY_IS_HEADER (header)) {
1220 uid = modest_tny_folder_get_header_unique_id (header);
1221 if (!g_str_equal(priv->msg_uid, uid)) {
1222 check_dimming_rules_after_change (window);
1224 g_object_unref (G_OBJECT(header));
1228 g_object_unref(G_OBJECT(header));
1231 if (priv->row_reference) {
1232 gtk_tree_row_reference_free (priv->row_reference);
1235 /* Setup row_reference for the actual msg. */
1236 priv->row_reference = gtk_tree_row_reference_new (priv->header_model, tree_path);
1237 if (priv->row_reference == NULL) {
1238 g_warning("No reference for msg header item.");
1242 /* Now set up next_row_reference. */
1243 if (priv->next_row_reference) {
1244 gtk_tree_row_reference_free (priv->next_row_reference);
1247 priv->next_row_reference = gtk_tree_row_reference_copy (priv->row_reference);
1248 select_next_valid_row (priv->header_model,
1249 &(priv->next_row_reference), FALSE, priv->is_outbox);
1251 /* Connect the remaining callbacks to become able to detect
1252 * changes in header-view. */
1253 priv->row_changed_handler =
1254 g_signal_connect (priv->header_model, "row-changed",
1255 G_CALLBACK (modest_msg_view_window_on_row_changed),
1257 priv->row_deleted_handler =
1258 g_signal_connect (priv->header_model, "row-deleted",
1259 G_CALLBACK (modest_msg_view_window_on_row_deleted),
1261 priv->rows_reordered_handler =
1262 g_signal_connect (priv->header_model, "rows-reordered",
1263 G_CALLBACK (modest_msg_view_window_on_row_reordered),
1266 check_dimming_rules_after_change (window);
1270 modest_msg_view_window_on_row_reordered (GtkTreeModel *header_model,
1274 ModestMsgViewWindow *window)
1276 ModestMsgViewWindowPrivate *priv = NULL;
1277 gboolean already_changed = FALSE;
1279 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(window);
1281 /* If the current row was reordered select the proper next
1282 valid row. The same if the next row reference changes */
1283 if (priv->row_reference &&
1284 gtk_tree_row_reference_valid (priv->row_reference)) {
1286 path = gtk_tree_row_reference_get_path (priv->row_reference);
1287 if (gtk_tree_path_compare (path, arg1) == 0) {
1288 if (priv->next_row_reference) {
1289 gtk_tree_row_reference_free (priv->next_row_reference);
1291 priv->next_row_reference = gtk_tree_row_reference_copy (priv->row_reference);
1292 select_next_valid_row (header_model, &(priv->next_row_reference), FALSE, priv->is_outbox);
1293 already_changed = TRUE;
1295 gtk_tree_path_free (path);
1297 if (!already_changed &&
1298 priv->next_row_reference &&
1299 gtk_tree_row_reference_valid (priv->next_row_reference)) {
1301 path = gtk_tree_row_reference_get_path (priv->next_row_reference);
1302 if (gtk_tree_path_compare (path, arg1) == 0) {
1303 if (priv->next_row_reference) {
1304 gtk_tree_row_reference_free (priv->next_row_reference);
1306 priv->next_row_reference = gtk_tree_row_reference_copy (priv->row_reference);
1307 select_next_valid_row (header_model, &(priv->next_row_reference), FALSE, priv->is_outbox);
1309 gtk_tree_path_free (path);
1311 check_dimming_rules_after_change (window);
1314 /* The modest_msg_view_window_update_model_replaced implements update
1315 * function for ModestHeaderViewObserver. Checks whether the TnyFolder
1316 * actually belongs to the header-view is the same as the TnyFolder of
1317 * the message of msg-view or not. If they are different, there is
1318 * nothing to do. If they are the same, then the model has replaced and
1319 * the reference in msg-view shall be replaced from the old model to
1320 * the new model. In this case the view will be detached from it's
1321 * header folder. From this point the next/prev buttons are dimmed.
1324 modest_msg_view_window_update_model_replaced (ModestHeaderViewObserver *observer,
1325 GtkTreeModel *model,
1326 const gchar *tny_folder_id)
1328 ModestMsgViewWindowPrivate *priv = NULL;
1329 ModestMsgViewWindow *window = NULL;
1331 g_assert(MODEST_IS_HEADER_VIEW_OBSERVER(observer));
1332 g_assert(MODEST_IS_MSG_VIEW_WINDOW(observer));
1334 window = MODEST_MSG_VIEW_WINDOW(observer);
1335 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(window);
1337 /* If there is an other folder in the header-view then we do
1338 * not care about it's model (msg list). Else if the
1339 * header-view shows the folder the msg shown by us is in, we
1340 * shall replace our model reference and make some check. */
1341 if(model == NULL || tny_folder_id == NULL ||
1342 (priv->header_folder_id && !g_str_equal(tny_folder_id, priv->header_folder_id)))
1345 /* Model is changed(replaced), so we should forget the old
1346 * one. Because there might be other references and there
1347 * might be some change on the model even if we unreferenced
1348 * it, we need to disconnect our signals here. */
1349 if (priv->header_model) {
1350 if (g_signal_handler_is_connected(G_OBJECT (priv->header_model),
1351 priv->row_changed_handler))
1352 g_signal_handler_disconnect(G_OBJECT (priv->header_model),
1353 priv->row_changed_handler);
1354 if (g_signal_handler_is_connected(G_OBJECT (priv->header_model),
1355 priv->row_deleted_handler))
1356 g_signal_handler_disconnect(G_OBJECT (priv->header_model),
1357 priv->row_deleted_handler);
1358 if (g_signal_handler_is_connected(G_OBJECT (priv->header_model),
1359 priv->row_inserted_handler))
1360 g_signal_handler_disconnect(G_OBJECT (priv->header_model),
1361 priv->row_inserted_handler);
1362 if (g_signal_handler_is_connected(G_OBJECT (priv->header_model),
1363 priv->rows_reordered_handler))
1364 g_signal_handler_disconnect(G_OBJECT (priv->header_model),
1365 priv->rows_reordered_handler);
1368 if (priv->row_reference)
1369 gtk_tree_row_reference_free (priv->row_reference);
1370 if (priv->next_row_reference)
1371 gtk_tree_row_reference_free (priv->next_row_reference);
1372 g_object_unref(priv->header_model);
1375 priv->row_changed_handler = 0;
1376 priv->row_deleted_handler = 0;
1377 priv->row_inserted_handler = 0;
1378 priv->rows_reordered_handler = 0;
1379 priv->next_row_reference = NULL;
1380 priv->row_reference = NULL;
1381 priv->header_model = NULL;
1384 priv->header_model = g_object_ref (model);
1386 /* Also we must connect to the new model for row insertions.
1387 * Only for insertions now. We will need other ones only after
1388 * the msg is show by msg-view is added to the new model. */
1389 priv->row_inserted_handler =
1390 g_signal_connect (priv->header_model, "row-inserted",
1391 G_CALLBACK(modest_msg_view_window_on_row_inserted),
1394 modest_ui_actions_check_menu_dimming_rules(MODEST_WINDOW(window));
1395 modest_ui_actions_check_toolbar_dimming_rules(MODEST_WINDOW(window));
1399 modest_msg_view_window_toolbar_on_transfer_mode (ModestMsgViewWindow *self)
1401 ModestMsgViewWindowPrivate *priv= NULL;
1403 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self), FALSE);
1404 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
1406 return priv->progress_hint;
1410 modest_msg_view_window_get_header (ModestMsgViewWindow *self)
1412 ModestMsgViewWindowPrivate *priv= NULL;
1414 TnyHeader *header = NULL;
1415 GtkTreePath *path = NULL;
1418 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self), NULL);
1419 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
1421 /* If the message was not obtained from a treemodel,
1422 * for instance if it was opened directly by the search UI:
1424 if (priv->header_model == NULL ||
1425 priv->row_reference == NULL ||
1426 !gtk_tree_row_reference_valid (priv->row_reference)) {
1427 msg = modest_msg_view_window_get_message (self);
1429 header = tny_msg_get_header (msg);
1430 g_object_unref (msg);
1435 /* Get iter of the currently selected message in the header view: */
1436 path = gtk_tree_row_reference_get_path (priv->row_reference);
1437 g_return_val_if_fail (path != NULL, NULL);
1438 gtk_tree_model_get_iter (priv->header_model,
1442 /* Get current message header */
1443 gtk_tree_model_get (priv->header_model, &iter,
1444 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
1447 gtk_tree_path_free (path);
1452 modest_msg_view_window_get_message (ModestMsgViewWindow *self)
1454 ModestMsgViewWindowPrivate *priv;
1456 g_return_val_if_fail (self, NULL);
1458 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(self);
1460 return tny_msg_view_get_msg (TNY_MSG_VIEW (priv->msg_view));
1464 modest_msg_view_window_get_message_uid (ModestMsgViewWindow *self)
1466 ModestMsgViewWindowPrivate *priv;
1468 g_return_val_if_fail (self, NULL);
1470 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
1472 return (const gchar*) priv->msg_uid;
1476 modest_msg_view_window_toggle_find_toolbar (GtkToggleAction *toggle,
1479 ModestMsgViewWindow *window = MODEST_MSG_VIEW_WINDOW (data);
1480 ModestMsgViewWindowPrivate *priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1481 ModestWindowPrivate *parent_priv = MODEST_WINDOW_GET_PRIVATE (window);
1485 is_active = gtk_toggle_action_get_active (toggle);
1488 gtk_widget_show (priv->find_toolbar);
1489 hildon_find_toolbar_highlight_entry (HILDON_FIND_TOOLBAR (priv->find_toolbar), TRUE);
1491 gtk_widget_hide (priv->find_toolbar);
1492 modest_msg_view_grab_focus (MODEST_MSG_VIEW (priv->msg_view));
1495 /* update the toggle buttons status */
1496 action = gtk_ui_manager_get_action (parent_priv->ui_manager, "/ToolBar/FindInMessage");
1497 <<<<<<< HEAD:src/hildon2/modest-msg-view-window.c
1499 modest_utils_toggle_action_set_active_block_notify (GTK_TOGGLE_ACTION (action), is_active);
1502 modest_utils_toggle_action_set_active_block_notify (GTK_TOGGLE_ACTION (action), is_active);
1503 >>>>>>> fe21497... * Fixes NB#92694, migrated the viewer menus to 1.5 specs version:src/hildon2/modest-msg-view-window.c
1507 modest_msg_view_window_find_toolbar_close (GtkWidget *widget,
1508 ModestMsgViewWindow *obj)
1510 GtkToggleAction *toggle;
1511 ModestWindowPrivate *parent_priv;
1512 ModestMsgViewWindowPrivate *priv;
1514 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (obj);
1515 parent_priv = MODEST_WINDOW_GET_PRIVATE (obj);
1517 toggle = GTK_TOGGLE_ACTION (gtk_ui_manager_get_action (parent_priv->ui_manager, "/ToolBar/FindInMessage"));
1518 gtk_toggle_action_set_active (toggle, FALSE);
1519 modest_msg_view_grab_focus (MODEST_MSG_VIEW (priv->msg_view));
1523 modest_msg_view_window_find_toolbar_search (GtkWidget *widget,
1524 ModestMsgViewWindow *obj)
1526 gchar *current_search;
1527 ModestMsgViewWindowPrivate *priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (obj);
1529 if (modest_mime_part_view_is_empty (MODEST_MIME_PART_VIEW (priv->msg_view))) {
1530 hildon_banner_show_information (NULL, NULL, _("mail_ib_nothing_to_find"));
1534 g_object_get (G_OBJECT (widget), "prefix", ¤t_search, NULL);
1536 if ((current_search == NULL) || (strcmp (current_search, "") == 0)) {
1537 g_free (current_search);
1538 hildon_banner_show_information (NULL, NULL, dgettext("hildon-common-strings", "ecdg_ib_find_rep_enter_text"));
1542 if ((priv->last_search == NULL) || (strcmp (priv->last_search, current_search) != 0)) {
1544 g_free (priv->last_search);
1545 priv->last_search = g_strdup (current_search);
1546 result = modest_isearch_view_search (MODEST_ISEARCH_VIEW (priv->msg_view),
1549 hildon_banner_show_information (NULL, NULL, dgettext("hildon-libs", "ckct_ib_find_no_matches"));
1550 g_free (priv->last_search);
1551 priv->last_search = NULL;
1553 modest_msg_view_grab_focus (MODEST_MSG_VIEW (priv->msg_view));
1554 hildon_find_toolbar_highlight_entry (HILDON_FIND_TOOLBAR (priv->find_toolbar), TRUE);
1557 if (!modest_isearch_view_search_next (MODEST_ISEARCH_VIEW (priv->msg_view))) {
1558 hildon_banner_show_information (NULL, NULL, dgettext("hildon-libs", "ckct_ib_find_search_complete"));
1559 g_free (priv->last_search);
1560 priv->last_search = NULL;
1562 modest_msg_view_grab_focus (MODEST_MSG_VIEW (priv->msg_view));
1563 hildon_find_toolbar_highlight_entry (HILDON_FIND_TOOLBAR (priv->find_toolbar), TRUE);
1567 g_free (current_search);
1571 <<<<<<< HEAD:src/hildon2/modest-msg-view-window.c
1573 modest_msg_view_window_set_zoom (ModestWindow *window,
1576 ModestMsgViewWindowPrivate *priv;
1577 ModestWindowPrivate *parent_priv;
1579 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window));
1581 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1582 parent_priv = MODEST_WINDOW_GET_PRIVATE (window);
1583 modest_zoomable_set_zoom (MODEST_ZOOMABLE (priv->msg_view), zoom);
1588 >>>>>>> fe21497... * Fixes NB#92694, migrated the viewer menus to 1.5 specs version:src/hildon2/modest-msg-view-window.c
1590 modest_msg_view_window_get_zoom (ModestWindow *window)
1592 ModestMsgViewWindowPrivate *priv;
1594 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), 1.0);
1596 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1597 return modest_zoomable_get_zoom (MODEST_ZOOMABLE (priv->msg_view));
1601 <<<<<<< HEAD:src/hildon2/modest-msg-view-window.c
1602 modest_msg_view_window_zoom_plus (ModestWindow *window)
1605 ModestMsgViewWindowPrivate *priv;
1607 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), 1.0);
1608 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1610 zoom_level = modest_zoomable_get_zoom (MODEST_ZOOMABLE (priv->msg_view));
1612 if (zoom_level >= 2.0) {
1613 hildon_banner_show_information (NULL, NULL, dgettext("hildon-common-strings", "ckct_ib_max_zoom_level_reached"));
1615 } else if (zoom_level >= 1.5) {
1617 } else if (zoom_level >= 1.2) {
1619 } else if (zoom_level >= 1.0) {
1621 } else if (zoom_level >= 0.8) {
1623 } else if (zoom_level >= 0.5) {
1629 /* set zoom level */
1630 modest_zoomable_set_zoom (MODEST_ZOOMABLE (priv->msg_view), zoom_level);
1637 modest_msg_view_window_zoom_minus (ModestWindow *window)
1640 ModestMsgViewWindowPrivate *priv;
1642 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), 1.0);
1643 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1645 zoom_level = modest_zoomable_get_zoom (MODEST_ZOOMABLE (priv->msg_view));
1647 if (zoom_level <= 0.5) {
1648 hildon_banner_show_information (NULL, NULL, dgettext("hildon-common-strings", "ckct_ib_min_zoom_level_reached"));
1650 } else if (zoom_level <= 0.8) {
1652 } else if (zoom_level <= 1.0) {
1654 } else if (zoom_level <= 1.2) {
1656 } else if (zoom_level <= 1.5) {
1658 } else if (zoom_level <= 2.0) {
1664 /* set zoom level */
1665 modest_zoomable_set_zoom (MODEST_ZOOMABLE (priv->msg_view), zoom_level);
1673 >>>>>>> fe21497... * Fixes NB#92694, migrated the viewer menus to 1.5 specs version:src/hildon2/modest-msg-view-window.c
1674 modest_msg_view_window_key_event (GtkWidget *window,
1680 focus = gtk_window_get_focus (GTK_WINDOW (window));
1682 /* for the find toolbar case */
1683 if (focus && GTK_IS_ENTRY (focus)) {
1684 if (event->keyval == GDK_BackSpace) {
1686 copy = gdk_event_copy ((GdkEvent *) event);
1687 gtk_widget_event (focus, copy);
1688 gdk_event_free (copy);
1693 if (event->keyval == GDK_Up || event->keyval == GDK_KP_Up ||
1694 event->keyval == GDK_Down || event->keyval == GDK_KP_Down ||
1695 event->keyval == GDK_Page_Up || event->keyval == GDK_KP_Page_Up ||
1696 event->keyval == GDK_Page_Down || event->keyval == GDK_KP_Page_Down ||
1697 event->keyval == GDK_Home || event->keyval == GDK_KP_Home ||
1698 event->keyval == GDK_End || event->keyval == GDK_KP_End) {
1699 /* ModestMsgViewWindowPrivate *priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window); */
1700 /* gboolean return_value; */
1702 if (event->type == GDK_KEY_PRESS) {
1703 GtkScrollType scroll_type;
1705 switch (event->keyval) {
1708 scroll_type = GTK_SCROLL_STEP_UP; break;
1711 scroll_type = GTK_SCROLL_STEP_DOWN; break;
1713 case GDK_KP_Page_Up:
1714 scroll_type = GTK_SCROLL_PAGE_UP; break;
1716 case GDK_KP_Page_Down:
1717 scroll_type = GTK_SCROLL_PAGE_DOWN; break;
1720 scroll_type = GTK_SCROLL_START; break;
1723 scroll_type = GTK_SCROLL_END; break;
1724 default: scroll_type = GTK_SCROLL_NONE;
1727 /* g_signal_emit_by_name (G_OBJECT (priv->main_scroll), "scroll-child", */
1728 /* scroll_type, FALSE, &return_value); */
1739 modest_msg_view_window_last_message_selected (ModestMsgViewWindow *window)
1742 ModestMsgViewWindowPrivate *priv;
1743 GtkTreeIter tmp_iter;
1744 gboolean is_last_selected;
1746 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), TRUE);
1747 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1749 /*if no model (so no rows at all), then virtually we are the last*/
1750 if (!priv->header_model || !priv->row_reference)
1753 if (!gtk_tree_row_reference_valid (priv->row_reference))
1756 path = gtk_tree_row_reference_get_path (priv->row_reference);
1760 is_last_selected = TRUE;
1761 while (is_last_selected) {
1763 gtk_tree_path_next (path);
1764 if (!gtk_tree_model_get_iter (priv->header_model, &tmp_iter, path))
1766 gtk_tree_model_get (priv->header_model, &tmp_iter,
1767 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
1770 if (msg_is_visible (header, priv->is_outbox))
1771 is_last_selected = FALSE;
1772 g_object_unref(G_OBJECT(header));
1775 gtk_tree_path_free (path);
1776 return is_last_selected;
1780 modest_msg_view_window_has_headers_model (ModestMsgViewWindow *window)
1782 ModestMsgViewWindowPrivate *priv;
1784 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), TRUE);
1785 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1787 return priv->header_model != NULL;
1791 modest_msg_view_window_is_search_result (ModestMsgViewWindow *window)
1793 ModestMsgViewWindowPrivate *priv;
1795 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), TRUE);
1796 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1798 return priv->is_search_result;
1802 msg_is_visible (TnyHeader *header, gboolean check_outbox)
1804 if ((tny_header_get_flags(header) & TNY_HEADER_FLAG_DELETED))
1806 if (!check_outbox) {
1809 ModestTnySendQueueStatus status;
1810 status = modest_tny_all_send_queues_get_msg_status (header);
1811 return ((status != MODEST_TNY_SEND_QUEUE_FAILED) &&
1812 (status != MODEST_TNY_SEND_QUEUE_SENDING));
1817 modest_msg_view_window_first_message_selected (ModestMsgViewWindow *window)
1820 ModestMsgViewWindowPrivate *priv;
1821 gboolean is_first_selected;
1822 GtkTreeIter tmp_iter;
1824 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), TRUE);
1825 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1827 /*if no model (so no rows at all), then virtually we are the first*/
1828 if (!priv->header_model || !priv->row_reference)
1831 if (!gtk_tree_row_reference_valid (priv->row_reference))
1834 path = gtk_tree_row_reference_get_path (priv->row_reference);
1838 is_first_selected = TRUE;
1839 while (is_first_selected) {
1841 if(!gtk_tree_path_prev (path))
1843 /* Here the 'if' is needless for logic, but let make sure
1844 * iter is valid for gtk_tree_model_get. */
1845 if (!gtk_tree_model_get_iter (priv->header_model, &tmp_iter, path))
1847 gtk_tree_model_get (priv->header_model, &tmp_iter,
1848 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
1851 if (msg_is_visible (header, priv->is_outbox))
1852 is_first_selected = FALSE;
1853 g_object_unref(G_OBJECT(header));
1856 gtk_tree_path_free (path);
1857 return is_first_selected;
1862 GtkTreeRowReference *row_reference;
1866 message_reader_performer (gboolean canceled,
1868 GtkWindow *parent_window,
1869 TnyAccount *account,
1872 ModestMailOperation *mail_op = NULL;
1873 MsgReaderInfo *info;
1875 info = (MsgReaderInfo *) user_data;
1876 if (canceled || err) {
1880 /* Register the header - it'll be unregistered in the callback */
1881 modest_window_mgr_register_header (modest_runtime_get_window_mgr (), info->header, NULL);
1883 /* New mail operation */
1884 mail_op = modest_mail_operation_new_with_error_handling (G_OBJECT(parent_window),
1885 modest_ui_actions_disk_operations_error_handler,
1888 modest_mail_operation_queue_add (modest_runtime_get_mail_operation_queue (), mail_op);
1889 modest_mail_operation_get_msg (mail_op, info->header, TRUE, view_msg_cb, info->row_reference);
1890 g_object_unref (mail_op);
1892 /* Update dimming rules */
1893 modest_ui_actions_check_toolbar_dimming_rules (MODEST_WINDOW (parent_window));
1894 modest_ui_actions_check_menu_dimming_rules (MODEST_WINDOW (parent_window));
1897 /* Frees. The row_reference will be freed by the view_msg_cb callback */
1898 g_object_unref (info->header);
1899 g_slice_free (MsgReaderInfo, info);
1904 * Reads the message whose summary item is @header. It takes care of
1905 * several things, among others:
1907 * If the message was not previously downloaded then ask the user
1908 * before downloading. If there is no connection launch the connection
1909 * dialog. Update toolbar dimming rules.
1911 * Returns: TRUE if the mail operation was started, otherwise if the
1912 * user do not want to download the message, or if the user do not
1913 * want to connect, then the operation is not issued
1916 message_reader (ModestMsgViewWindow *window,
1917 ModestMsgViewWindowPrivate *priv,
1919 GtkTreeRowReference *row_reference)
1921 ModestWindowMgr *mgr;
1922 TnyAccount *account;
1924 MsgReaderInfo *info;
1926 g_return_val_if_fail (row_reference != NULL, FALSE);
1928 mgr = modest_runtime_get_window_mgr ();
1929 /* Msg download completed */
1930 if (!(tny_header_get_flags (header) & TNY_HEADER_FLAG_CACHED)) {
1931 /* Ask the user if he wants to download the message if
1933 if (!tny_device_is_online (modest_runtime_get_device())) {
1934 GtkResponseType response;
1936 response = modest_platform_run_confirmation_dialog (GTK_WINDOW (window),
1937 _("mcen_nc_get_msg"));
1938 if (response == GTK_RESPONSE_CANCEL)
1941 folder = tny_header_get_folder (header);
1942 info = g_slice_new (MsgReaderInfo);
1943 info->header = g_object_ref (header);
1944 info->row_reference = gtk_tree_row_reference_copy (row_reference);
1946 /* Offer the connection dialog if necessary */
1947 modest_platform_connect_if_remote_and_perform ((GtkWindow *) window,
1949 TNY_FOLDER_STORE (folder),
1950 message_reader_performer,
1952 g_object_unref (folder);
1957 folder = tny_header_get_folder (header);
1958 account = tny_folder_get_account (folder);
1959 info = g_slice_new (MsgReaderInfo);
1960 info->header = g_object_ref (header);
1961 info->row_reference = gtk_tree_row_reference_copy (row_reference);
1963 message_reader_performer (FALSE, NULL, (GtkWindow *) window, account, info);
1964 g_object_unref (account);
1965 g_object_unref (folder);
1971 modest_msg_view_window_select_next_message (ModestMsgViewWindow *window)
1973 ModestMsgViewWindowPrivate *priv;
1974 GtkTreePath *path= NULL;
1975 GtkTreeIter tmp_iter;
1977 gboolean retval = TRUE;
1978 GtkTreeRowReference *row_reference = NULL;
1980 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), FALSE);
1981 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1983 if (!priv->row_reference)
1986 /* Update the next row reference if it's not valid. This could
1987 happen if for example the header which it was pointing to,
1988 was deleted. The best place to do it is in the row-deleted
1989 handler but the tinymail model do not work like the glib
1990 tree models and reports the deletion when the row is still
1992 if (!gtk_tree_row_reference_valid (priv->next_row_reference)) {
1993 if (gtk_tree_row_reference_valid (priv->row_reference)) {
1994 priv->next_row_reference = gtk_tree_row_reference_copy (priv->row_reference);
1995 select_next_valid_row (priv->header_model, &(priv->next_row_reference), FALSE, priv->is_outbox);
1998 if (priv->next_row_reference)
1999 path = gtk_tree_row_reference_get_path (priv->next_row_reference);
2003 row_reference = gtk_tree_row_reference_copy (priv->next_row_reference);
2005 gtk_tree_model_get_iter (priv->header_model,
2008 gtk_tree_path_free (path);
2010 gtk_tree_model_get (priv->header_model, &tmp_iter,
2011 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
2014 /* Read the message & show it */
2015 if (!message_reader (window, priv, header, row_reference)) {
2018 gtk_tree_row_reference_free (row_reference);
2021 g_object_unref (header);
2027 modest_msg_view_window_select_previous_message (ModestMsgViewWindow *window)
2029 ModestMsgViewWindowPrivate *priv = NULL;
2031 gboolean finished = FALSE;
2032 gboolean retval = FALSE;
2034 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), FALSE);
2035 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
2037 /* Return inmediatly if there is no header model */
2038 if (!priv->header_model || !priv->row_reference)
2041 path = gtk_tree_row_reference_get_path (priv->row_reference);
2042 while (!finished && gtk_tree_path_prev (path)) {
2046 gtk_tree_model_get_iter (priv->header_model, &iter, path);
2047 gtk_tree_model_get (priv->header_model, &iter,
2048 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
2052 if (msg_is_visible (header, priv->is_outbox)) {
2053 GtkTreeRowReference *row_reference;
2054 row_reference = gtk_tree_row_reference_new (priv->header_model, path);
2055 /* Read the message & show it */
2056 retval = message_reader (window, priv, header, row_reference);
2057 gtk_tree_row_reference_free (row_reference);
2061 g_object_unref (header);
2065 gtk_tree_path_free (path);
2070 view_msg_cb (ModestMailOperation *mail_op,
2077 ModestMsgViewWindow *self = NULL;
2078 ModestMsgViewWindowPrivate *priv = NULL;
2079 GtkTreeRowReference *row_reference = NULL;
2081 /* Unregister the header (it was registered before creating the mail operation) */
2082 modest_window_mgr_unregister_header (modest_runtime_get_window_mgr (), header);
2084 row_reference = (GtkTreeRowReference *) user_data;
2086 gtk_tree_row_reference_free (row_reference);
2090 /* If there was any error */
2091 if (!modest_ui_actions_msg_retrieval_check (mail_op, header, msg)) {
2092 gtk_tree_row_reference_free (row_reference);
2096 /* Get the window */
2097 self = (ModestMsgViewWindow *) modest_mail_operation_get_source (mail_op);
2098 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self));
2099 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
2101 /* Update the row reference */
2102 if (priv->row_reference != NULL) {
2103 gtk_tree_row_reference_free (priv->row_reference);
2104 priv->row_reference = gtk_tree_row_reference_copy (row_reference);
2105 if (priv->next_row_reference != NULL) {
2106 gtk_tree_row_reference_free (priv->next_row_reference);
2108 priv->next_row_reference = gtk_tree_row_reference_copy (priv->row_reference);
2109 select_next_valid_row (priv->header_model, &(priv->next_row_reference), TRUE, priv->is_outbox);
2112 /* Mark header as read */
2113 if (!(tny_header_get_flags (header) & TNY_HEADER_FLAG_SEEN))
2114 tny_header_set_flag (header, TNY_HEADER_FLAG_SEEN);
2116 /* Set new message */
2117 if (priv->msg_view != NULL && TNY_IS_MSG_VIEW (priv->msg_view)) {
2118 tny_msg_view_set_msg (TNY_MSG_VIEW (priv->msg_view), msg);
2119 modest_msg_view_window_update_priority (self);
2120 update_window_title (MODEST_MSG_VIEW_WINDOW (self));
2121 modest_msg_view_grab_focus (MODEST_MSG_VIEW (priv->msg_view));
2124 /* Set the new message uid of the window */
2125 if (priv->msg_uid) {
2126 g_free (priv->msg_uid);
2127 priv->msg_uid = modest_tny_folder_get_header_unique_id (header);
2130 /* Notify the observers */
2131 g_signal_emit (G_OBJECT (self), signals[MSG_CHANGED_SIGNAL],
2132 0, priv->header_model, priv->row_reference);
2135 g_object_unref (self);
2136 gtk_tree_row_reference_free (row_reference);
2140 modest_msg_view_window_get_folder_type (ModestMsgViewWindow *window)
2142 ModestMsgViewWindowPrivate *priv;
2144 TnyFolderType folder_type;
2146 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
2148 folder_type = TNY_FOLDER_TYPE_UNKNOWN;
2150 msg = tny_msg_view_get_msg (TNY_MSG_VIEW (priv->msg_view));
2154 folder = tny_msg_get_folder (msg);
2156 folder_type = modest_tny_folder_guess_folder_type (folder);
2157 g_object_unref (folder);
2159 g_object_unref (msg);
2167 modest_msg_view_window_update_priority (ModestMsgViewWindow *window)
2169 ModestMsgViewWindowPrivate *priv;
2170 TnyHeader *header = NULL;
2171 TnyHeaderFlags flags = 0;
2173 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
2175 if (priv->header_model && priv->row_reference) {
2177 GtkTreePath *path = NULL;
2179 path = gtk_tree_row_reference_get_path (priv->row_reference);
2180 g_return_if_fail (path != NULL);
2181 gtk_tree_model_get_iter (priv->header_model,
2183 gtk_tree_row_reference_get_path (priv->row_reference));
2185 gtk_tree_model_get (priv->header_model, &iter, TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
2187 gtk_tree_path_free (path);
2190 msg = tny_msg_view_get_msg (TNY_MSG_VIEW (priv->msg_view));
2192 header = tny_msg_get_header (msg);
2193 g_object_unref (msg);
2198 flags = tny_header_get_flags (header);
2199 g_object_unref(G_OBJECT(header));
2202 modest_msg_view_set_priority (MODEST_MSG_VIEW(priv->msg_view), flags);
2207 toolbar_resize (ModestMsgViewWindow *self)
2209 ModestMsgViewWindowPrivate *priv = NULL;
2210 ModestWindowPrivate *parent_priv = NULL;
2212 gint static_button_size;
2213 ModestWindowMgr *mgr;
2215 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self));
2216 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
2217 parent_priv = MODEST_WINDOW_GET_PRIVATE(self);
2219 mgr = modest_runtime_get_window_mgr ();
2220 static_button_size = modest_window_mgr_get_fullscreen_mode (mgr)?118:108;
2222 if (parent_priv->toolbar) {
2223 /* left size buttons */
2224 widget = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/ToolbarMessageReply");
2225 gtk_tool_item_set_expand (GTK_TOOL_ITEM (widget), FALSE);
2226 gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (widget), FALSE);
2227 gtk_widget_set_size_request (GTK_WIDGET (widget), static_button_size, -1);
2228 widget = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/ToolbarMessageMoveTo");
2229 gtk_tool_item_set_expand (GTK_TOOL_ITEM (widget), FALSE);
2230 gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (widget), FALSE);
2231 gtk_widget_set_size_request (GTK_WIDGET (widget), static_button_size, -1);
2232 widget = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/ToolbarDeleteMessage");
2233 gtk_tool_item_set_expand (GTK_TOOL_ITEM (widget), FALSE);
2234 gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (widget), FALSE);
2235 gtk_widget_set_size_request (GTK_WIDGET (widget), static_button_size, -1);
2236 widget = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/FindInMessage");
2237 gtk_tool_item_set_expand (GTK_TOOL_ITEM (widget), FALSE);
2238 gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (widget), FALSE);
2239 gtk_widget_set_size_request (GTK_WIDGET (widget), static_button_size, -1);
2241 gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (priv->next_toolitem), TRUE);
2242 gtk_tool_item_set_expand (GTK_TOOL_ITEM (priv->next_toolitem), TRUE);
2243 gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (priv->prev_toolitem), TRUE);
2244 gtk_tool_item_set_expand (GTK_TOOL_ITEM (priv->prev_toolitem), TRUE);
2249 <<<<<<< HEAD:src/hildon2/modest-msg-view-window.c
2252 >>>>>>> fe21497... * Fixes NB#92694, migrated the viewer menus to 1.5 specs version:src/hildon2/modest-msg-view-window.c
2254 modest_msg_view_window_show_toolbar (ModestWindow *self,
2255 gboolean show_toolbar)
2257 ModestMsgViewWindowPrivate *priv = NULL;
2258 ModestWindowPrivate *parent_priv;
2259 GtkWidget *reply_button = NULL, *menu = NULL;
2260 <<<<<<< HEAD:src/hildon2/modest-msg-view-window.c
2262 GtkWidget *placeholder = NULL;
2264 >>>>>>> fe21497... * Fixes NB#92694, migrated the viewer menus to 1.5 specs version:src/hildon2/modest-msg-view-window.c
2266 parent_priv = MODEST_WINDOW_GET_PRIVATE(self);
2267 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(self);
2269 /* Set optimized view status */
2270 priv->optimized_view = !show_toolbar;
2272 if (!parent_priv->toolbar) {
2273 parent_priv->toolbar = gtk_ui_manager_get_widget (parent_priv->ui_manager,
2275 gtk_widget_set_no_show_all (parent_priv->toolbar, TRUE);
2277 priv->next_toolitem = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/ToolbarMessageNext");
2278 priv->prev_toolitem = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/ToolbarMessageBack");
2279 toolbar_resize (MODEST_MSG_VIEW_WINDOW (self));
2281 <<<<<<< HEAD:src/hildon2/modest-msg-view-window.c
2283 /* Add ProgressBar (Transfer toolbar) */
2284 priv->progress_bar = modest_progress_bar_new ();
2285 gtk_widget_set_no_show_all (priv->progress_bar, TRUE);
2286 placeholder = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/ProgressbarView");
2287 insert_index = gtk_toolbar_get_item_index(GTK_TOOLBAR (parent_priv->toolbar), GTK_TOOL_ITEM(placeholder));
2288 gtk_container_add (GTK_CONTAINER (priv->progress_toolitem), priv->progress_bar);
2289 gtk_toolbar_insert(GTK_TOOLBAR(parent_priv->toolbar), GTK_TOOL_ITEM (priv->progress_toolitem), insert_index);
2291 /* Connect cancel 'clicked' signal to abort progress mode */
2292 g_signal_connect(priv->cancel_toolitem, "clicked",
2293 G_CALLBACK(cancel_progressbar),
2296 /* Add it to the observers list */
2297 priv->progress_widgets = g_slist_prepend(priv->progress_widgets, priv->progress_bar);
2299 >>>>>>> fe21497... * Fixes NB#92694, migrated the viewer menus to 1.5 specs version:src/hildon2/modest-msg-view-window.c
2301 hildon_window_add_toolbar (HILDON_WINDOW (self),
2302 GTK_TOOLBAR (parent_priv->toolbar));
2304 /* Set reply button tap and hold menu */
2305 reply_button = gtk_ui_manager_get_widget (parent_priv->ui_manager,
2306 "/ToolBar/ToolbarMessageReply");
2307 menu = gtk_ui_manager_get_widget (parent_priv->ui_manager,
2308 "/ToolbarReplyCSM");
2309 if (menu && reply_button)
2310 gtk_widget_tap_and_hold_setup (GTK_WIDGET (reply_button), menu, NULL, 0);
2314 /* Quick hack: this prevents toolbar icons "dance" when progress bar show status is changed */
2315 /* TODO: resize mode migth be GTK_RESIZE_QUEUE, in order to avoid unneccesary shows */
2316 gtk_container_set_resize_mode (GTK_CONTAINER(parent_priv->toolbar), GTK_RESIZE_IMMEDIATE);
2318 gtk_widget_show (GTK_WIDGET (parent_priv->toolbar));
2319 if (modest_msg_view_window_transfer_mode_enabled (MODEST_MSG_VIEW_WINDOW (self)))
2320 set_progress_hint (MODEST_MSG_VIEW_WINDOW (self), TRUE);
2322 set_progress_hint (MODEST_MSG_VIEW_WINDOW (self), FALSE);
2325 gtk_widget_set_no_show_all (parent_priv->toolbar, TRUE);
2326 gtk_widget_hide (GTK_WIDGET (parent_priv->toolbar));
2328 <<<<<<< HEAD:src/hildon2/modest-msg-view-window.c
2331 >>>>>>> fe21497... * Fixes NB#92694, migrated the viewer menus to 1.5 specs version:src/hildon2/modest-msg-view-window.c
2335 modest_msg_view_window_clipboard_owner_change (GtkClipboard *clipboard,
2337 ModestMsgViewWindow *window)
2339 if (!GTK_WIDGET_VISIBLE (window))
2342 modest_window_check_dimming_rules_group (MODEST_WINDOW (window), MODEST_DIMMING_RULES_CLIPBOARD);
2346 modest_msg_view_window_transfer_mode_enabled (ModestMsgViewWindow *self)
2348 ModestMsgViewWindowPrivate *priv;
2350 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self), FALSE);
2351 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(self);
2353 return priv->progress_hint;
2357 observers_empty (ModestMsgViewWindow *self)
2360 ModestMsgViewWindowPrivate *priv;
2361 gboolean is_empty = TRUE;
2362 guint pending_ops = 0;
2364 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(self);
2365 tmp = priv->progress_widgets;
2367 /* Check all observers */
2368 while (tmp && is_empty) {
2369 pending_ops = modest_progress_object_num_pending_operations (MODEST_PROGRESS_OBJECT(tmp->data));
2370 is_empty = pending_ops == 0;
2372 tmp = g_slist_next(tmp);
2379 on_account_removed (TnyAccountStore *account_store,
2380 TnyAccount *account,
2383 /* Do nothing if it's a transport account, because we only
2384 show the messages of a store account */
2385 if (tny_account_get_account_type(account) == TNY_ACCOUNT_TYPE_STORE) {
2386 const gchar *parent_acc = NULL;
2387 const gchar *our_acc = NULL;
2389 our_acc = modest_window_get_active_account (MODEST_WINDOW (user_data));
2390 parent_acc = modest_tny_account_get_parent_modest_account_name_for_server_account (account);
2392 /* Close this window if I'm showing a message of the removed account */
2393 if (strcmp (parent_acc, our_acc) == 0)
2394 modest_ui_actions_on_close_window (NULL, MODEST_WINDOW (user_data));
2399 on_mail_operation_started (ModestMailOperation *mail_op,
2402 ModestMsgViewWindow *self;
2403 ModestMailOperationTypeOperation op_type;
2405 ModestMsgViewWindowPrivate *priv;
2406 GObject *source = NULL;
2408 self = MODEST_MSG_VIEW_WINDOW (user_data);
2409 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
2410 op_type = modest_mail_operation_get_type_operation (mail_op);
2411 tmp = priv->progress_widgets;
2412 source = modest_mail_operation_get_source(mail_op);
2413 if (G_OBJECT (self) == source) {
2414 if (op_type == MODEST_MAIL_OPERATION_TYPE_RECEIVE ) {
2415 set_toolbar_transfer_mode(self);
2417 modest_progress_object_add_operation (
2418 MODEST_PROGRESS_OBJECT (tmp->data),
2420 tmp = g_slist_next (tmp);
2424 g_object_unref (source);
2428 on_mail_operation_finished (ModestMailOperation *mail_op,
2431 ModestMsgViewWindow *self;
2432 ModestMailOperationTypeOperation op_type;
2434 ModestMsgViewWindowPrivate *priv;
2436 self = MODEST_MSG_VIEW_WINDOW (user_data);
2437 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
2438 op_type = modest_mail_operation_get_type_operation (mail_op);
2439 tmp = priv->progress_widgets;
2441 if (op_type == MODEST_MAIL_OPERATION_TYPE_RECEIVE ) {
2443 modest_progress_object_remove_operation (MODEST_PROGRESS_OBJECT (tmp->data),
2445 tmp = g_slist_next (tmp);
2448 /* If no more operations are being observed, NORMAL mode is enabled again */
2449 if (observers_empty (self)) {
2450 set_progress_hint (self, FALSE);
2453 /* Update dimming rules. We have to do this right here
2454 and not in view_msg_cb because at that point the
2455 transfer mode is still enabled so the dimming rule
2456 won't let the user delete the message that has been
2457 readed for example */
2458 modest_ui_actions_check_toolbar_dimming_rules (MODEST_WINDOW (self));
2459 modest_ui_actions_check_menu_dimming_rules (MODEST_WINDOW (self));
2464 on_queue_changed (ModestMailOperationQueue *queue,
2465 ModestMailOperation *mail_op,
2466 ModestMailOperationQueueNotification type,
2467 ModestMsgViewWindow *self)
2469 ModestMsgViewWindowPrivate *priv;
2471 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
2473 /* If this operations was created by another window, do nothing */
2474 if (!modest_mail_operation_is_mine (mail_op, G_OBJECT(self)))
2477 if (type == MODEST_MAIL_OPERATION_QUEUE_OPERATION_ADDED) {
2478 priv->sighandlers = modest_signal_mgr_connect (priv->sighandlers,
2480 "operation-started",
2481 G_CALLBACK (on_mail_operation_started),
2483 priv->sighandlers = modest_signal_mgr_connect (priv->sighandlers,
2485 "operation-finished",
2486 G_CALLBACK (on_mail_operation_finished),
2488 } else if (type == MODEST_MAIL_OPERATION_QUEUE_OPERATION_REMOVED) {
2489 priv->sighandlers = modest_signal_mgr_disconnect (priv->sighandlers,
2491 "operation-started");
2492 priv->sighandlers = modest_signal_mgr_disconnect (priv->sighandlers,
2494 "operation-finished");
2499 modest_msg_view_window_get_attachments (ModestMsgViewWindow *win)
2501 ModestMsgViewWindowPrivate *priv;
2502 TnyList *selected_attachments = NULL;
2504 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (win), NULL);
2505 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (win);
2507 selected_attachments = modest_msg_view_get_selected_attachments (MODEST_MSG_VIEW (priv->msg_view));
2509 return selected_attachments;
2515 guint banner_idle_id;
2516 } DecodeAsyncHelper;
2519 decode_async_banner_idle (gpointer user_data)
2521 DecodeAsyncHelper *helper = (DecodeAsyncHelper *) user_data;
2523 helper->banner_idle_id = 0;
2524 helper->banner = hildon_banner_show_animation (NULL, NULL, _("mail_me_opening"));
2525 g_object_ref (helper->banner);
2531 on_decode_to_stream_async_handler (TnyMimePart *mime_part,
2537 DecodeAsyncHelper *helper = (DecodeAsyncHelper *) user_data;
2539 if (helper->banner_idle_id > 0) {
2540 g_source_remove (helper->banner_idle_id);
2541 helper->banner_idle_id = 0;
2543 if (helper->banner) {
2544 gtk_widget_destroy (helper->banner);
2546 if (cancelled || err) {
2547 modest_platform_information_banner (NULL, NULL,
2548 _("mail_ib_file_operation_failed"));
2552 /* make the file read-only */
2553 g_chmod(helper->filepath, 0444);
2555 /* Activate the file */
2556 modest_platform_activate_file (helper->filepath, tny_mime_part_get_content_type (mime_part));
2560 g_free (helper->filepath);
2561 g_object_unref (helper->banner);
2562 g_slice_free (DecodeAsyncHelper, helper);
2566 modest_msg_view_window_view_attachment (ModestMsgViewWindow *window,
2567 TnyMimePart *mime_part)
2569 ModestMsgViewWindowPrivate *priv;
2570 const gchar *msg_uid;
2571 gchar *attachment_uid = NULL;
2572 gint attachment_index = 0;
2573 TnyList *attachments;
2575 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window));
2576 g_return_if_fail (TNY_IS_MIME_PART (mime_part) || (mime_part == NULL));
2577 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
2579 msg_uid = modest_msg_view_window_get_message_uid (MODEST_MSG_VIEW_WINDOW (window));
2580 attachments = modest_msg_view_get_attachments (MODEST_MSG_VIEW (priv->msg_view));
2581 attachment_index = modest_list_index (attachments, (GObject *) mime_part);
2582 g_object_unref (attachments);
2584 if (msg_uid && attachment_index >= 0) {
2585 attachment_uid = g_strdup_printf ("%s/%d", msg_uid, attachment_index);
2588 if (mime_part == NULL) {
2589 gboolean error = FALSE;
2590 TnyList *selected_attachments = modest_msg_view_get_selected_attachments (MODEST_MSG_VIEW (priv->msg_view));
2591 if (selected_attachments == NULL || tny_list_get_length (selected_attachments) == 0) {
2593 } else if (tny_list_get_length (selected_attachments) > 1) {
2594 hildon_banner_show_information (NULL, NULL, _("mcen_ib_unable_to_display_more"));
2598 iter = tny_list_create_iterator (selected_attachments);
2599 mime_part = (TnyMimePart *) tny_iterator_get_current (iter);
2600 g_object_unref (iter);
2602 g_object_unref (selected_attachments);
2607 g_object_ref (mime_part);
2610 if (tny_mime_part_is_purged (mime_part)) {
2611 g_object_unref (mime_part);
2615 if (!modest_tny_mime_part_is_msg (mime_part)) {
2616 gchar *filepath = NULL;
2617 const gchar *att_filename = tny_mime_part_get_filename (mime_part);
2618 gboolean show_error_banner = FALSE;
2619 TnyFsStream *temp_stream = NULL;
2620 temp_stream = modest_utils_create_temp_stream (att_filename, attachment_uid,
2623 if (temp_stream != NULL) {
2624 DecodeAsyncHelper *helper = g_slice_new (DecodeAsyncHelper);
2625 helper->filepath = g_strdup (filepath);
2626 helper->banner = NULL;
2627 helper->banner_idle_id = g_timeout_add (1000, decode_async_banner_idle, helper);
2628 tny_mime_part_decode_to_stream_async (mime_part, TNY_STREAM (temp_stream),
2629 on_decode_to_stream_async_handler,
2632 g_object_unref (temp_stream);
2633 /* NOTE: files in the temporary area will be automatically
2634 * cleaned after some time if they are no longer in use */
2637 const gchar *content_type;
2638 /* the file may already exist but it isn't writable,
2639 * let's try to open it anyway */
2640 content_type = tny_mime_part_get_content_type (mime_part);
2641 modest_platform_activate_file (filepath, content_type);
2643 g_warning ("%s: modest_utils_create_temp_stream failed", __FUNCTION__);
2644 show_error_banner = TRUE;
2649 if (show_error_banner)
2650 modest_platform_information_banner (NULL, NULL, _("mail_ib_file_operation_failed"));
2652 /* message attachment */
2653 TnyHeader *header = NULL;
2654 ModestWindowMgr *mgr;
2655 ModestWindow *msg_win = NULL;
2658 header = tny_msg_get_header (TNY_MSG (mime_part));
2659 mgr = modest_runtime_get_window_mgr ();
2660 found = modest_window_mgr_find_registered_header (mgr, header, &msg_win);
2663 /* if it's found, but there is no msg_win, it's probably in the process of being created;
2664 * thus, we don't do anything */
2665 g_warning ("window for is already being created");
2667 /* it's not found, so create a new window for it */
2668 modest_window_mgr_register_header (mgr, header, attachment_uid); /* register the uid before building the window */
2669 gchar *account = g_strdup (modest_window_get_active_account (MODEST_WINDOW (window)));
2671 account = modest_account_mgr_get_default_account (modest_runtime_get_account_mgr ());
2672 msg_win = modest_msg_view_window_new_for_attachment (TNY_MSG (mime_part), account, attachment_uid);
2673 modest_window_set_zoom (MODEST_WINDOW (msg_win),
2674 modest_window_get_zoom (MODEST_WINDOW (window)));
2675 modest_window_mgr_register_window (mgr, msg_win, MODEST_WINDOW (window));
2676 gtk_widget_show_all (GTK_WIDGET (msg_win));
2679 g_object_unref (mime_part);
2692 GnomeVFSResult result;
2695 static void save_mime_part_info_free (SaveMimePartInfo *info, gboolean with_struct);
2696 static gboolean idle_save_mime_part_show_result (SaveMimePartInfo *info);
2697 static gpointer save_mime_part_to_file (SaveMimePartInfo *info);
2698 static void save_mime_parts_to_file_with_checks (SaveMimePartInfo *info);
2701 save_mime_part_info_free (SaveMimePartInfo *info, gboolean with_struct)
2705 for (node = info->pairs; node != NULL; node = g_list_next (node)) {
2706 SaveMimePartPair *pair = (SaveMimePartPair *) node->data;
2707 g_free (pair->filename);
2708 g_object_unref (pair->part);
2709 g_slice_free (SaveMimePartPair, pair);
2711 g_list_free (info->pairs);
2714 gtk_widget_destroy (info->banner);
2715 g_slice_free (SaveMimePartInfo, info);
2720 idle_save_mime_part_show_result (SaveMimePartInfo *info)
2722 if (info->pairs != NULL) {
2723 save_mime_part_to_file (info);
2725 /* This is a GDK lock because we are an idle callback and
2726 * hildon_banner_show_information is or does Gtk+ code */
2728 gdk_threads_enter (); /* CHECKED */
2729 save_mime_part_info_free (info, TRUE);
2730 if (info->result == GNOME_VFS_OK) {
2731 hildon_banner_show_information (NULL, NULL, _CS("sfil_ib_saved"));
2732 } else if (info->result == GNOME_VFS_ERROR_NO_SPACE) {
2733 hildon_banner_show_information (NULL, NULL, dgettext("ke-recv",
2734 "cerm_device_memory_full"));
2736 hildon_banner_show_information (NULL, NULL, _("mail_ib_file_operation_failed"));
2738 gdk_threads_leave (); /* CHECKED */
2745 save_mime_part_to_file (SaveMimePartInfo *info)
2747 GnomeVFSHandle *handle;
2749 SaveMimePartPair *pair = (SaveMimePartPair *) info->pairs->data;
2751 info->result = gnome_vfs_create (&handle, pair->filename, GNOME_VFS_OPEN_WRITE, FALSE, 0644);
2752 if (info->result == GNOME_VFS_OK) {
2753 GError *error = NULL;
2754 stream = tny_vfs_stream_new (handle);
2755 if (tny_mime_part_decode_to_stream (pair->part, stream, &error) < 0) {
2756 g_warning ("modest: could not save attachment %s: %d (%s)\n", pair->filename, error?error->code:-1, error?error->message:"Unknown error");
2758 info->result = GNOME_VFS_ERROR_IO;
2760 g_object_unref (G_OBJECT (stream));
2761 g_object_unref (pair->part);
2762 g_slice_free (SaveMimePartPair, pair);
2763 info->pairs = g_list_delete_link (info->pairs, info->pairs);
2765 g_warning ("modest: could not create save attachment %s: %s\n", pair->filename, gnome_vfs_result_to_string (info->result));
2766 save_mime_part_info_free (info, FALSE);
2769 g_idle_add ((GSourceFunc) idle_save_mime_part_show_result, info);
2774 save_mime_parts_to_file_with_checks (SaveMimePartInfo *info)
2776 gboolean is_ok = TRUE;
2777 gint replaced_files = 0;
2778 const GList *files = info->pairs;
2781 for (iter = files; (iter != NULL) && (replaced_files < 2); iter = g_list_next(iter)) {
2782 SaveMimePartPair *pair = iter->data;
2783 if (modest_utils_file_exists (pair->filename)) {
2787 if (replaced_files) {
2788 GtkWidget *confirm_overwrite_dialog;
2789 const gchar *message = (replaced_files == 1) ?
2790 _FM("docm_nc_replace_file") : _FM("docm_nc_replace_multiple");
2791 confirm_overwrite_dialog = hildon_note_new_confirmation (NULL, message);
2792 if (gtk_dialog_run (GTK_DIALOG (confirm_overwrite_dialog)) != GTK_RESPONSE_OK) {
2795 gtk_widget_destroy (confirm_overwrite_dialog);
2799 save_mime_part_info_free (info, TRUE);
2801 GtkWidget *banner = hildon_banner_show_animation (NULL, NULL,
2802 _CS("sfil_ib_saving"));
2803 info->banner = banner;
2804 g_thread_create ((GThreadFunc)save_mime_part_to_file, info, FALSE, NULL);
2810 save_attachments_response (GtkDialog *dialog,
2814 TnyList *mime_parts;
2816 GList *files_to_save = NULL;
2818 mime_parts = TNY_LIST (user_data);
2820 if (arg1 != GTK_RESPONSE_OK)
2823 chooser_uri = gtk_file_chooser_get_uri (GTK_FILE_CHOOSER (dialog));
2825 if (!modest_utils_folder_writable (chooser_uri)) {
2826 hildon_banner_show_information
2827 (NULL, NULL, dgettext("hildon-fm", "sfil_ib_readonly_location"));
2831 iter = tny_list_create_iterator (mime_parts);
2832 while (!tny_iterator_is_done (iter)) {
2833 TnyMimePart *mime_part = (TnyMimePart *) tny_iterator_get_current (iter);
2835 if ((modest_tny_mime_part_is_attachment_for_modest (mime_part)) &&
2836 !tny_mime_part_is_purged (mime_part) &&
2837 (tny_mime_part_get_filename (mime_part) != NULL)) {
2838 SaveMimePartPair *pair;
2840 pair = g_slice_new0 (SaveMimePartPair);
2842 if (tny_list_get_length (mime_parts) > 1) {
2844 gnome_vfs_escape_slashes (tny_mime_part_get_filename (mime_part));
2845 pair->filename = g_build_filename (chooser_uri, escaped, NULL);
2848 pair->filename = g_strdup (chooser_uri);
2850 pair->part = mime_part;
2851 files_to_save = g_list_prepend (files_to_save, pair);
2853 tny_iterator_next (iter);
2855 g_object_unref (iter);
2857 g_free (chooser_uri);
2859 if (files_to_save != NULL) {
2860 SaveMimePartInfo *info = g_slice_new0 (SaveMimePartInfo);
2861 info->pairs = files_to_save;
2862 info->result = TRUE;
2863 save_mime_parts_to_file_with_checks (info);
2867 /* Free and close the dialog */
2868 g_object_unref (mime_parts);
2869 gtk_widget_destroy (GTK_WIDGET (dialog));
2873 modest_msg_view_window_save_attachments (ModestMsgViewWindow *window, TnyList *mime_parts)
2875 ModestMsgViewWindowPrivate *priv;
2876 GtkWidget *save_dialog = NULL;
2877 gchar *folder = NULL;
2878 gchar *filename = NULL;
2879 gchar *save_multiple_str = NULL;
2881 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window));
2882 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
2884 if (mime_parts == NULL) {
2885 mime_parts = modest_msg_view_get_selected_attachments (MODEST_MSG_VIEW (priv->msg_view));
2886 if (mime_parts == NULL || tny_list_get_length (mime_parts) == 0)
2889 g_object_ref (mime_parts);
2892 /* prepare dialog */
2893 if (tny_list_get_length (mime_parts) == 1) {
2895 /* only one attachment selected */
2896 iter = tny_list_create_iterator (mime_parts);
2897 TnyMimePart *mime_part = (TnyMimePart *) tny_iterator_get_current (iter);
2898 g_object_unref (iter);
2899 if (!modest_tny_mime_part_is_msg (mime_part) &&
2900 modest_tny_mime_part_is_attachment_for_modest (mime_part) &&
2901 !tny_mime_part_is_purged (mime_part)) {
2902 filename = g_strdup (tny_mime_part_get_filename (mime_part));
2904 /* TODO: show any error? */
2905 g_warning ("Tried to save a non-file attachment");
2906 g_object_unref (mime_parts);
2909 g_object_unref (mime_part);
2911 save_multiple_str = g_strdup_printf (_FM("sfil_va_number_of_objects_attachments"),
2912 tny_list_get_length (mime_parts));
2915 save_dialog = hildon_file_chooser_dialog_new (GTK_WINDOW (window),
2916 GTK_FILE_CHOOSER_ACTION_SAVE);
2919 folder = g_build_filename (g_get_home_dir (), DEFAULT_FOLDER, NULL);
2920 gtk_file_chooser_set_current_folder (GTK_FILE_CHOOSER (save_dialog), folder);
2925 gtk_file_chooser_set_current_name (GTK_FILE_CHOOSER (save_dialog),
2930 /* if multiple, set multiple string */
2931 if (save_multiple_str) {
2932 g_object_set (G_OBJECT (save_dialog), "save-multiple", save_multiple_str, NULL);
2933 gtk_window_set_title (GTK_WINDOW (save_dialog), _FM("sfil_ti_save_objects_files"));
2936 /* We must run this asynchronously, because the hildon dialog
2937 performs a gtk_dialog_run by itself which leads to gdk
2939 g_signal_connect (save_dialog, "response",
2940 G_CALLBACK (save_attachments_response), mime_parts);
2942 gtk_widget_show_all (save_dialog);
2946 show_remove_attachment_information (gpointer userdata)
2948 ModestMsgViewWindow *window = (ModestMsgViewWindow *) userdata;
2949 ModestMsgViewWindowPrivate *priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
2951 /* We're outside the main lock */
2952 gdk_threads_enter ();
2954 if (priv->remove_attachment_banner != NULL) {
2955 gtk_widget_destroy (priv->remove_attachment_banner);
2956 g_object_unref (priv->remove_attachment_banner);
2959 priv->remove_attachment_banner = g_object_ref (
2960 hildon_banner_show_animation (NULL, NULL, _("mcen_ib_removing_attachment")));
2962 gdk_threads_leave ();
2968 modest_msg_view_window_remove_attachments (ModestMsgViewWindow *window, gboolean get_all)
2970 ModestMsgViewWindowPrivate *priv;
2971 TnyList *mime_parts = NULL;
2972 gchar *confirmation_message;
2978 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window));
2979 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
2982 mime_parts = modest_msg_view_get_attachments (MODEST_MSG_VIEW (priv->msg_view));
2984 mime_parts = modest_msg_view_get_selected_attachments (MODEST_MSG_VIEW (priv->msg_view));
2986 /* Remove already purged messages from mime parts list */
2987 iter = tny_list_create_iterator (mime_parts);
2988 while (!tny_iterator_is_done (iter)) {
2989 TnyMimePart *part = TNY_MIME_PART (tny_iterator_get_current (iter));
2990 tny_iterator_next (iter);
2991 if (tny_mime_part_is_purged (part)) {
2992 tny_list_remove (mime_parts, (GObject *) part);
2994 g_object_unref (part);
2996 g_object_unref (iter);
2998 if (tny_list_get_length (mime_parts) == 0) {
2999 g_object_unref (mime_parts);
3003 n_attachments = tny_list_get_length (mime_parts);
3004 if (n_attachments == 1) {
3008 iter = tny_list_create_iterator (mime_parts);
3009 part = (TnyMimePart *) tny_iterator_get_current (iter);
3010 g_object_unref (iter);
3011 if (modest_tny_mime_part_is_msg (part)) {
3013 header = tny_msg_get_header (TNY_MSG (part));
3014 filename = tny_header_dup_subject (header);
3015 g_object_unref (header);
3016 if (filename == NULL)
3017 filename = g_strdup (_("mail_va_no_subject"));
3019 filename = g_strdup (tny_mime_part_get_filename (TNY_MIME_PART (part)));
3021 confirmation_message = g_strdup_printf (_("mcen_nc_purge_file_text"), filename);
3023 g_object_unref (part);
3025 confirmation_message = g_strdup_printf (ngettext("mcen_nc_purge_file_text",
3026 "mcen_nc_purge_files_text",
3027 n_attachments), n_attachments);
3029 response = modest_platform_run_confirmation_dialog (GTK_WINDOW (window),
3030 confirmation_message);
3031 g_free (confirmation_message);
3033 if (response != GTK_RESPONSE_OK) {
3034 g_object_unref (mime_parts);
3038 priv->purge_timeout = g_timeout_add (2000, show_remove_attachment_information, window);
3040 iter = tny_list_create_iterator (mime_parts);
3041 while (!tny_iterator_is_done (iter)) {
3044 part = (TnyMimePart *) tny_iterator_get_current (iter);
3045 tny_mime_part_set_purged (TNY_MIME_PART (part));
3046 g_object_unref (part);
3047 tny_iterator_next (iter);
3049 g_object_unref (iter);
3051 msg = tny_msg_view_get_msg (TNY_MSG_VIEW (priv->msg_view));
3052 tny_msg_view_clear (TNY_MSG_VIEW (priv->msg_view));
3053 tny_msg_rewrite_cache (msg);
3054 tny_msg_view_set_msg (TNY_MSG_VIEW (priv->msg_view), msg);
3055 g_object_unref (msg);
3057 g_object_unref (mime_parts);
3059 if (priv->purge_timeout > 0) {
3060 g_source_remove (priv->purge_timeout);
3061 priv->purge_timeout = 0;
3064 if (priv->remove_attachment_banner) {
3065 gtk_widget_destroy (priv->remove_attachment_banner);
3066 g_object_unref (priv->remove_attachment_banner);
3067 priv->remove_attachment_banner = NULL;
3075 update_window_title (ModestMsgViewWindow *window)
3077 ModestMsgViewWindowPrivate *priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
3079 TnyHeader *header = NULL;
3080 gchar *subject = NULL;
3082 msg = tny_msg_view_get_msg (TNY_MSG_VIEW (priv->msg_view));
3085 header = tny_msg_get_header (msg);
3086 subject = tny_header_dup_subject (header);
3087 g_object_unref (header);
3088 g_object_unref (msg);
3091 if ((subject == NULL)||(subject[0] == '\0')) {
3093 subject = g_strdup (_("mail_va_no_subject"));
3096 gtk_window_set_title (GTK_WINDOW (window), subject);
3100 static void on_move_focus (GtkWidget *widget,
3101 GtkDirectionType direction,
3104 g_signal_stop_emission_by_name (G_OBJECT (widget), "move-focus");
3108 fetch_image_open_stream (TnyStreamCache *self, gint64 *expected_size, gchar *uri)
3110 GnomeVFSResult result;
3111 GnomeVFSHandle *handle = NULL;
3112 GnomeVFSFileInfo *info = NULL;
3115 result = gnome_vfs_open (&handle, uri, GNOME_VFS_OPEN_READ);
3116 if (result != GNOME_VFS_OK) {
3121 info = gnome_vfs_file_info_new ();
3122 result = gnome_vfs_get_file_info_from_handle (handle, info, GNOME_VFS_FILE_INFO_DEFAULT);
3123 if (result != GNOME_VFS_OK || ! (info->valid_fields & GNOME_VFS_FILE_INFO_FIELDS_SIZE)) {
3124 /* We put a "safe" default size for going to cache */
3125 *expected_size = (300*1024);
3127 *expected_size = info->size;
3129 gnome_vfs_file_info_unref (info);
3131 stream = tny_vfs_stream_new (handle);
3140 TnyStream *output_stream;
3141 GtkWidget *msg_view;
3145 on_fetch_image_idle_refresh_view (gpointer userdata)
3148 FetchImageData *fidata = (FetchImageData *) userdata;
3149 g_message ("REFRESH VIEW");
3150 if (GTK_WIDGET_DRAWABLE (fidata->msg_view)) {
3151 g_message ("QUEUING DRAW");
3152 gtk_widget_queue_draw (fidata->msg_view);
3154 g_object_unref (fidata->msg_view);
3155 g_slice_free (FetchImageData, fidata);
3160 on_fetch_image_thread (gpointer userdata)
3162 FetchImageData *fidata = (FetchImageData *) userdata;
3163 TnyStreamCache *cache;
3164 TnyStream *cache_stream;
3166 cache = modest_runtime_get_images_cache ();
3167 cache_stream = tny_stream_cache_get_stream (cache, fidata->cache_id, (TnyStreamCacheOpenStreamFetcher) fetch_image_open_stream, (gpointer) fidata->uri);
3168 g_free (fidata->cache_id);
3169 g_free (fidata->uri);
3171 if (cache_stream != NULL) {
3172 tny_stream_write_to_stream (cache_stream, fidata->output_stream);
3173 tny_stream_close (cache_stream);
3174 g_object_unref (cache_stream);
3177 tny_stream_close (fidata->output_stream);
3178 g_object_unref (fidata->output_stream);
3181 gdk_threads_enter ();
3182 g_idle_add (on_fetch_image_idle_refresh_view, fidata);
3183 gdk_threads_leave ();
3189 on_fetch_image (ModestMsgView *msgview,
3192 ModestMsgViewWindow *window)
3194 const gchar *current_account;
3195 ModestMsgViewWindowPrivate *priv;
3196 FetchImageData *fidata;
3198 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
3200 current_account = modest_window_get_active_account (MODEST_WINDOW (window));
3202 fidata = g_slice_new0 (FetchImageData);
3203 fidata->msg_view = g_object_ref (msgview);
3204 fidata->uri = g_strdup (uri);
3205 fidata->cache_id = modest_images_cache_get_id (current_account, uri);
3206 fidata->output_stream = g_object_ref (stream);
3208 if (g_thread_create (on_fetch_image_thread, fidata, FALSE, NULL) == NULL) {
3209 g_object_unref (fidata->output_stream);
3210 g_free (fidata->cache_id);
3211 g_free (fidata->uri);
3212 g_object_unref (fidata->msg_view);
3213 g_slice_free (FetchImageData, fidata);
3214 tny_stream_close (stream);
3222 add_to_menu (ModestMsgViewWindow *self,
3223 HildonAppMenu *menu,
3226 ModestDimmingRulesGroup *dimming_group,
3227 GCallback dimming_callback)
3231 button = gtk_button_new_with_label (label);
3232 g_signal_connect_after (G_OBJECT (button), "clicked",
3233 callback, (gpointer) self);
3234 modest_dimming_rules_group_add_widget_rule (dimming_group,
3237 MODEST_WINDOW (self));
3238 hildon_app_menu_append (menu, GTK_BUTTON (button));
3242 setup_menu (ModestMsgViewWindow *self, ModestDimmingRulesGroup *group)
3244 ModestMsgViewWindowPrivate *priv = NULL;
3245 GtkWidget *app_menu;
3247 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW(self));
3249 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
3251 app_menu = hildon_app_menu_new ();
3253 /* Settings menu buttons */
3254 add_to_menu (self, HILDON_APP_MENU (app_menu), _("mcen_me_inbox_reply"),
3255 G_CALLBACK (modest_ui_actions_on_reply),
3256 group, G_CALLBACK (modest_ui_dimming_rules_on_reply_msg));
3257 add_to_menu (self, HILDON_APP_MENU (app_menu), _("mcen_me_inbox_replytoall"),
3258 G_CALLBACK (modest_ui_actions_on_reply_all),
3259 group, G_CALLBACK (modest_ui_dimming_rules_on_reply_msg));
3260 add_to_menu (self, HILDON_APP_MENU (app_menu), _("mcen_me_inbox_forward"),
3261 G_CALLBACK (modest_ui_actions_on_forward),
3262 group, G_CALLBACK (modest_ui_dimming_rules_on_reply_msg));
3263 add_to_menu (self, HILDON_APP_MENU (app_menu), _("mcen_me_viewer_newemail"),
3264 G_CALLBACK (modest_ui_actions_on_new_msg),
3265 group, G_CALLBACK (modest_ui_dimming_rules_on_new_msg));
3266 add_to_menu (self, HILDON_APP_MENU (app_menu), _("mcen_me_inbox_messagedetails"),
3267 G_CALLBACK (modest_ui_actions_on_details),
3268 group, G_CALLBACK (modest_ui_dimming_rules_on_details));
3269 add_to_menu (self, HILDON_APP_MENU (app_menu), _("mcen_me_viewer_save_attachments"),
3270 G_CALLBACK (modest_ui_actions_save_attachments),
3271 group, G_CALLBACK (modest_ui_dimming_rules_on_save_attachments));
3272 add_to_menu (self, HILDON_APP_MENU (app_menu), _("mcen_me_inbox_remove_attachments"),
3273 G_CALLBACK (modest_ui_actions_remove_attachments),
3274 group, G_CALLBACK (modest_ui_dimming_rules_on_remove_attachments));
3275 add_to_menu (self, HILDON_APP_MENU (app_menu), _("mcen_me_viewer_addtocontacts"),
3276 G_CALLBACK (modest_ui_actions_on_add_to_contacts),
3277 group, G_CALLBACK (modest_ui_dimming_rules_on_add_to_contacts));
3279 hildon_stackable_window_set_main_menu (HILDON_STACKABLE_WINDOW (self),
3280 HILDON_APP_MENU (app_menu));
3284 modest_msg_view_window_toggle_menu (HildonWindow *window,
3288 modest_ui_actions_check_menu_dimming_rules (MODEST_WINDOW (window));
3290 return HILDON_WINDOW_CLASS (parent_class)->toggle_menu (window, button, time);