1 /* Copyright (c) 2006, Nokia Corporation
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions are
8 * * Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 * * Redistributions in binary form must reproduce the above copyright
11 * notice, this list of conditions and the following disclaimer in the
12 * documentation and/or other materials provided with the distribution.
13 * * Neither the name of the Nokia Corporation nor the names of its
14 * contributors may be used to endorse or promote products derived from
15 * this software without specific prior written permission.
17 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
18 * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
19 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
20 * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
21 * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
22 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
23 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
24 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
25 * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
26 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
27 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29 #include <glib/gi18n.h>
31 #include <tny-account-store.h>
32 #include <tny-simple-list.h>
34 #include <tny-mime-part.h>
35 #include <tny-vfs-stream.h>
36 #include <tny-error.h>
37 #include "modest-marshal.h"
38 #include "modest-platform.h"
39 #include <modest-utils.h>
40 #include <modest-maemo-utils.h>
41 #include <modest-tny-msg.h>
42 #include <modest-msg-view-window.h>
43 #include <modest-main-window-ui.h>
44 #include "modest-msg-view-window-ui-dimming.h"
45 #include <modest-widget-memory.h>
46 #include <modest-progress-object.h>
47 #include <modest-runtime.h>
48 #include <modest-window-priv.h>
49 #include <modest-tny-folder.h>
50 #include <modest-text-utils.h>
51 #include <modest-account-mgr-helpers.h>
52 #include <hildon/hildon-pannable-area.h>
53 #include <hildon/hildon-picker-dialog.h>
54 #include <hildon/hildon-app-menu.h>
55 #include "modest-defs.h"
56 #include "modest-hildon-includes.h"
57 #include "modest-ui-dimming-manager.h"
58 #include <gdk/gdkkeysyms.h>
59 #include <modest-tny-account.h>
60 #include <modest-mime-part-view.h>
61 #include <modest-isearch-view.h>
62 #include <modest-tny-mime-part.h>
63 #include <modest-address-book.h>
66 #include <glib/gstdio.h>
67 #include <modest-debug.h>
68 #include <modest-header-window.h>
70 #define MYDOCS_ENV "MYDOCSDIR"
71 #define DOCS_FOLDER ".documents"
73 typedef struct _ModestMsgViewWindowPrivate ModestMsgViewWindowPrivate;
74 struct _ModestMsgViewWindowPrivate {
77 GtkWidget *main_scroll;
78 GtkWidget *find_toolbar;
81 /* Progress observers */
82 GSList *progress_widgets;
85 GtkWidget *prev_toolitem;
86 GtkWidget *next_toolitem;
87 gboolean progress_hint;
90 /* Optimized view enabled */
91 gboolean optimized_view;
93 /* Whether this was created via the *_new_for_search_result() function. */
94 gboolean is_search_result;
96 /* Whether the message is in outbox */
99 /* A reference to the @model of the header view
100 * to allow selecting previous/next messages,
101 * if the message is currently selected in the header view.
103 const gchar *header_folder_id;
104 GtkTreeModel *header_model;
105 GtkTreeRowReference *row_reference;
106 GtkTreeRowReference *next_row_reference;
108 gulong clipboard_change_handler;
109 gulong queue_change_handler;
110 gulong account_removed_handler;
111 gulong row_changed_handler;
112 gulong row_deleted_handler;
113 gulong row_inserted_handler;
114 gulong rows_reordered_handler;
117 GtkWidget *remove_attachment_banner;
124 static void modest_msg_view_window_class_init (ModestMsgViewWindowClass *klass);
125 static void modest_msg_view_window_init (ModestMsgViewWindow *obj);
126 static void modest_header_view_observer_init(
127 ModestHeaderViewObserverIface *iface_class);
128 static void modest_msg_view_window_finalize (GObject *obj);
129 static void modest_msg_view_window_toggle_find_toolbar (GtkToggleAction *obj,
131 static void modest_msg_view_window_find_toolbar_close (GtkWidget *widget,
132 ModestMsgViewWindow *obj);
133 static void modest_msg_view_window_find_toolbar_search (GtkWidget *widget,
134 ModestMsgViewWindow *obj);
136 static void modest_msg_view_window_disconnect_signals (ModestWindow *self);
138 static gdouble modest_msg_view_window_get_zoom (ModestWindow *window);
139 static void modest_msg_view_window_set_zoom (ModestWindow *window,
141 static gboolean modest_msg_view_window_zoom_minus (ModestWindow *window);
142 static gboolean modest_msg_view_window_zoom_plus (ModestWindow *window);
143 static gboolean modest_msg_view_window_key_event (GtkWidget *window,
146 static void modest_msg_view_window_update_priority (ModestMsgViewWindow *window);
148 static void modest_msg_view_window_show_toolbar (ModestWindow *window,
149 gboolean show_toolbar);
151 static void modest_msg_view_window_clipboard_owner_change (GtkClipboard *clipboard,
153 ModestMsgViewWindow *window);
155 static void modest_msg_view_window_on_row_changed (GtkTreeModel *header_model,
158 ModestMsgViewWindow *window);
160 static void modest_msg_view_window_on_row_deleted (GtkTreeModel *header_model,
162 ModestMsgViewWindow *window);
164 static void modest_msg_view_window_on_row_inserted (GtkTreeModel *header_model,
165 GtkTreePath *tree_path,
166 GtkTreeIter *tree_iter,
167 ModestMsgViewWindow *window);
169 static void modest_msg_view_window_on_row_reordered (GtkTreeModel *header_model,
173 ModestMsgViewWindow *window);
175 static void modest_msg_view_window_update_model_replaced (ModestHeaderViewObserver *window,
177 const gchar *tny_folder_id);
179 static void on_queue_changed (ModestMailOperationQueue *queue,
180 ModestMailOperation *mail_op,
181 ModestMailOperationQueueNotification type,
182 ModestMsgViewWindow *self);
184 static void on_account_removed (TnyAccountStore *account_store,
188 static void on_move_focus (GtkWidget *widget,
189 GtkDirectionType direction,
192 static void view_msg_cb (ModestMailOperation *mail_op,
199 static void set_progress_hint (ModestMsgViewWindow *self,
202 static void update_window_title (ModestMsgViewWindow *window);
204 static gboolean set_toolbar_transfer_mode (ModestMsgViewWindow *self);
205 static void init_window (ModestMsgViewWindow *obj);
207 static gboolean msg_is_visible (TnyHeader *header, gboolean check_outbox);
209 static void check_dimming_rules_after_change (ModestMsgViewWindow *window);
211 static gboolean on_fetch_image (ModestMsgView *msgview,
214 ModestMsgViewWindow *window);
216 static gboolean modest_msg_view_window_scroll_child (ModestMsgViewWindow *self,
217 GtkScrollType scroll_type,
220 static gboolean message_reader (ModestMsgViewWindow *window,
221 ModestMsgViewWindowPrivate *priv,
223 GtkTreeRowReference *row_reference);
225 static void setup_menu (ModestMsgViewWindow *self);
226 static gboolean _modest_msg_view_window_map_event (GtkWidget *widget,
231 /* list my signals */
238 static const GtkToggleActionEntry msg_view_toggle_action_entries [] = {
239 { "FindInMessage", MODEST_TOOLBAR_ICON_FIND, N_("qgn_toolb_gene_find"), NULL, NULL, G_CALLBACK (modest_msg_view_window_toggle_find_toolbar), FALSE },
243 #define MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE((o), \
244 MODEST_TYPE_MSG_VIEW_WINDOW, \
245 ModestMsgViewWindowPrivate))
247 static GtkWindowClass *parent_class = NULL;
249 /* uncomment the following if you have defined any signals */
250 static guint signals[LAST_SIGNAL] = {0};
253 modest_msg_view_window_get_type (void)
255 static GType my_type = 0;
257 static const GTypeInfo my_info = {
258 sizeof(ModestMsgViewWindowClass),
259 NULL, /* base init */
260 NULL, /* base finalize */
261 (GClassInitFunc) modest_msg_view_window_class_init,
262 NULL, /* class finalize */
263 NULL, /* class data */
264 sizeof(ModestMsgViewWindow),
266 (GInstanceInitFunc) modest_msg_view_window_init,
269 my_type = g_type_register_static (MODEST_TYPE_HILDON2_WINDOW,
270 "ModestMsgViewWindow",
273 static const GInterfaceInfo modest_header_view_observer_info =
275 (GInterfaceInitFunc) modest_header_view_observer_init,
276 NULL, /* interface_finalize */
277 NULL /* interface_data */
280 g_type_add_interface_static (my_type,
281 MODEST_TYPE_HEADER_VIEW_OBSERVER,
282 &modest_header_view_observer_info);
288 save_state (ModestWindow *self)
290 modest_widget_memory_save (modest_runtime_get_conf (),
292 MODEST_CONF_MSG_VIEW_WINDOW_KEY);
296 gboolean modest_msg_view_window_scroll_child (ModestMsgViewWindow *self,
297 GtkScrollType scroll_type,
301 ModestMsgViewWindowPrivate *priv;
302 gboolean return_value;
304 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(self);
305 g_signal_emit_by_name (priv->main_scroll, "scroll-child", scroll_type, horizontal, &return_value);
310 add_scroll_binding (GtkBindingSet *binding_set,
312 GtkScrollType scroll)
314 guint keypad_keyval = keyval - GDK_Left + GDK_KP_Left;
316 gtk_binding_entry_add_signal (binding_set, keyval, 0,
318 GTK_TYPE_SCROLL_TYPE, scroll,
319 G_TYPE_BOOLEAN, FALSE);
320 gtk_binding_entry_add_signal (binding_set, keypad_keyval, 0,
322 GTK_TYPE_SCROLL_TYPE, scroll,
323 G_TYPE_BOOLEAN, FALSE);
327 modest_msg_view_window_class_init (ModestMsgViewWindowClass *klass)
329 GObjectClass *gobject_class;
330 HildonWindowClass *hildon_window_class;
331 ModestWindowClass *modest_window_class;
332 GtkBindingSet *binding_set;
334 gobject_class = (GObjectClass*) klass;
335 hildon_window_class = (HildonWindowClass *) klass;
336 modest_window_class = (ModestWindowClass *) klass;
338 parent_class = g_type_class_peek_parent (klass);
339 gobject_class->finalize = modest_msg_view_window_finalize;
341 modest_window_class->set_zoom_func = modest_msg_view_window_set_zoom;
342 modest_window_class->get_zoom_func = modest_msg_view_window_get_zoom;
343 modest_window_class->zoom_plus_func = modest_msg_view_window_zoom_plus;
344 modest_window_class->zoom_minus_func = modest_msg_view_window_zoom_minus;
345 modest_window_class->show_toolbar_func = modest_msg_view_window_show_toolbar;
346 modest_window_class->disconnect_signals_func = modest_msg_view_window_disconnect_signals;
348 modest_window_class->save_state_func = save_state;
350 klass->scroll_child = modest_msg_view_window_scroll_child;
352 signals[MSG_CHANGED_SIGNAL] =
353 g_signal_new ("msg-changed",
354 G_TYPE_FROM_CLASS (gobject_class),
356 G_STRUCT_OFFSET (ModestMsgViewWindowClass, msg_changed),
358 modest_marshal_VOID__POINTER_POINTER,
359 G_TYPE_NONE, 2, G_TYPE_POINTER, G_TYPE_POINTER);
361 signals[SCROLL_CHILD_SIGNAL] =
362 g_signal_new ("scroll-child",
363 G_TYPE_FROM_CLASS (gobject_class),
364 G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
365 G_STRUCT_OFFSET (ModestMsgViewWindowClass, scroll_child),
367 modest_marshal_BOOLEAN__ENUM_BOOLEAN,
368 G_TYPE_BOOLEAN, 2, GTK_TYPE_SCROLL_TYPE, G_TYPE_BOOLEAN);
370 binding_set = gtk_binding_set_by_class (klass);
371 add_scroll_binding (binding_set, GDK_Up, GTK_SCROLL_STEP_UP);
372 add_scroll_binding (binding_set, GDK_Down, GTK_SCROLL_STEP_DOWN);
373 add_scroll_binding (binding_set, GDK_Page_Up, GTK_SCROLL_PAGE_UP);
374 add_scroll_binding (binding_set, GDK_Page_Down, GTK_SCROLL_PAGE_DOWN);
375 add_scroll_binding (binding_set, GDK_Home, GTK_SCROLL_START);
376 add_scroll_binding (binding_set, GDK_End, GTK_SCROLL_END);
378 g_type_class_add_private (gobject_class, sizeof(ModestMsgViewWindowPrivate));
382 static void modest_header_view_observer_init(
383 ModestHeaderViewObserverIface *iface_class)
385 iface_class->update_func = modest_msg_view_window_update_model_replaced;
389 modest_msg_view_window_init (ModestMsgViewWindow *obj)
391 ModestMsgViewWindowPrivate *priv;
392 ModestWindowPrivate *parent_priv = NULL;
393 GtkActionGroup *action_group = NULL;
394 GError *error = NULL;
395 GdkPixbuf *window_icon;
397 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(obj);
398 parent_priv = MODEST_WINDOW_GET_PRIVATE(obj);
399 parent_priv->ui_manager = gtk_ui_manager_new();
401 action_group = gtk_action_group_new ("ModestMsgViewWindowActions");
402 gtk_action_group_set_translation_domain (action_group, GETTEXT_PACKAGE);
404 /* Add common actions */
405 gtk_action_group_add_actions (action_group,
406 modest_action_entries,
407 G_N_ELEMENTS (modest_action_entries),
409 gtk_action_group_add_toggle_actions (action_group,
410 msg_view_toggle_action_entries,
411 G_N_ELEMENTS (msg_view_toggle_action_entries),
414 gtk_ui_manager_insert_action_group (parent_priv->ui_manager, action_group, 0);
415 g_object_unref (action_group);
417 /* Load the UI definition */
418 gtk_ui_manager_add_ui_from_file (parent_priv->ui_manager, MODEST_UIDIR "modest-msg-view-window-ui.xml",
421 g_printerr ("modest: could not merge modest-msg-view-window-ui.xml: %s\n", error->message);
422 g_error_free (error);
427 /* Add accelerators */
428 gtk_window_add_accel_group (GTK_WINDOW (obj),
429 gtk_ui_manager_get_accel_group (parent_priv->ui_manager));
431 priv->is_search_result = FALSE;
432 priv->is_outbox = FALSE;
434 priv->msg_view = NULL;
435 priv->header_model = NULL;
436 priv->header_folder_id = NULL;
437 priv->clipboard_change_handler = 0;
438 priv->queue_change_handler = 0;
439 priv->account_removed_handler = 0;
440 priv->row_changed_handler = 0;
441 priv->row_deleted_handler = 0;
442 priv->row_inserted_handler = 0;
443 priv->rows_reordered_handler = 0;
444 priv->progress_hint = FALSE;
445 priv->fetching_images = 0;
447 priv->optimized_view = FALSE;
448 priv->purge_timeout = 0;
449 priv->remove_attachment_banner = NULL;
450 priv->msg_uid = NULL;
452 priv->sighandlers = NULL;
455 init_window (MODEST_MSG_VIEW_WINDOW(obj));
457 /* Set window icon */
458 window_icon = modest_platform_get_icon (MODEST_APP_MSG_VIEW_ICON, MODEST_ICON_SIZE_BIG);
460 gtk_window_set_icon (GTK_WINDOW (obj), window_icon);
461 g_object_unref (window_icon);
464 hildon_program_add_window (hildon_program_get_instance(),
471 set_toolbar_transfer_mode (ModestMsgViewWindow *self)
473 ModestMsgViewWindowPrivate *priv = NULL;
475 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self), FALSE);
477 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(self);
479 set_progress_hint (self, TRUE);
485 update_progress_hint (ModestMsgViewWindow *self)
487 ModestMsgViewWindowPrivate *priv;
488 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(self);
490 if (GTK_WIDGET_VISIBLE (self)) {
491 hildon_gtk_window_set_progress_indicator (GTK_WINDOW (self),
492 (priv->progress_hint || (priv->fetching_images > 0))?1:0);
497 set_progress_hint (ModestMsgViewWindow *self,
500 ModestWindowPrivate *parent_priv;
501 ModestMsgViewWindowPrivate *priv;
503 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self));
505 parent_priv = MODEST_WINDOW_GET_PRIVATE(self);
506 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(self);
508 /* Sets current progress hint */
509 priv->progress_hint = enabled;
511 update_progress_hint (self);
517 init_window (ModestMsgViewWindow *obj)
519 GtkWidget *main_vbox;
520 ModestMsgViewWindowPrivate *priv;
522 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(obj);
524 priv->msg_view = GTK_WIDGET (tny_platform_factory_new_msg_view (modest_tny_platform_factory_get_instance ()));
525 modest_msg_view_set_shadow_type (MODEST_MSG_VIEW (priv->msg_view), GTK_SHADOW_NONE);
526 main_vbox = gtk_vbox_new (FALSE, 6);
527 #ifdef MODEST_TOOLKIT_HILDON2
528 priv->main_scroll = hildon_pannable_area_new ();
529 gtk_container_add (GTK_CONTAINER (priv->main_scroll), priv->msg_view);
531 #ifdef MODEST_USE_MOZEMBED
532 priv->main_scroll = priv->msg_view;
533 gtk_widget_set_size_request (priv->msg_view, -1, 1600);
535 priv->main_scroll = gtk_scrolled_window_new (NULL, NULL);
536 gtk_container_add (GTK_CONTAINER (priv->main_scroll), priv->msg_view);
538 gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (priv->main_scroll), GTK_POLICY_NEVER, GTK_POLICY_AUTOMATIC);
539 gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW (priv->main_scroll), GTK_SHADOW_NONE);
540 modest_maemo_set_thumbable_scrollbar (GTK_SCROLLED_WINDOW(priv->main_scroll), TRUE);
543 gtk_box_pack_start (GTK_BOX(main_vbox), priv->main_scroll, TRUE, TRUE, 0);
544 gtk_container_add (GTK_CONTAINER(obj), main_vbox);
546 priv->find_toolbar = hildon_find_toolbar_new (NULL);
547 hildon_window_add_toolbar (HILDON_WINDOW (obj), GTK_TOOLBAR (priv->find_toolbar));
548 gtk_widget_set_no_show_all (priv->find_toolbar, TRUE);
550 /* NULL-ize fields if the window is destroyed */
551 g_signal_connect (priv->msg_view, "destroy", G_CALLBACK (gtk_widget_destroyed), &(priv->msg_view));
553 gtk_widget_show_all (GTK_WIDGET(main_vbox));
557 modest_msg_view_window_disconnect_signals (ModestWindow *self)
559 ModestMsgViewWindowPrivate *priv;
560 GtkWidget *header_view = NULL;
561 GtkWindow *parent_window = NULL;
563 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
565 if (gtk_clipboard_get (GDK_SELECTION_PRIMARY) &&
566 g_signal_handler_is_connected (gtk_clipboard_get (GDK_SELECTION_PRIMARY),
567 priv->clipboard_change_handler))
568 g_signal_handler_disconnect (gtk_clipboard_get (GDK_SELECTION_PRIMARY),
569 priv->clipboard_change_handler);
571 if (g_signal_handler_is_connected (G_OBJECT (modest_runtime_get_mail_operation_queue ()),
572 priv->queue_change_handler))
573 g_signal_handler_disconnect (G_OBJECT (modest_runtime_get_mail_operation_queue ()),
574 priv->queue_change_handler);
576 if (g_signal_handler_is_connected (G_OBJECT (modest_runtime_get_account_store ()),
577 priv->account_removed_handler))
578 g_signal_handler_disconnect (G_OBJECT (modest_runtime_get_account_store ()),
579 priv->account_removed_handler);
581 if (priv->header_model) {
582 if (g_signal_handler_is_connected(G_OBJECT (priv->header_model),
583 priv->row_changed_handler))
584 g_signal_handler_disconnect(G_OBJECT (priv->header_model),
585 priv->row_changed_handler);
587 if (g_signal_handler_is_connected(G_OBJECT (priv->header_model),
588 priv->row_deleted_handler))
589 g_signal_handler_disconnect(G_OBJECT (priv->header_model),
590 priv->row_deleted_handler);
592 if (g_signal_handler_is_connected(G_OBJECT (priv->header_model),
593 priv->row_inserted_handler))
594 g_signal_handler_disconnect(G_OBJECT (priv->header_model),
595 priv->row_inserted_handler);
597 if (g_signal_handler_is_connected(G_OBJECT (priv->header_model),
598 priv->rows_reordered_handler))
599 g_signal_handler_disconnect(G_OBJECT (priv->header_model),
600 priv->rows_reordered_handler);
603 modest_signal_mgr_disconnect_all_and_destroy (priv->sighandlers);
604 priv->sighandlers = NULL;
606 parent_window = gtk_window_get_transient_for (GTK_WINDOW (self));
607 if (parent_window && MODEST_IS_HEADER_WINDOW (parent_window)) {
608 header_view = GTK_WIDGET (modest_header_window_get_header_view (MODEST_HEADER_WINDOW (parent_window)));
610 modest_header_view_remove_observer(MODEST_HEADER_VIEW (header_view),
611 MODEST_HEADER_VIEW_OBSERVER(self));
617 modest_msg_view_window_finalize (GObject *obj)
619 ModestMsgViewWindowPrivate *priv;
621 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (obj);
623 /* Sanity check: shouldn't be needed, the window mgr should
624 call this function before */
625 modest_msg_view_window_disconnect_signals (MODEST_WINDOW (obj));
627 if (priv->header_model != NULL) {
628 g_object_unref (priv->header_model);
629 priv->header_model = NULL;
632 if (priv->remove_attachment_banner) {
633 gtk_widget_destroy (priv->remove_attachment_banner);
634 g_object_unref (priv->remove_attachment_banner);
635 priv->remove_attachment_banner = NULL;
638 if (priv->purge_timeout > 0) {
639 g_source_remove (priv->purge_timeout);
640 priv->purge_timeout = 0;
643 if (priv->row_reference) {
644 gtk_tree_row_reference_free (priv->row_reference);
645 priv->row_reference = NULL;
648 if (priv->next_row_reference) {
649 gtk_tree_row_reference_free (priv->next_row_reference);
650 priv->next_row_reference = NULL;
654 g_free (priv->msg_uid);
655 priv->msg_uid = NULL;
658 G_OBJECT_CLASS(parent_class)->finalize (obj);
662 select_next_valid_row (GtkTreeModel *model,
663 GtkTreeRowReference **row_reference,
667 GtkTreeIter tmp_iter;
669 GtkTreePath *next = NULL;
670 gboolean retval = FALSE, finished;
672 g_return_val_if_fail (gtk_tree_row_reference_valid (*row_reference), FALSE);
674 path = gtk_tree_row_reference_get_path (*row_reference);
675 gtk_tree_model_get_iter (model, &tmp_iter, path);
676 gtk_tree_row_reference_free (*row_reference);
677 *row_reference = NULL;
681 TnyHeader *header = NULL;
683 if (gtk_tree_model_iter_next (model, &tmp_iter)) {
684 gtk_tree_model_get (model, &tmp_iter,
685 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
689 if (msg_is_visible (header, is_outbox)) {
690 next = gtk_tree_model_get_path (model, &tmp_iter);
691 *row_reference = gtk_tree_row_reference_new (model, next);
692 gtk_tree_path_free (next);
696 g_object_unref (header);
699 } else if (cycle && gtk_tree_model_get_iter_first (model, &tmp_iter)) {
700 next = gtk_tree_model_get_path (model, &tmp_iter);
702 /* Ensure that we are not selecting the same */
703 if (gtk_tree_path_compare (path, next) != 0) {
704 gtk_tree_model_get (model, &tmp_iter,
705 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
708 if (msg_is_visible (header, is_outbox)) {
709 *row_reference = gtk_tree_row_reference_new (model, next);
713 g_object_unref (header);
717 /* If we ended up in the same message
718 then there is no valid next
722 gtk_tree_path_free (next);
724 /* If there are no more messages and we don't
725 want to start again in the first one then
726 there is no valid next message */
732 gtk_tree_path_free (path);
737 /* TODO: This should be in _init(), with the parameters as properties. */
739 modest_msg_view_window_construct (ModestMsgViewWindow *self,
740 const gchar *modest_account_name,
741 const gchar *mailbox,
742 const gchar *msg_uid)
745 ModestMsgViewWindowPrivate *priv = NULL;
746 ModestWindowPrivate *parent_priv = NULL;
747 ModestDimmingRulesGroup *toolbar_rules_group = NULL;
748 ModestDimmingRulesGroup *clipboard_rules_group = NULL;
750 obj = G_OBJECT (self);
751 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(obj);
752 parent_priv = MODEST_WINDOW_GET_PRIVATE(obj);
754 priv->msg_uid = g_strdup (msg_uid);
757 parent_priv->menubar = NULL;
759 toolbar_rules_group = modest_dimming_rules_group_new (MODEST_DIMMING_RULES_TOOLBAR, TRUE);
760 clipboard_rules_group = modest_dimming_rules_group_new (MODEST_DIMMING_RULES_CLIPBOARD, FALSE);
763 /* Add common dimming rules */
764 modest_dimming_rules_group_add_rules (toolbar_rules_group,
765 modest_msg_view_toolbar_dimming_entries,
766 G_N_ELEMENTS (modest_msg_view_toolbar_dimming_entries),
767 MODEST_WINDOW (self));
768 modest_dimming_rules_group_add_rules (clipboard_rules_group,
769 modest_msg_view_clipboard_dimming_entries,
770 G_N_ELEMENTS (modest_msg_view_clipboard_dimming_entries),
771 MODEST_WINDOW (self));
773 /* Insert dimming rules group for this window */
774 modest_ui_dimming_manager_insert_rules_group (parent_priv->ui_dimming_manager, toolbar_rules_group);
775 modest_ui_dimming_manager_insert_rules_group (parent_priv->ui_dimming_manager, clipboard_rules_group);
776 g_object_unref (toolbar_rules_group);
777 g_object_unref (clipboard_rules_group);
779 /* g_signal_connect (G_OBJECT(obj), "delete-event", G_CALLBACK(on_delete_event), obj); */
781 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);
782 g_signal_connect (G_OBJECT(priv->msg_view), "activate_link",
783 G_CALLBACK (modest_ui_actions_on_msg_link_clicked), obj);
784 g_signal_connect (G_OBJECT(priv->msg_view), "link_hover",
785 G_CALLBACK (modest_ui_actions_on_msg_link_hover), obj);
786 g_signal_connect (G_OBJECT(priv->msg_view), "attachment_clicked",
787 G_CALLBACK (modest_ui_actions_on_msg_attachment_clicked), obj);
788 g_signal_connect (G_OBJECT(priv->msg_view), "recpt_activated",
789 G_CALLBACK (modest_ui_actions_on_msg_recpt_activated), obj);
790 g_signal_connect (G_OBJECT(priv->msg_view), "show_details",
791 G_CALLBACK (modest_ui_actions_on_details), obj);
792 g_signal_connect (G_OBJECT(priv->msg_view), "link_contextual",
793 G_CALLBACK (modest_ui_actions_on_msg_link_contextual), obj);
794 g_signal_connect (G_OBJECT (priv->msg_view), "fetch_image",
795 G_CALLBACK (on_fetch_image), obj);
797 g_signal_connect (G_OBJECT (obj), "key-release-event",
798 G_CALLBACK (modest_msg_view_window_key_event),
801 g_signal_connect (G_OBJECT (obj), "key-press-event",
802 G_CALLBACK (modest_msg_view_window_key_event),
805 g_signal_connect (G_OBJECT (obj), "move-focus",
806 G_CALLBACK (on_move_focus), obj);
808 g_signal_connect (G_OBJECT (obj), "map-event",
809 G_CALLBACK (_modest_msg_view_window_map_event),
812 /* Mail Operation Queue */
813 priv->queue_change_handler = g_signal_connect (G_OBJECT (modest_runtime_get_mail_operation_queue ()),
815 G_CALLBACK (on_queue_changed),
818 /* Account manager */
819 priv->account_removed_handler = g_signal_connect (G_OBJECT (modest_runtime_get_account_store ()),
821 G_CALLBACK(on_account_removed),
824 modest_window_set_active_account (MODEST_WINDOW(obj), modest_account_name);
825 modest_window_set_active_mailbox (MODEST_WINDOW(obj), mailbox);
827 g_signal_connect (G_OBJECT (priv->find_toolbar), "close", G_CALLBACK (modest_msg_view_window_find_toolbar_close), obj);
828 g_signal_connect (G_OBJECT (priv->find_toolbar), "search", G_CALLBACK (modest_msg_view_window_find_toolbar_search), obj);
829 priv->last_search = NULL;
831 modest_msg_view_window_show_toolbar (MODEST_WINDOW (obj), TRUE);
833 /* Init the clipboard actions dim status */
834 modest_msg_view_grab_focus(MODEST_MSG_VIEW (priv->msg_view));
836 update_window_title (MODEST_MSG_VIEW_WINDOW (obj));
841 /* FIXME: parameter checks */
843 modest_msg_view_window_new_with_header_model (TnyMsg *msg,
844 const gchar *modest_account_name,
845 const gchar *mailbox,
846 const gchar *msg_uid,
848 GtkTreeRowReference *row_reference)
850 ModestMsgViewWindow *window = NULL;
851 ModestMsgViewWindowPrivate *priv = NULL;
852 TnyFolder *header_folder = NULL;
853 ModestHeaderView *header_view = NULL;
854 ModestWindow *main_window = NULL;
855 ModestWindowMgr *mgr = NULL;
858 modest_tny_mime_part_to_string (TNY_MIME_PART (msg), 0);
861 mgr = modest_runtime_get_window_mgr ();
862 window = MODEST_MSG_VIEW_WINDOW (modest_window_mgr_get_msg_view_window (mgr));
863 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), NULL);
865 modest_msg_view_window_construct (window, modest_account_name, mailbox, msg_uid);
867 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
869 /* Remember the message list's TreeModel so we can detect changes
870 * and change the list selection when necessary: */
872 main_window = modest_window_mgr_get_main_window(mgr, FALSE); /* don't create */
874 header_view = MODEST_HEADER_VIEW(modest_main_window_get_child_widget(
875 MODEST_MAIN_WINDOW(main_window),
876 MODEST_MAIN_WINDOW_WIDGET_TYPE_HEADER_VIEW));
879 if (header_view != NULL){
880 header_folder = modest_header_view_get_folder(header_view);
881 /* This could happen if the header folder was
882 unseleted before opening this msg window (for
883 example if the user selects an account in the
884 folder view of the main window */
886 priv->is_outbox = (modest_tny_folder_guess_folder_type (header_folder) == TNY_FOLDER_TYPE_OUTBOX);
887 priv->header_folder_id = tny_folder_get_id(header_folder);
888 g_assert(priv->header_folder_id != NULL);
889 g_object_unref(header_folder);
893 /* Setup row references and connect signals */
894 priv->header_model = g_object_ref (model);
897 priv->row_reference = gtk_tree_row_reference_copy (row_reference);
898 priv->next_row_reference = gtk_tree_row_reference_copy (row_reference);
899 select_next_valid_row (model, &(priv->next_row_reference), TRUE, priv->is_outbox);
901 priv->row_reference = NULL;
902 priv->next_row_reference = NULL;
905 /* Connect signals */
906 priv->row_changed_handler =
907 g_signal_connect (GTK_TREE_MODEL(model), "row-changed",
908 G_CALLBACK(modest_msg_view_window_on_row_changed),
910 priv->row_deleted_handler =
911 g_signal_connect (GTK_TREE_MODEL(model), "row-deleted",
912 G_CALLBACK(modest_msg_view_window_on_row_deleted),
914 priv->row_inserted_handler =
915 g_signal_connect (GTK_TREE_MODEL(model), "row-inserted",
916 G_CALLBACK(modest_msg_view_window_on_row_inserted),
918 priv->rows_reordered_handler =
919 g_signal_connect(GTK_TREE_MODEL(model), "rows-reordered",
920 G_CALLBACK(modest_msg_view_window_on_row_reordered),
923 if (header_view != NULL){
924 modest_header_view_add_observer(header_view,
925 MODEST_HEADER_VIEW_OBSERVER(window));
928 tny_msg_view_set_msg (TNY_MSG_VIEW (priv->msg_view), msg);
929 update_window_title (MODEST_MSG_VIEW_WINDOW (window));
931 /* gtk_widget_show_all (GTK_WIDGET (window)); */
932 modest_msg_view_window_update_priority (window);
933 /* Check dimming rules */
934 modest_ui_actions_check_toolbar_dimming_rules (MODEST_WINDOW (window));
935 modest_ui_actions_check_menu_dimming_rules (MODEST_WINDOW (window));
936 modest_window_check_dimming_rules_group (MODEST_WINDOW (window), MODEST_DIMMING_RULES_CLIPBOARD);
938 return MODEST_WINDOW(window);
942 modest_msg_view_window_new_from_header_view (ModestHeaderView *header_view,
943 const gchar *modest_account_name,
944 const gchar *mailbox,
945 const gchar *msg_uid,
946 GtkTreeRowReference *row_reference)
948 ModestMsgViewWindow *window = NULL;
949 ModestMsgViewWindowPrivate *priv = NULL;
950 TnyFolder *header_folder = NULL;
951 ModestWindowMgr *mgr = NULL;
955 mgr = modest_runtime_get_window_mgr ();
956 window = MODEST_MSG_VIEW_WINDOW (modest_window_mgr_get_msg_view_window (mgr));
957 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), NULL);
959 modest_msg_view_window_construct (window, modest_account_name, mailbox, msg_uid);
961 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
963 /* Remember the message list's TreeModel so we can detect changes
964 * and change the list selection when necessary: */
966 if (header_view != NULL){
967 header_folder = modest_header_view_get_folder(header_view);
968 /* This could happen if the header folder was
969 unseleted before opening this msg window (for
970 example if the user selects an account in the
971 folder view of the main window */
973 priv->is_outbox = (modest_tny_folder_guess_folder_type (header_folder) == TNY_FOLDER_TYPE_OUTBOX);
974 priv->header_folder_id = tny_folder_get_id(header_folder);
975 g_assert(priv->header_folder_id != NULL);
976 g_object_unref(header_folder);
980 /* Setup row references and connect signals */
981 priv->header_model = gtk_tree_view_get_model (GTK_TREE_VIEW (header_view));
982 g_object_ref (priv->header_model);
985 priv->row_reference = gtk_tree_row_reference_copy (row_reference);
986 priv->next_row_reference = gtk_tree_row_reference_copy (row_reference);
987 select_next_valid_row (priv->header_model, &(priv->next_row_reference), TRUE, priv->is_outbox);
989 priv->row_reference = NULL;
990 priv->next_row_reference = NULL;
993 /* Connect signals */
994 priv->row_changed_handler =
995 g_signal_connect (GTK_TREE_MODEL(priv->header_model), "row-changed",
996 G_CALLBACK(modest_msg_view_window_on_row_changed),
998 priv->row_deleted_handler =
999 g_signal_connect (GTK_TREE_MODEL(priv->header_model), "row-deleted",
1000 G_CALLBACK(modest_msg_view_window_on_row_deleted),
1002 priv->row_inserted_handler =
1003 g_signal_connect (GTK_TREE_MODEL(priv->header_model), "row-inserted",
1004 G_CALLBACK(modest_msg_view_window_on_row_inserted),
1006 priv->rows_reordered_handler =
1007 g_signal_connect(GTK_TREE_MODEL(priv->header_model), "rows-reordered",
1008 G_CALLBACK(modest_msg_view_window_on_row_reordered),
1011 if (header_view != NULL){
1012 modest_header_view_add_observer(header_view,
1013 MODEST_HEADER_VIEW_OBSERVER(window));
1016 tny_msg_view_set_msg (TNY_MSG_VIEW (priv->msg_view), NULL);
1018 path = gtk_tree_row_reference_get_path (row_reference);
1019 if (gtk_tree_model_get_iter (priv->header_model, &iter, path)) {
1021 gtk_tree_model_get (priv->header_model, &iter,
1022 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
1024 message_reader (window, priv, header, row_reference);
1026 gtk_tree_path_free (path);
1028 /* Check dimming rules */
1029 modest_ui_actions_check_toolbar_dimming_rules (MODEST_WINDOW (window));
1030 modest_ui_actions_check_menu_dimming_rules (MODEST_WINDOW (window));
1031 modest_window_check_dimming_rules_group (MODEST_WINDOW (window), MODEST_DIMMING_RULES_CLIPBOARD);
1033 return MODEST_WINDOW(window);
1037 modest_msg_view_window_new_for_search_result (TnyMsg *msg,
1038 const gchar *modest_account_name,
1039 const gchar *mailbox,
1040 const gchar *msg_uid)
1042 ModestMsgViewWindow *window = NULL;
1043 ModestMsgViewWindowPrivate *priv = NULL;
1044 ModestWindowMgr *mgr = NULL;
1046 mgr = modest_runtime_get_window_mgr ();
1047 window = MODEST_MSG_VIEW_WINDOW (modest_window_mgr_get_msg_view_window (mgr));
1048 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), NULL);
1049 modest_msg_view_window_construct (window, modest_account_name, mailbox, msg_uid);
1051 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1053 /* Remember that this is a search result,
1054 * so we can disable some UI appropriately: */
1055 priv->is_search_result = TRUE;
1057 tny_msg_view_set_msg (TNY_MSG_VIEW (priv->msg_view), msg);
1059 update_window_title (window);
1060 /* gtk_widget_show_all (GTK_WIDGET (window));*/
1061 modest_msg_view_window_update_priority (window);
1063 /* Check dimming rules */
1064 modest_ui_actions_check_toolbar_dimming_rules (MODEST_WINDOW (window));
1065 modest_ui_actions_check_menu_dimming_rules (MODEST_WINDOW (window));
1066 modest_window_check_dimming_rules_group (MODEST_WINDOW (window), MODEST_DIMMING_RULES_CLIPBOARD);
1068 return MODEST_WINDOW(window);
1072 modest_msg_view_window_new_for_attachment (TnyMsg *msg,
1073 const gchar *modest_account_name,
1074 const gchar *mailbox,
1075 const gchar *msg_uid)
1077 GObject *obj = NULL;
1078 ModestMsgViewWindowPrivate *priv;
1079 ModestWindowMgr *mgr = NULL;
1081 g_return_val_if_fail (msg, NULL);
1082 mgr = modest_runtime_get_window_mgr ();
1083 obj = G_OBJECT (modest_window_mgr_get_msg_view_window (mgr));
1084 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (obj);
1085 modest_msg_view_window_construct (MODEST_MSG_VIEW_WINDOW (obj),
1086 modest_account_name, mailbox, msg_uid);
1088 tny_msg_view_set_msg (TNY_MSG_VIEW (priv->msg_view), msg);
1089 update_window_title (MODEST_MSG_VIEW_WINDOW (obj));
1091 /* gtk_widget_show_all (GTK_WIDGET (obj)); */
1093 /* Check dimming rules */
1094 modest_ui_actions_check_toolbar_dimming_rules (MODEST_WINDOW (obj));
1095 modest_ui_actions_check_menu_dimming_rules (MODEST_WINDOW (obj));
1096 modest_window_check_dimming_rules_group (MODEST_WINDOW (obj), MODEST_DIMMING_RULES_CLIPBOARD);
1098 return MODEST_WINDOW(obj);
1102 modest_msg_view_window_on_row_changed (GtkTreeModel *header_model,
1105 ModestMsgViewWindow *window)
1107 check_dimming_rules_after_change (window);
1111 modest_msg_view_window_on_row_deleted(GtkTreeModel *header_model,
1113 ModestMsgViewWindow *window)
1115 check_dimming_rules_after_change (window);
1117 /* The window could have dissapeared */
1120 check_dimming_rules_after_change (ModestMsgViewWindow *window)
1122 modest_ui_actions_check_menu_dimming_rules (MODEST_WINDOW (window));
1123 modest_ui_actions_check_toolbar_dimming_rules (MODEST_WINDOW (window));
1127 /* On insertions we check if the folder still has the message we are
1128 * showing or do not. If do not, we do nothing. Which means we are still
1129 * not attached to any header folder and thus next/prev buttons are
1130 * still dimmed. Once the message that is shown by msg-view is found, the
1131 * new model of header-view will be attached and the references will be set.
1132 * On each further insertions dimming rules will be checked. However
1133 * this requires extra CPU time at least works.
1134 * (An message might be deleted from TnyFolder and thus will not be
1135 * inserted into the model again for example if it is removed by the
1136 * imap server and the header view is refreshed.)
1139 modest_msg_view_window_on_row_inserted (GtkTreeModel *model,
1140 GtkTreePath *tree_path,
1141 GtkTreeIter *tree_iter,
1142 ModestMsgViewWindow *window)
1144 ModestMsgViewWindowPrivate *priv = NULL;
1145 TnyHeader *header = NULL;
1147 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window));
1148 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1150 g_assert (model == priv->header_model);
1152 /* Check if the newly inserted message is the same we are actually
1153 * showing. IF not, we should remain detached from the header model
1154 * and thus prev and next toolbar buttons should remain dimmed. */
1155 gtk_tree_model_get (model, tree_iter,
1156 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
1159 if (TNY_IS_HEADER (header)) {
1162 uid = modest_tny_folder_get_header_unique_id (header);
1163 if (!g_str_equal(priv->msg_uid, uid)) {
1164 check_dimming_rules_after_change (window);
1166 g_object_unref (G_OBJECT(header));
1170 g_object_unref(G_OBJECT(header));
1173 if (priv->row_reference) {
1174 gtk_tree_row_reference_free (priv->row_reference);
1177 /* Setup row_reference for the actual msg. */
1178 priv->row_reference = gtk_tree_row_reference_new (priv->header_model, tree_path);
1179 if (priv->row_reference == NULL) {
1180 g_warning("No reference for msg header item.");
1184 /* Now set up next_row_reference. */
1185 if (priv->next_row_reference) {
1186 gtk_tree_row_reference_free (priv->next_row_reference);
1189 priv->next_row_reference = gtk_tree_row_reference_copy (priv->row_reference);
1190 select_next_valid_row (priv->header_model,
1191 &(priv->next_row_reference), FALSE, priv->is_outbox);
1193 /* Connect the remaining callbacks to become able to detect
1194 * changes in header-view. */
1195 priv->row_changed_handler =
1196 g_signal_connect (priv->header_model, "row-changed",
1197 G_CALLBACK (modest_msg_view_window_on_row_changed),
1199 priv->row_deleted_handler =
1200 g_signal_connect (priv->header_model, "row-deleted",
1201 G_CALLBACK (modest_msg_view_window_on_row_deleted),
1203 priv->rows_reordered_handler =
1204 g_signal_connect (priv->header_model, "rows-reordered",
1205 G_CALLBACK (modest_msg_view_window_on_row_reordered),
1208 check_dimming_rules_after_change (window);
1212 modest_msg_view_window_on_row_reordered (GtkTreeModel *header_model,
1216 ModestMsgViewWindow *window)
1218 ModestMsgViewWindowPrivate *priv = NULL;
1219 gboolean already_changed = FALSE;
1221 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(window);
1223 /* If the current row was reordered select the proper next
1224 valid row. The same if the next row reference changes */
1225 if (priv->row_reference &&
1226 gtk_tree_row_reference_valid (priv->row_reference)) {
1228 path = gtk_tree_row_reference_get_path (priv->row_reference);
1229 if (gtk_tree_path_compare (path, arg1) == 0) {
1230 if (priv->next_row_reference) {
1231 gtk_tree_row_reference_free (priv->next_row_reference);
1233 priv->next_row_reference = gtk_tree_row_reference_copy (priv->row_reference);
1234 select_next_valid_row (header_model, &(priv->next_row_reference), FALSE, priv->is_outbox);
1235 already_changed = TRUE;
1237 gtk_tree_path_free (path);
1239 if (!already_changed &&
1240 priv->next_row_reference &&
1241 gtk_tree_row_reference_valid (priv->next_row_reference)) {
1243 path = gtk_tree_row_reference_get_path (priv->next_row_reference);
1244 if (gtk_tree_path_compare (path, arg1) == 0) {
1245 if (priv->next_row_reference) {
1246 gtk_tree_row_reference_free (priv->next_row_reference);
1248 priv->next_row_reference = gtk_tree_row_reference_copy (priv->row_reference);
1249 select_next_valid_row (header_model, &(priv->next_row_reference), FALSE, priv->is_outbox);
1251 gtk_tree_path_free (path);
1253 check_dimming_rules_after_change (window);
1256 /* The modest_msg_view_window_update_model_replaced implements update
1257 * function for ModestHeaderViewObserver. Checks whether the TnyFolder
1258 * actually belongs to the header-view is the same as the TnyFolder of
1259 * the message of msg-view or not. If they are different, there is
1260 * nothing to do. If they are the same, then the model has replaced and
1261 * the reference in msg-view shall be replaced from the old model to
1262 * the new model. In this case the view will be detached from it's
1263 * header folder. From this point the next/prev buttons are dimmed.
1266 modest_msg_view_window_update_model_replaced (ModestHeaderViewObserver *observer,
1267 GtkTreeModel *model,
1268 const gchar *tny_folder_id)
1270 ModestMsgViewWindowPrivate *priv = NULL;
1271 ModestMsgViewWindow *window = NULL;
1273 g_assert(MODEST_IS_HEADER_VIEW_OBSERVER(observer));
1274 g_assert(MODEST_IS_MSG_VIEW_WINDOW(observer));
1276 window = MODEST_MSG_VIEW_WINDOW(observer);
1277 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(window);
1279 /* If there is an other folder in the header-view then we do
1280 * not care about it's model (msg list). Else if the
1281 * header-view shows the folder the msg shown by us is in, we
1282 * shall replace our model reference and make some check. */
1283 if(model == NULL || tny_folder_id == NULL ||
1284 (priv->header_folder_id && !g_str_equal(tny_folder_id, priv->header_folder_id)))
1287 /* Model is changed(replaced), so we should forget the old
1288 * one. Because there might be other references and there
1289 * might be some change on the model even if we unreferenced
1290 * it, we need to disconnect our signals here. */
1291 if (priv->header_model) {
1292 if (g_signal_handler_is_connected(G_OBJECT (priv->header_model),
1293 priv->row_changed_handler))
1294 g_signal_handler_disconnect(G_OBJECT (priv->header_model),
1295 priv->row_changed_handler);
1296 if (g_signal_handler_is_connected(G_OBJECT (priv->header_model),
1297 priv->row_deleted_handler))
1298 g_signal_handler_disconnect(G_OBJECT (priv->header_model),
1299 priv->row_deleted_handler);
1300 if (g_signal_handler_is_connected(G_OBJECT (priv->header_model),
1301 priv->row_inserted_handler))
1302 g_signal_handler_disconnect(G_OBJECT (priv->header_model),
1303 priv->row_inserted_handler);
1304 if (g_signal_handler_is_connected(G_OBJECT (priv->header_model),
1305 priv->rows_reordered_handler))
1306 g_signal_handler_disconnect(G_OBJECT (priv->header_model),
1307 priv->rows_reordered_handler);
1310 if (priv->row_reference)
1311 gtk_tree_row_reference_free (priv->row_reference);
1312 if (priv->next_row_reference)
1313 gtk_tree_row_reference_free (priv->next_row_reference);
1314 g_object_unref(priv->header_model);
1317 priv->row_changed_handler = 0;
1318 priv->row_deleted_handler = 0;
1319 priv->row_inserted_handler = 0;
1320 priv->rows_reordered_handler = 0;
1321 priv->next_row_reference = NULL;
1322 priv->row_reference = NULL;
1323 priv->header_model = NULL;
1326 priv->header_model = g_object_ref (model);
1328 /* Also we must connect to the new model for row insertions.
1329 * Only for insertions now. We will need other ones only after
1330 * the msg is show by msg-view is added to the new model. */
1331 priv->row_inserted_handler =
1332 g_signal_connect (priv->header_model, "row-inserted",
1333 G_CALLBACK(modest_msg_view_window_on_row_inserted),
1336 modest_ui_actions_check_menu_dimming_rules(MODEST_WINDOW(window));
1337 modest_ui_actions_check_toolbar_dimming_rules(MODEST_WINDOW(window));
1341 modest_msg_view_window_toolbar_on_transfer_mode (ModestMsgViewWindow *self)
1343 ModestMsgViewWindowPrivate *priv= NULL;
1345 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self), FALSE);
1346 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
1348 return priv->progress_hint;
1352 modest_msg_view_window_get_header (ModestMsgViewWindow *self)
1354 ModestMsgViewWindowPrivate *priv= NULL;
1356 TnyHeader *header = NULL;
1357 GtkTreePath *path = NULL;
1360 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self), NULL);
1361 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
1363 /* If the message was not obtained from a treemodel,
1364 * for instance if it was opened directly by the search UI:
1366 if (priv->header_model == NULL ||
1367 priv->row_reference == NULL ||
1368 !gtk_tree_row_reference_valid (priv->row_reference)) {
1369 msg = modest_msg_view_window_get_message (self);
1371 header = tny_msg_get_header (msg);
1372 g_object_unref (msg);
1377 /* Get iter of the currently selected message in the header view: */
1378 path = gtk_tree_row_reference_get_path (priv->row_reference);
1379 g_return_val_if_fail (path != NULL, NULL);
1380 gtk_tree_model_get_iter (priv->header_model,
1384 /* Get current message header */
1385 gtk_tree_model_get (priv->header_model, &iter,
1386 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
1389 gtk_tree_path_free (path);
1394 modest_msg_view_window_get_message (ModestMsgViewWindow *self)
1396 ModestMsgViewWindowPrivate *priv;
1398 g_return_val_if_fail (self, NULL);
1400 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(self);
1402 return tny_msg_view_get_msg (TNY_MSG_VIEW (priv->msg_view));
1406 modest_msg_view_window_get_message_uid (ModestMsgViewWindow *self)
1408 ModestMsgViewWindowPrivate *priv;
1410 g_return_val_if_fail (self, NULL);
1412 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
1414 return (const gchar*) priv->msg_uid;
1418 modest_msg_view_window_toggle_find_toolbar (GtkToggleAction *toggle,
1421 ModestMsgViewWindow *window = MODEST_MSG_VIEW_WINDOW (data);
1422 ModestMsgViewWindowPrivate *priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1423 ModestWindowPrivate *parent_priv = MODEST_WINDOW_GET_PRIVATE (window);
1427 is_active = gtk_toggle_action_get_active (toggle);
1430 gtk_widget_show (priv->find_toolbar);
1431 hildon_find_toolbar_highlight_entry (HILDON_FIND_TOOLBAR (priv->find_toolbar), TRUE);
1433 gtk_widget_hide (priv->find_toolbar);
1434 modest_msg_view_grab_focus (MODEST_MSG_VIEW (priv->msg_view));
1437 /* update the toggle buttons status */
1438 action = gtk_ui_manager_get_action (parent_priv->ui_manager, "/ToolBar/FindInMessage");
1440 modest_utils_toggle_action_set_active_block_notify (GTK_TOGGLE_ACTION (action), is_active);
1445 modest_msg_view_window_find_toolbar_close (GtkWidget *widget,
1446 ModestMsgViewWindow *obj)
1448 GtkToggleAction *toggle;
1449 ModestWindowPrivate *parent_priv;
1450 ModestMsgViewWindowPrivate *priv;
1452 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (obj);
1453 parent_priv = MODEST_WINDOW_GET_PRIVATE (obj);
1455 toggle = GTK_TOGGLE_ACTION (gtk_ui_manager_get_action (parent_priv->ui_manager, "/ToolBar/FindInMessage"));
1456 gtk_toggle_action_set_active (toggle, FALSE);
1457 modest_msg_view_grab_focus (MODEST_MSG_VIEW (priv->msg_view));
1461 modest_msg_view_window_find_toolbar_search (GtkWidget *widget,
1462 ModestMsgViewWindow *obj)
1464 gchar *current_search;
1465 ModestMsgViewWindowPrivate *priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (obj);
1467 if (modest_mime_part_view_is_empty (MODEST_MIME_PART_VIEW (priv->msg_view))) {
1468 hildon_banner_show_information (NULL, NULL, _("mail_ib_nothing_to_find"));
1472 g_object_get (G_OBJECT (widget), "prefix", ¤t_search, NULL);
1474 if ((current_search == NULL) || (strcmp (current_search, "") == 0)) {
1475 g_free (current_search);
1476 hildon_banner_show_information (NULL, NULL, _CS("ecdg_ib_find_rep_enter_text"));
1480 if ((priv->last_search == NULL) || (strcmp (priv->last_search, current_search) != 0)) {
1482 g_free (priv->last_search);
1483 priv->last_search = g_strdup (current_search);
1484 result = modest_isearch_view_search (MODEST_ISEARCH_VIEW (priv->msg_view),
1487 hildon_banner_show_information (NULL, NULL,
1488 _HL("ckct_ib_find_no_matches"));
1489 g_free (priv->last_search);
1490 priv->last_search = NULL;
1492 hildon_find_toolbar_highlight_entry (HILDON_FIND_TOOLBAR (priv->find_toolbar), TRUE);
1495 if (!modest_isearch_view_search_next (MODEST_ISEARCH_VIEW (priv->msg_view))) {
1496 hildon_banner_show_information (NULL, NULL,
1497 _HL("ckct_ib_find_search_complete"));
1498 g_free (priv->last_search);
1499 priv->last_search = NULL;
1501 hildon_find_toolbar_highlight_entry (HILDON_FIND_TOOLBAR (priv->find_toolbar), TRUE);
1505 g_free (current_search);
1510 modest_msg_view_window_set_zoom (ModestWindow *window,
1513 ModestMsgViewWindowPrivate *priv;
1514 ModestWindowPrivate *parent_priv;
1516 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window));
1518 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1519 parent_priv = MODEST_WINDOW_GET_PRIVATE (window);
1520 modest_zoomable_set_zoom (MODEST_ZOOMABLE (priv->msg_view), zoom);
1525 modest_msg_view_window_get_zoom (ModestWindow *window)
1527 ModestMsgViewWindowPrivate *priv;
1529 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), 1.0);
1531 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1532 return modest_zoomable_get_zoom (MODEST_ZOOMABLE (priv->msg_view));
1536 modest_msg_view_window_zoom_plus (ModestWindow *window)
1539 ModestMsgViewWindowPrivate *priv;
1543 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), 1.0);
1544 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1546 zoom_level = modest_zoomable_get_zoom (MODEST_ZOOMABLE (priv->msg_view));
1548 if (zoom_level >= 2.0) {
1549 hildon_banner_show_information (NULL, NULL,
1550 _CS("ckct_ib_max_zoom_level_reached"));
1552 } else if (zoom_level >= 1.5) {
1554 } else if (zoom_level >= 1.2) {
1556 } else if (zoom_level >= 1.0) {
1558 } else if (zoom_level >= 0.8) {
1560 } else if (zoom_level >= 0.5) {
1566 /* set zoom level */
1567 int_zoom = (gint) rint (zoom_level*100.0+0.1);
1568 banner_text = g_strdup_printf (_HL("wdgt_ib_zoom"), int_zoom);
1569 modest_platform_information_banner (GTK_WIDGET (window), NULL, banner_text);
1570 g_free (banner_text);
1571 modest_zoomable_set_zoom (MODEST_ZOOMABLE (priv->msg_view), zoom_level);
1577 modest_msg_view_window_zoom_minus (ModestWindow *window)
1580 ModestMsgViewWindowPrivate *priv;
1584 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), 1.0);
1585 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1587 zoom_level = modest_zoomable_get_zoom (MODEST_ZOOMABLE (priv->msg_view));
1589 if (zoom_level <= 0.5) {
1590 hildon_banner_show_information (NULL, NULL,
1591 _CS("ckct_ib_min_zoom_level_reached"));
1593 } else if (zoom_level <= 0.8) {
1595 } else if (zoom_level <= 1.0) {
1597 } else if (zoom_level <= 1.2) {
1599 } else if (zoom_level <= 1.5) {
1601 } else if (zoom_level <= 2.0) {
1607 /* set zoom level */
1608 int_zoom = (gint) rint (zoom_level*100.0+0.1);
1609 banner_text = g_strdup_printf (_HL("wdgt_ib_zoom"), int_zoom);
1610 modest_platform_information_banner (GTK_WIDGET (window), NULL, banner_text);
1611 g_free (banner_text);
1612 modest_zoomable_set_zoom (MODEST_ZOOMABLE (priv->msg_view), zoom_level);
1619 modest_msg_view_window_key_event (GtkWidget *window,
1625 focus = gtk_window_get_focus (GTK_WINDOW (window));
1627 /* for the find toolbar case */
1628 if (focus && GTK_IS_ENTRY (focus)) {
1629 if (event->keyval == GDK_BackSpace) {
1631 copy = gdk_event_copy ((GdkEvent *) event);
1632 gtk_widget_event (focus, copy);
1633 gdk_event_free (copy);
1638 if (event->keyval == GDK_Up || event->keyval == GDK_KP_Up ||
1639 event->keyval == GDK_Down || event->keyval == GDK_KP_Down ||
1640 event->keyval == GDK_Page_Up || event->keyval == GDK_KP_Page_Up ||
1641 event->keyval == GDK_Page_Down || event->keyval == GDK_KP_Page_Down ||
1642 event->keyval == GDK_Home || event->keyval == GDK_KP_Home ||
1643 event->keyval == GDK_End || event->keyval == GDK_KP_End) {
1644 /* ModestMsgViewWindowPrivate *priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window); */
1645 /* gboolean return_value; */
1647 if (event->type == GDK_KEY_PRESS) {
1648 GtkScrollType scroll_type;
1650 switch (event->keyval) {
1653 scroll_type = GTK_SCROLL_STEP_UP; break;
1656 scroll_type = GTK_SCROLL_STEP_DOWN; break;
1658 case GDK_KP_Page_Up:
1659 scroll_type = GTK_SCROLL_PAGE_UP; break;
1661 case GDK_KP_Page_Down:
1662 scroll_type = GTK_SCROLL_PAGE_DOWN; break;
1665 scroll_type = GTK_SCROLL_START; break;
1668 scroll_type = GTK_SCROLL_END; break;
1669 default: scroll_type = GTK_SCROLL_NONE;
1672 /* g_signal_emit_by_name (G_OBJECT (priv->main_scroll), "scroll-child", */
1673 /* scroll_type, FALSE, &return_value); */
1684 modest_msg_view_window_last_message_selected (ModestMsgViewWindow *window)
1687 ModestMsgViewWindowPrivate *priv;
1688 GtkTreeIter tmp_iter;
1689 gboolean is_last_selected;
1691 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), TRUE);
1692 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1694 /*if no model (so no rows at all), then virtually we are the last*/
1695 if (!priv->header_model || !priv->row_reference)
1698 if (!gtk_tree_row_reference_valid (priv->row_reference))
1701 path = gtk_tree_row_reference_get_path (priv->row_reference);
1705 is_last_selected = TRUE;
1706 while (is_last_selected) {
1708 gtk_tree_path_next (path);
1709 if (!gtk_tree_model_get_iter (priv->header_model, &tmp_iter, path))
1711 gtk_tree_model_get (priv->header_model, &tmp_iter,
1712 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
1715 if (msg_is_visible (header, priv->is_outbox))
1716 is_last_selected = FALSE;
1717 g_object_unref(G_OBJECT(header));
1720 gtk_tree_path_free (path);
1721 return is_last_selected;
1725 modest_msg_view_window_has_headers_model (ModestMsgViewWindow *window)
1727 ModestMsgViewWindowPrivate *priv;
1729 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), TRUE);
1730 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1732 return priv->header_model != NULL;
1736 modest_msg_view_window_is_search_result (ModestMsgViewWindow *window)
1738 ModestMsgViewWindowPrivate *priv;
1740 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), TRUE);
1741 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1743 return priv->is_search_result;
1747 msg_is_visible (TnyHeader *header, gboolean check_outbox)
1749 if ((tny_header_get_flags(header) & TNY_HEADER_FLAG_DELETED))
1751 if (!check_outbox) {
1754 ModestTnySendQueueStatus status;
1755 status = modest_tny_all_send_queues_get_msg_status (header);
1756 return ((status != MODEST_TNY_SEND_QUEUE_FAILED) &&
1757 (status != MODEST_TNY_SEND_QUEUE_SENDING));
1762 modest_msg_view_window_first_message_selected (ModestMsgViewWindow *window)
1765 ModestMsgViewWindowPrivate *priv;
1766 gboolean is_first_selected;
1767 GtkTreeIter tmp_iter;
1769 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), TRUE);
1770 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1772 /*if no model (so no rows at all), then virtually we are the first*/
1773 if (!priv->header_model || !priv->row_reference)
1776 if (!gtk_tree_row_reference_valid (priv->row_reference))
1779 path = gtk_tree_row_reference_get_path (priv->row_reference);
1783 is_first_selected = TRUE;
1784 while (is_first_selected) {
1786 if(!gtk_tree_path_prev (path))
1788 /* Here the 'if' is needless for logic, but let make sure
1789 * iter is valid for gtk_tree_model_get. */
1790 if (!gtk_tree_model_get_iter (priv->header_model, &tmp_iter, path))
1792 gtk_tree_model_get (priv->header_model, &tmp_iter,
1793 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
1796 if (msg_is_visible (header, priv->is_outbox))
1797 is_first_selected = FALSE;
1798 g_object_unref(G_OBJECT(header));
1801 gtk_tree_path_free (path);
1802 return is_first_selected;
1807 GtkTreeRowReference *row_reference;
1811 message_reader_performer (gboolean canceled,
1813 GtkWindow *parent_window,
1814 TnyAccount *account,
1817 ModestMailOperation *mail_op = NULL;
1818 MsgReaderInfo *info;
1820 info = (MsgReaderInfo *) user_data;
1821 if (canceled || err) {
1822 update_window_title (MODEST_MSG_VIEW_WINDOW (parent_window));
1826 /* Register the header - it'll be unregistered in the callback */
1827 modest_window_mgr_register_header (modest_runtime_get_window_mgr (), info->header, NULL);
1829 /* New mail operation */
1830 mail_op = modest_mail_operation_new_with_error_handling (G_OBJECT(parent_window),
1831 modest_ui_actions_disk_operations_error_handler,
1834 modest_mail_operation_queue_add (modest_runtime_get_mail_operation_queue (), mail_op);
1835 modest_mail_operation_get_msg (mail_op, info->header, TRUE, view_msg_cb, info->row_reference);
1836 g_object_unref (mail_op);
1838 /* Update dimming rules */
1839 modest_ui_actions_check_toolbar_dimming_rules (MODEST_WINDOW (parent_window));
1840 modest_ui_actions_check_menu_dimming_rules (MODEST_WINDOW (parent_window));
1843 /* Frees. The row_reference will be freed by the view_msg_cb callback */
1844 g_object_unref (info->header);
1845 g_slice_free (MsgReaderInfo, info);
1850 * Reads the message whose summary item is @header. It takes care of
1851 * several things, among others:
1853 * If the message was not previously downloaded then ask the user
1854 * before downloading. If there is no connection launch the connection
1855 * dialog. Update toolbar dimming rules.
1857 * Returns: TRUE if the mail operation was started, otherwise if the
1858 * user do not want to download the message, or if the user do not
1859 * want to connect, then the operation is not issued
1862 message_reader (ModestMsgViewWindow *window,
1863 ModestMsgViewWindowPrivate *priv,
1865 GtkTreeRowReference *row_reference)
1867 ModestWindowMgr *mgr;
1868 TnyAccount *account;
1870 MsgReaderInfo *info;
1872 g_return_val_if_fail (row_reference != NULL, FALSE);
1874 mgr = modest_runtime_get_window_mgr ();
1875 /* Msg download completed */
1876 if (!(tny_header_get_flags (header) & TNY_HEADER_FLAG_CACHED)) {
1878 /* We set the header from model while we're loading */
1879 tny_header_view_set_header (TNY_HEADER_VIEW (priv->msg_view), header);
1880 gtk_window_set_title (GTK_WINDOW (window), _CS("ckdg_pb_updating"));
1882 /* Ask the user if he wants to download the message if
1884 if (!tny_device_is_online (modest_runtime_get_device())) {
1885 GtkResponseType response;
1887 response = modest_platform_run_confirmation_dialog (GTK_WINDOW (window),
1888 _("mcen_nc_get_msg"));
1889 if (response == GTK_RESPONSE_CANCEL) {
1890 update_window_title (window);
1894 folder = tny_header_get_folder (header);
1895 info = g_slice_new (MsgReaderInfo);
1896 info->header = g_object_ref (header);
1897 info->row_reference = gtk_tree_row_reference_copy (row_reference);
1899 /* Offer the connection dialog if necessary */
1900 modest_platform_connect_if_remote_and_perform ((GtkWindow *) window,
1902 TNY_FOLDER_STORE (folder),
1903 message_reader_performer,
1905 g_object_unref (folder);
1910 folder = tny_header_get_folder (header);
1911 account = tny_folder_get_account (folder);
1912 info = g_slice_new (MsgReaderInfo);
1913 info->header = g_object_ref (header);
1914 info->row_reference = gtk_tree_row_reference_copy (row_reference);
1916 message_reader_performer (FALSE, NULL, (GtkWindow *) window, account, info);
1917 g_object_unref (account);
1918 g_object_unref (folder);
1924 modest_msg_view_window_select_next_message (ModestMsgViewWindow *window)
1926 ModestMsgViewWindowPrivate *priv;
1927 GtkTreePath *path= NULL;
1928 GtkTreeIter tmp_iter;
1930 gboolean retval = TRUE;
1931 GtkTreeRowReference *row_reference = NULL;
1933 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), FALSE);
1934 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1936 if (!priv->row_reference)
1939 /* Update the next row reference if it's not valid. This could
1940 happen if for example the header which it was pointing to,
1941 was deleted. The best place to do it is in the row-deleted
1942 handler but the tinymail model do not work like the glib
1943 tree models and reports the deletion when the row is still
1945 if (!gtk_tree_row_reference_valid (priv->next_row_reference)) {
1946 if (gtk_tree_row_reference_valid (priv->row_reference)) {
1947 priv->next_row_reference = gtk_tree_row_reference_copy (priv->row_reference);
1948 select_next_valid_row (priv->header_model, &(priv->next_row_reference), FALSE, priv->is_outbox);
1951 if (priv->next_row_reference)
1952 path = gtk_tree_row_reference_get_path (priv->next_row_reference);
1956 row_reference = gtk_tree_row_reference_copy (priv->next_row_reference);
1958 gtk_tree_model_get_iter (priv->header_model,
1961 gtk_tree_path_free (path);
1963 gtk_tree_model_get (priv->header_model, &tmp_iter,
1964 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
1967 /* Read the message & show it */
1968 if (!message_reader (window, priv, header, row_reference)) {
1971 gtk_tree_row_reference_free (row_reference);
1974 g_object_unref (header);
1980 modest_msg_view_window_select_previous_message (ModestMsgViewWindow *window)
1982 ModestMsgViewWindowPrivate *priv = NULL;
1984 gboolean finished = FALSE;
1985 gboolean retval = FALSE;
1987 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), FALSE);
1988 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1990 /* Return inmediatly if there is no header model */
1991 if (!priv->header_model || !priv->row_reference)
1994 path = gtk_tree_row_reference_get_path (priv->row_reference);
1995 while (!finished && gtk_tree_path_prev (path)) {
1999 gtk_tree_model_get_iter (priv->header_model, &iter, path);
2000 gtk_tree_model_get (priv->header_model, &iter,
2001 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
2005 if (msg_is_visible (header, priv->is_outbox)) {
2006 GtkTreeRowReference *row_reference;
2007 row_reference = gtk_tree_row_reference_new (priv->header_model, path);
2008 /* Read the message & show it */
2009 retval = message_reader (window, priv, header, row_reference);
2010 gtk_tree_row_reference_free (row_reference);
2014 g_object_unref (header);
2018 gtk_tree_path_free (path);
2023 view_msg_cb (ModestMailOperation *mail_op,
2030 ModestMsgViewWindow *self = NULL;
2031 ModestMsgViewWindowPrivate *priv = NULL;
2032 GtkTreeRowReference *row_reference = NULL;
2034 /* Unregister the header (it was registered before creating the mail operation) */
2035 modest_window_mgr_unregister_header (modest_runtime_get_window_mgr (), header);
2037 row_reference = (GtkTreeRowReference *) user_data;
2039 gtk_tree_row_reference_free (row_reference);
2040 self = (ModestMsgViewWindow *) modest_mail_operation_get_source (mail_op);
2042 /* Restore window title */
2043 update_window_title (self);
2044 g_object_unref (self);
2049 /* If there was any error */
2050 if (!modest_ui_actions_msg_retrieval_check (mail_op, header, msg)) {
2051 gtk_tree_row_reference_free (row_reference);
2052 self = (ModestMsgViewWindow *) modest_mail_operation_get_source (mail_op);
2054 /* Restore window title */
2055 update_window_title (self);
2056 g_object_unref (self);
2061 /* Get the window */
2062 self = (ModestMsgViewWindow *) modest_mail_operation_get_source (mail_op);
2063 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self));
2064 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
2066 /* Update the row reference */
2067 if (priv->row_reference != NULL) {
2068 gtk_tree_row_reference_free (priv->row_reference);
2069 priv->row_reference = gtk_tree_row_reference_copy (row_reference);
2070 if (priv->next_row_reference != NULL) {
2071 gtk_tree_row_reference_free (priv->next_row_reference);
2073 priv->next_row_reference = gtk_tree_row_reference_copy (priv->row_reference);
2074 select_next_valid_row (priv->header_model, &(priv->next_row_reference), TRUE, priv->is_outbox);
2077 /* Mark header as read */
2078 if (!(tny_header_get_flags (header) & TNY_HEADER_FLAG_SEEN))
2079 tny_header_set_flag (header, TNY_HEADER_FLAG_SEEN);
2081 /* Set new message */
2082 if (priv->msg_view != NULL && TNY_IS_MSG_VIEW (priv->msg_view)) {
2083 tny_msg_view_set_msg (TNY_MSG_VIEW (priv->msg_view), msg);
2084 modest_msg_view_window_update_priority (self);
2085 update_window_title (MODEST_MSG_VIEW_WINDOW (self));
2086 modest_msg_view_grab_focus (MODEST_MSG_VIEW (priv->msg_view));
2089 /* Set the new message uid of the window */
2090 if (priv->msg_uid) {
2091 g_free (priv->msg_uid);
2092 priv->msg_uid = modest_tny_folder_get_header_unique_id (header);
2095 /* Notify the observers */
2096 g_signal_emit (G_OBJECT (self), signals[MSG_CHANGED_SIGNAL],
2097 0, priv->header_model, priv->row_reference);
2100 g_object_unref (self);
2101 gtk_tree_row_reference_free (row_reference);
2105 modest_msg_view_window_get_folder_type (ModestMsgViewWindow *window)
2107 ModestMsgViewWindowPrivate *priv;
2109 TnyFolderType folder_type;
2111 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
2113 folder_type = TNY_FOLDER_TYPE_UNKNOWN;
2115 msg = tny_msg_view_get_msg (TNY_MSG_VIEW (priv->msg_view));
2119 folder = tny_msg_get_folder (msg);
2121 folder_type = modest_tny_folder_guess_folder_type (folder);
2122 g_object_unref (folder);
2124 g_object_unref (msg);
2132 modest_msg_view_window_update_priority (ModestMsgViewWindow *window)
2134 ModestMsgViewWindowPrivate *priv;
2135 TnyHeader *header = NULL;
2136 TnyHeaderFlags flags = 0;
2138 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
2140 if (priv->header_model && priv->row_reference) {
2142 GtkTreePath *path = NULL;
2144 path = gtk_tree_row_reference_get_path (priv->row_reference);
2145 g_return_if_fail (path != NULL);
2146 gtk_tree_model_get_iter (priv->header_model,
2148 gtk_tree_row_reference_get_path (priv->row_reference));
2150 gtk_tree_model_get (priv->header_model, &iter, TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
2152 gtk_tree_path_free (path);
2155 msg = tny_msg_view_get_msg (TNY_MSG_VIEW (priv->msg_view));
2157 header = tny_msg_get_header (msg);
2158 g_object_unref (msg);
2163 flags = tny_header_get_flags (header);
2164 g_object_unref(G_OBJECT(header));
2167 modest_msg_view_set_priority (MODEST_MSG_VIEW(priv->msg_view), flags);
2172 toolbar_resize (ModestMsgViewWindow *self)
2174 ModestMsgViewWindowPrivate *priv = NULL;
2175 ModestWindowPrivate *parent_priv = NULL;
2177 gint static_button_size;
2178 ModestWindowMgr *mgr;
2180 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self));
2181 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
2182 parent_priv = MODEST_WINDOW_GET_PRIVATE(self);
2184 mgr = modest_runtime_get_window_mgr ();
2185 static_button_size = modest_window_mgr_get_fullscreen_mode (mgr)?120:120;
2187 if (parent_priv->toolbar) {
2188 /* left size buttons */
2189 widget = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/ToolbarMessageReply");
2190 gtk_tool_item_set_expand (GTK_TOOL_ITEM (widget), FALSE);
2191 gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (widget), FALSE);
2192 gtk_widget_set_size_request (GTK_WIDGET (widget), static_button_size, -1);
2193 widget = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/ToolbarMessageMoveTo");
2194 gtk_tool_item_set_expand (GTK_TOOL_ITEM (widget), FALSE);
2195 gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (widget), FALSE);
2196 gtk_widget_set_size_request (GTK_WIDGET (widget), static_button_size, -1);
2197 widget = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/ToolbarDeleteMessage");
2198 gtk_tool_item_set_expand (GTK_TOOL_ITEM (widget), FALSE);
2199 gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (widget), FALSE);
2200 gtk_widget_set_size_request (GTK_WIDGET (widget), static_button_size, -1);
2201 widget = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/FindInMessage");
2202 gtk_tool_item_set_expand (GTK_TOOL_ITEM (widget), FALSE);
2203 gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (widget), FALSE);
2204 gtk_widget_set_size_request (GTK_WIDGET (widget), static_button_size, -1);
2206 gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (priv->next_toolitem), TRUE);
2207 gtk_tool_item_set_expand (GTK_TOOL_ITEM (priv->next_toolitem), TRUE);
2208 gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (priv->prev_toolitem), TRUE);
2209 gtk_tool_item_set_expand (GTK_TOOL_ITEM (priv->prev_toolitem), TRUE);
2214 modest_msg_view_window_show_toolbar (ModestWindow *self,
2215 gboolean show_toolbar)
2217 ModestMsgViewWindowPrivate *priv = NULL;
2218 ModestWindowPrivate *parent_priv;
2220 parent_priv = MODEST_WINDOW_GET_PRIVATE(self);
2221 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(self);
2223 /* Set optimized view status */
2224 priv->optimized_view = !show_toolbar;
2226 if (!parent_priv->toolbar) {
2227 parent_priv->toolbar = gtk_ui_manager_get_widget (parent_priv->ui_manager,
2229 gtk_toolbar_set_icon_size (GTK_TOOLBAR (parent_priv->toolbar), HILDON_ICON_SIZE_FINGER);
2230 gtk_widget_set_no_show_all (parent_priv->toolbar, TRUE);
2232 priv->next_toolitem = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/ToolbarMessageNext");
2233 priv->prev_toolitem = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/ToolbarMessageBack");
2234 toolbar_resize (MODEST_MSG_VIEW_WINDOW (self));
2237 hildon_window_add_toolbar (HILDON_WINDOW (self),
2238 GTK_TOOLBAR (parent_priv->toolbar));
2243 /* Quick hack: this prevents toolbar icons "dance" when progress bar show status is changed */
2244 /* TODO: resize mode migth be GTK_RESIZE_QUEUE, in order to avoid unneccesary shows */
2245 gtk_container_set_resize_mode (GTK_CONTAINER(parent_priv->toolbar), GTK_RESIZE_IMMEDIATE);
2247 gtk_widget_show (GTK_WIDGET (parent_priv->toolbar));
2248 if (modest_msg_view_window_transfer_mode_enabled (MODEST_MSG_VIEW_WINDOW (self)))
2249 set_progress_hint (MODEST_MSG_VIEW_WINDOW (self), TRUE);
2251 set_progress_hint (MODEST_MSG_VIEW_WINDOW (self), FALSE);
2254 gtk_widget_set_no_show_all (parent_priv->toolbar, TRUE);
2255 gtk_widget_hide (GTK_WIDGET (parent_priv->toolbar));
2260 modest_msg_view_window_clipboard_owner_change (GtkClipboard *clipboard,
2262 ModestMsgViewWindow *window)
2264 if (!GTK_WIDGET_VISIBLE (window))
2267 modest_window_check_dimming_rules_group (MODEST_WINDOW (window), MODEST_DIMMING_RULES_CLIPBOARD);
2271 modest_msg_view_window_transfer_mode_enabled (ModestMsgViewWindow *self)
2273 ModestMsgViewWindowPrivate *priv;
2275 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self), FALSE);
2276 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(self);
2278 return priv->progress_hint;
2282 observers_empty (ModestMsgViewWindow *self)
2285 ModestMsgViewWindowPrivate *priv;
2286 gboolean is_empty = TRUE;
2287 guint pending_ops = 0;
2289 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(self);
2290 tmp = priv->progress_widgets;
2292 /* Check all observers */
2293 while (tmp && is_empty) {
2294 pending_ops = modest_progress_object_num_pending_operations (MODEST_PROGRESS_OBJECT(tmp->data));
2295 is_empty = pending_ops == 0;
2297 tmp = g_slist_next(tmp);
2304 on_account_removed (TnyAccountStore *account_store,
2305 TnyAccount *account,
2308 /* Do nothing if it's a transport account, because we only
2309 show the messages of a store account */
2310 if (tny_account_get_account_type(account) == TNY_ACCOUNT_TYPE_STORE) {
2311 const gchar *parent_acc = NULL;
2312 const gchar *our_acc = NULL;
2314 our_acc = modest_window_get_active_account (MODEST_WINDOW (user_data));
2315 parent_acc = modest_tny_account_get_parent_modest_account_name_for_server_account (account);
2317 /* Close this window if I'm showing a message of the removed account */
2318 if (our_acc && parent_acc && strcmp (parent_acc, our_acc) == 0)
2319 modest_ui_actions_on_close_window (NULL, MODEST_WINDOW (user_data));
2324 on_mail_operation_started (ModestMailOperation *mail_op,
2327 ModestMsgViewWindow *self;
2328 ModestMailOperationTypeOperation op_type;
2330 ModestMsgViewWindowPrivate *priv;
2331 GObject *source = NULL;
2333 self = MODEST_MSG_VIEW_WINDOW (user_data);
2334 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
2335 op_type = modest_mail_operation_get_type_operation (mail_op);
2336 tmp = priv->progress_widgets;
2337 source = modest_mail_operation_get_source(mail_op);
2338 if (G_OBJECT (self) == source) {
2339 if (op_type == MODEST_MAIL_OPERATION_TYPE_RECEIVE) {
2340 set_toolbar_transfer_mode(self);
2342 modest_progress_object_add_operation (
2343 MODEST_PROGRESS_OBJECT (tmp->data),
2345 tmp = g_slist_next (tmp);
2349 g_object_unref (source);
2353 on_mail_operation_finished (ModestMailOperation *mail_op,
2356 ModestMsgViewWindow *self;
2357 ModestMailOperationTypeOperation op_type;
2359 ModestMsgViewWindowPrivate *priv;
2361 self = MODEST_MSG_VIEW_WINDOW (user_data);
2362 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
2363 op_type = modest_mail_operation_get_type_operation (mail_op);
2364 tmp = priv->progress_widgets;
2366 if (op_type == MODEST_MAIL_OPERATION_TYPE_RECEIVE) {
2368 modest_progress_object_remove_operation (MODEST_PROGRESS_OBJECT (tmp->data),
2370 tmp = g_slist_next (tmp);
2373 /* If no more operations are being observed, NORMAL mode is enabled again */
2374 if (observers_empty (self)) {
2375 set_progress_hint (self, FALSE);
2379 /* Update dimming rules. We have to do this right here
2380 and not in view_msg_cb because at that point the
2381 transfer mode is still enabled so the dimming rule
2382 won't let the user delete the message that has been
2383 readed for example */
2384 check_dimming_rules_after_change (self);
2389 on_queue_changed (ModestMailOperationQueue *queue,
2390 ModestMailOperation *mail_op,
2391 ModestMailOperationQueueNotification type,
2392 ModestMsgViewWindow *self)
2394 ModestMsgViewWindowPrivate *priv;
2396 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
2398 /* If this operations was created by another window, do nothing */
2399 if (!modest_mail_operation_is_mine (mail_op, G_OBJECT(self)))
2402 if (type == MODEST_MAIL_OPERATION_QUEUE_OPERATION_ADDED) {
2403 priv->sighandlers = modest_signal_mgr_connect (priv->sighandlers,
2405 "operation-started",
2406 G_CALLBACK (on_mail_operation_started),
2408 priv->sighandlers = modest_signal_mgr_connect (priv->sighandlers,
2410 "operation-finished",
2411 G_CALLBACK (on_mail_operation_finished),
2413 } else if (type == MODEST_MAIL_OPERATION_QUEUE_OPERATION_REMOVED) {
2414 priv->sighandlers = modest_signal_mgr_disconnect (priv->sighandlers,
2416 "operation-started");
2417 priv->sighandlers = modest_signal_mgr_disconnect (priv->sighandlers,
2419 "operation-finished");
2424 modest_msg_view_window_get_attachments (ModestMsgViewWindow *win)
2426 ModestMsgViewWindowPrivate *priv;
2427 TnyList *selected_attachments = NULL;
2429 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (win), NULL);
2430 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (win);
2432 /* In Hildon 2.2 as there's no selection we assume we have all attachments selected */
2433 selected_attachments = modest_msg_view_get_attachments (MODEST_MSG_VIEW (priv->msg_view));
2435 return selected_attachments;
2439 on_decode_to_stream_async_handler (TnyMimePart *mime_part,
2445 gchar *filepath = (gchar *) user_data;
2447 if (cancelled || err) {
2449 gchar *msg = g_strdup_printf (_KR("cerm_device_memory_full"), "");
2450 modest_platform_information_banner (NULL, NULL, msg);
2456 /* make the file read-only */
2457 g_chmod(filepath, 0444);
2459 /* Activate the file */
2460 modest_platform_activate_file (filepath, tny_mime_part_get_content_type (mime_part));
2468 modest_msg_view_window_view_attachment (ModestMsgViewWindow *window,
2469 TnyMimePart *mime_part)
2471 ModestMsgViewWindowPrivate *priv;
2472 const gchar *msg_uid;
2473 gchar *attachment_uid = NULL;
2474 gint attachment_index = 0;
2475 TnyList *attachments;
2477 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window));
2478 g_return_if_fail (TNY_IS_MIME_PART (mime_part) || (mime_part == NULL));
2479 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
2481 msg_uid = modest_msg_view_window_get_message_uid (MODEST_MSG_VIEW_WINDOW (window));
2482 attachments = modest_msg_view_get_attachments (MODEST_MSG_VIEW (priv->msg_view));
2483 attachment_index = modest_list_index (attachments, (GObject *) mime_part);
2484 g_object_unref (attachments);
2486 if (msg_uid && attachment_index >= 0) {
2487 attachment_uid = g_strdup_printf ("%s/%d", msg_uid, attachment_index);
2490 if (mime_part == NULL) {
2491 gboolean error = FALSE;
2492 TnyList *selected_attachments = modest_msg_view_get_selected_attachments (MODEST_MSG_VIEW (priv->msg_view));
2493 if (selected_attachments == NULL || tny_list_get_length (selected_attachments) == 0) {
2495 } else if (tny_list_get_length (selected_attachments) > 1) {
2496 hildon_banner_show_information (NULL, NULL, _("mcen_ib_unable_to_display_more"));
2500 iter = tny_list_create_iterator (selected_attachments);
2501 mime_part = (TnyMimePart *) tny_iterator_get_current (iter);
2502 g_object_unref (iter);
2504 if (selected_attachments)
2505 g_object_unref (selected_attachments);
2510 g_object_ref (mime_part);
2513 if (tny_mime_part_is_purged (mime_part))
2516 if (!modest_tny_mime_part_is_msg (mime_part)) {
2517 gchar *filepath = NULL;
2518 const gchar *att_filename = tny_mime_part_get_filename (mime_part);
2519 gboolean show_error_banner = FALSE;
2520 TnyFsStream *temp_stream = NULL;
2521 temp_stream = modest_utils_create_temp_stream (att_filename, attachment_uid,
2524 if (temp_stream != NULL) {
2525 tny_mime_part_decode_to_stream_async (mime_part, TNY_STREAM (temp_stream),
2526 on_decode_to_stream_async_handler,
2528 g_strdup (filepath));
2529 g_object_unref (temp_stream);
2530 /* NOTE: files in the temporary area will be automatically
2531 * cleaned after some time if they are no longer in use */
2534 const gchar *content_type;
2535 /* the file may already exist but it isn't writable,
2536 * let's try to open it anyway */
2537 content_type = tny_mime_part_get_content_type (mime_part);
2538 modest_platform_activate_file (filepath, content_type);
2540 g_warning ("%s: modest_utils_create_temp_stream failed", __FUNCTION__);
2541 show_error_banner = TRUE;
2546 if (show_error_banner)
2547 modest_platform_information_banner (NULL, NULL, _("mail_ib_file_operation_failed"));
2549 /* message attachment */
2550 TnyHeader *header = NULL;
2551 ModestWindowMgr *mgr;
2552 ModestWindow *msg_win = NULL;
2555 header = tny_msg_get_header (TNY_MSG (mime_part));
2556 mgr = modest_runtime_get_window_mgr ();
2557 found = modest_window_mgr_find_registered_header (mgr, header, &msg_win);
2560 /* if it's found, but there is no msg_win, it's probably in the process of being created;
2561 * thus, we don't do anything */
2562 g_warning ("window for is already being created");
2564 /* it's not found, so create a new window for it */
2565 modest_window_mgr_register_header (mgr, header, attachment_uid); /* register the uid before building the window */
2566 gchar *account = g_strdup (modest_window_get_active_account (MODEST_WINDOW (window)));
2567 const gchar *mailbox = modest_window_get_active_mailbox (MODEST_WINDOW (window));
2569 account = modest_account_mgr_get_default_account (modest_runtime_get_account_mgr ());
2570 msg_win = modest_msg_view_window_new_for_attachment (TNY_MSG (mime_part), account,
2571 mailbox, attachment_uid);
2572 modest_window_set_zoom (MODEST_WINDOW (msg_win),
2573 modest_window_get_zoom (MODEST_WINDOW (window)));
2574 if (modest_window_mgr_register_window (mgr, msg_win, MODEST_WINDOW (window)))
2575 gtk_widget_show_all (GTK_WIDGET (msg_win));
2577 gtk_widget_destroy (GTK_WIDGET (msg_win));
2583 g_free (attachment_uid);
2585 g_object_unref (mime_part);
2597 GnomeVFSResult result;
2600 static void save_mime_part_info_free (SaveMimePartInfo *info, gboolean with_struct);
2601 static gboolean idle_save_mime_part_show_result (SaveMimePartInfo *info);
2602 static gpointer save_mime_part_to_file (SaveMimePartInfo *info);
2603 static void save_mime_parts_to_file_with_checks (SaveMimePartInfo *info);
2606 save_mime_part_info_free (SaveMimePartInfo *info, gboolean with_struct)
2610 for (node = info->pairs; node != NULL; node = g_list_next (node)) {
2611 SaveMimePartPair *pair = (SaveMimePartPair *) node->data;
2612 g_free (pair->filename);
2613 g_object_unref (pair->part);
2614 g_slice_free (SaveMimePartPair, pair);
2616 g_list_free (info->pairs);
2619 g_slice_free (SaveMimePartInfo, info);
2624 idle_save_mime_part_show_result (SaveMimePartInfo *info)
2626 if (info->pairs != NULL) {
2627 save_mime_part_to_file (info);
2629 /* This is a GDK lock because we are an idle callback and
2630 * hildon_banner_show_information is or does Gtk+ code */
2632 gdk_threads_enter (); /* CHECKED */
2633 save_mime_part_info_free (info, TRUE);
2634 if (info->result == GNOME_VFS_OK) {
2635 hildon_banner_show_information (NULL, NULL, _CS("sfil_ib_saved"));
2636 } else if (info->result == GNOME_VFS_ERROR_NO_SPACE) {
2637 gchar *msg = g_strdup_printf (_KR("cerm_device_memory_full"), "");
2638 modest_platform_information_banner (NULL, NULL, msg);
2641 hildon_banner_show_information (NULL, NULL, _("mail_ib_file_operation_failed"));
2643 gdk_threads_leave (); /* CHECKED */
2650 save_mime_part_to_file (SaveMimePartInfo *info)
2652 GnomeVFSHandle *handle;
2654 SaveMimePartPair *pair = (SaveMimePartPair *) info->pairs->data;
2656 info->result = gnome_vfs_create (&handle, pair->filename, GNOME_VFS_OPEN_WRITE, FALSE, 0644);
2657 if (info->result == GNOME_VFS_OK) {
2658 GError *error = NULL;
2659 stream = tny_vfs_stream_new (handle);
2660 if (tny_mime_part_decode_to_stream (pair->part, stream, &error) < 0) {
2661 g_warning ("modest: could not save attachment %s: %d (%s)\n", pair->filename, error?error->code:-1, error?error->message:"Unknown error");
2663 if ((error->domain == TNY_ERROR_DOMAIN) &&
2664 (error->code == TNY_IO_ERROR_WRITE) &&
2665 (errno == ENOSPC)) {
2666 info->result = GNOME_VFS_ERROR_NO_SPACE;
2668 info->result = GNOME_VFS_ERROR_IO;
2671 g_object_unref (G_OBJECT (stream));
2672 g_object_unref (pair->part);
2673 g_slice_free (SaveMimePartPair, pair);
2674 info->pairs = g_list_delete_link (info->pairs, info->pairs);
2676 g_warning ("modest: could not create save attachment %s: %s\n", pair->filename, gnome_vfs_result_to_string (info->result));
2677 save_mime_part_info_free (info, FALSE);
2680 g_idle_add ((GSourceFunc) idle_save_mime_part_show_result, info);
2685 save_mime_parts_to_file_with_checks (SaveMimePartInfo *info)
2687 gboolean is_ok = TRUE;
2688 gint replaced_files = 0;
2689 const GList *files = info->pairs;
2692 for (iter = files; (iter != NULL) && (replaced_files < 2); iter = g_list_next(iter)) {
2693 SaveMimePartPair *pair = iter->data;
2694 if (modest_utils_file_exists (pair->filename)) {
2698 if (replaced_files) {
2699 GtkWidget *confirm_overwrite_dialog;
2700 const gchar *message = (replaced_files == 1) ?
2701 _FM("docm_nc_replace_file") : _FM("docm_nc_replace_multiple");
2702 confirm_overwrite_dialog = hildon_note_new_confirmation (NULL, message);
2703 if (gtk_dialog_run (GTK_DIALOG (confirm_overwrite_dialog)) != GTK_RESPONSE_OK) {
2706 gtk_widget_destroy (confirm_overwrite_dialog);
2710 save_mime_part_info_free (info, TRUE);
2712 g_thread_create ((GThreadFunc)save_mime_part_to_file, info, FALSE, NULL);
2718 save_attachments_response (GtkDialog *dialog,
2722 TnyList *mime_parts;
2724 GList *files_to_save = NULL;
2725 gchar *current_folder;
2727 mime_parts = TNY_LIST (user_data);
2729 if (arg1 != GTK_RESPONSE_OK)
2732 chooser_uri = gtk_file_chooser_get_uri (GTK_FILE_CHOOSER (dialog));
2733 current_folder = gtk_file_chooser_get_current_folder_uri (GTK_FILE_CHOOSER (dialog));
2734 if (current_folder && current_folder != '\0') {
2736 modest_conf_set_string (modest_runtime_get_conf (), MODEST_CONF_LATEST_SAVE_ATTACHMENT_PATH,
2737 current_folder,&err);
2739 g_debug ("Error storing latest used folder: %s", err->message);
2743 g_free (current_folder);
2745 if (!modest_utils_folder_writable (chooser_uri)) {
2746 hildon_banner_show_information
2747 (NULL, NULL, _FM("sfil_ib_readonly_location"));
2751 iter = tny_list_create_iterator (mime_parts);
2752 while (!tny_iterator_is_done (iter)) {
2753 TnyMimePart *mime_part = (TnyMimePart *) tny_iterator_get_current (iter);
2755 if ((modest_tny_mime_part_is_attachment_for_modest (mime_part)) &&
2756 !tny_mime_part_is_purged (mime_part) &&
2757 (tny_mime_part_get_filename (mime_part) != NULL)) {
2758 SaveMimePartPair *pair;
2760 pair = g_slice_new0 (SaveMimePartPair);
2762 if (tny_list_get_length (mime_parts) > 1) {
2764 gnome_vfs_escape_slashes (tny_mime_part_get_filename (mime_part));
2765 pair->filename = g_build_filename (chooser_uri, escaped, NULL);
2768 pair->filename = g_strdup (chooser_uri);
2770 pair->part = mime_part;
2771 files_to_save = g_list_prepend (files_to_save, pair);
2773 tny_iterator_next (iter);
2775 g_object_unref (iter);
2777 g_free (chooser_uri);
2779 if (files_to_save != NULL) {
2780 SaveMimePartInfo *info = g_slice_new0 (SaveMimePartInfo);
2781 info->pairs = files_to_save;
2782 info->result = TRUE;
2783 save_mime_parts_to_file_with_checks (info);
2787 /* Free and close the dialog */
2788 g_object_unref (mime_parts);
2789 gtk_widget_destroy (GTK_WIDGET (dialog));
2793 modest_msg_view_window_save_attachments (ModestMsgViewWindow *window,
2794 TnyList *mime_parts)
2796 ModestMsgViewWindowPrivate *priv;
2797 GtkWidget *save_dialog = NULL;
2798 gchar *conf_folder = NULL;
2799 gchar *filename = NULL;
2800 gchar *save_multiple_str = NULL;
2802 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window));
2803 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
2805 if (mime_parts == NULL) {
2806 /* In Hildon 2.2 save and delete operate over all the attachments as there's no
2807 * selection available */
2808 mime_parts = modest_msg_view_get_attachments (MODEST_MSG_VIEW (priv->msg_view));
2809 if (mime_parts && !modest_maemo_utils_select_attachments (GTK_WINDOW (window), mime_parts, FALSE)) {
2810 g_object_unref (mime_parts);
2813 if (mime_parts == NULL || tny_list_get_length (mime_parts) == 0) {
2815 g_object_unref (mime_parts);
2821 g_object_ref (mime_parts);
2824 /* prepare dialog */
2825 if (tny_list_get_length (mime_parts) == 1) {
2827 /* only one attachment selected */
2828 iter = tny_list_create_iterator (mime_parts);
2829 TnyMimePart *mime_part = (TnyMimePart *) tny_iterator_get_current (iter);
2830 g_object_unref (iter);
2831 if (!modest_tny_mime_part_is_msg (mime_part) &&
2832 modest_tny_mime_part_is_attachment_for_modest (mime_part) &&
2833 !tny_mime_part_is_purged (mime_part)) {
2834 filename = g_strdup (tny_mime_part_get_filename (mime_part));
2836 /* TODO: show any error? */
2837 g_warning ("Tried to save a non-file attachment");
2838 g_object_unref (mime_parts);
2841 g_object_unref (mime_part);
2843 save_multiple_str = g_strdup_printf (_FM("sfil_va_number_of_objects_attachments"),
2844 tny_list_get_length (mime_parts));
2847 save_dialog = hildon_file_chooser_dialog_new (GTK_WINDOW (window),
2848 GTK_FILE_CHOOSER_ACTION_SAVE);
2851 conf_folder = modest_conf_get_string (modest_runtime_get_conf (), MODEST_CONF_LATEST_SAVE_ATTACHMENT_PATH, NULL);
2852 if (conf_folder && conf_folder[0] != '\0') {
2853 gtk_file_chooser_set_current_folder_uri (GTK_FILE_CHOOSER (save_dialog), conf_folder);
2856 /* Set the default folder to images folder */
2857 docs_folder = g_build_filename (g_getenv (MYDOCS_ENV), DOCS_FOLDER, NULL);
2858 gtk_file_chooser_set_current_folder (GTK_FILE_CHOOSER (save_dialog), docs_folder);
2859 g_free (docs_folder);
2861 g_free (conf_folder);
2865 gtk_file_chooser_set_current_name (GTK_FILE_CHOOSER (save_dialog),
2870 /* if multiple, set multiple string */
2871 if (save_multiple_str) {
2872 g_object_set (G_OBJECT (save_dialog), "save-multiple", save_multiple_str, NULL);
2873 gtk_window_set_title (GTK_WINDOW (save_dialog), _FM("sfil_ti_save_objects_files"));
2876 /* We must run this asynchronously, because the hildon dialog
2877 performs a gtk_dialog_run by itself which leads to gdk
2879 g_signal_connect (save_dialog, "response",
2880 G_CALLBACK (save_attachments_response), mime_parts);
2882 gtk_widget_show_all (save_dialog);
2886 show_remove_attachment_information (gpointer userdata)
2888 ModestMsgViewWindow *window = (ModestMsgViewWindow *) userdata;
2889 ModestMsgViewWindowPrivate *priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
2891 /* We're outside the main lock */
2892 gdk_threads_enter ();
2894 if (priv->remove_attachment_banner != NULL) {
2895 gtk_widget_destroy (priv->remove_attachment_banner);
2896 g_object_unref (priv->remove_attachment_banner);
2899 priv->remove_attachment_banner = g_object_ref (
2900 hildon_banner_show_animation (NULL, NULL, _("mcen_me_inbox_remove_attachments")));
2902 gdk_threads_leave ();
2908 modest_msg_view_window_remove_attachments (ModestMsgViewWindow *window, gboolean get_all)
2910 ModestMsgViewWindowPrivate *priv;
2911 TnyList *mime_parts = NULL;
2912 gchar *confirmation_message;
2918 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window));
2919 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
2921 /* In hildon 2.2 we ignore the get_all flag as we always get all attachments. This is
2922 * because we don't have selection
2924 mime_parts = modest_msg_view_get_attachments (MODEST_MSG_VIEW (priv->msg_view));
2926 /* Remove already purged messages from mime parts list */
2927 iter = tny_list_create_iterator (mime_parts);
2928 while (!tny_iterator_is_done (iter)) {
2929 TnyMimePart *part = TNY_MIME_PART (tny_iterator_get_current (iter));
2930 tny_iterator_next (iter);
2931 if (tny_mime_part_is_purged (part)) {
2932 tny_list_remove (mime_parts, (GObject *) part);
2934 g_object_unref (part);
2936 g_object_unref (iter);
2938 if (!modest_maemo_utils_select_attachments (GTK_WINDOW (window), mime_parts, TRUE) ||
2939 tny_list_get_length (mime_parts) == 0) {
2940 g_object_unref (mime_parts);
2944 n_attachments = tny_list_get_length (mime_parts);
2945 if (n_attachments == 1) {
2949 iter = tny_list_create_iterator (mime_parts);
2950 part = (TnyMimePart *) tny_iterator_get_current (iter);
2951 g_object_unref (iter);
2952 if (modest_tny_mime_part_is_msg (part)) {
2954 header = tny_msg_get_header (TNY_MSG (part));
2955 filename = tny_header_dup_subject (header);
2956 g_object_unref (header);
2957 if (filename == NULL)
2958 filename = g_strdup (_("mail_va_no_subject"));
2960 filename = g_strdup (tny_mime_part_get_filename (TNY_MIME_PART (part)));
2962 confirmation_message = g_strdup_printf (_("mcen_nc_purge_file_text"), filename);
2964 g_object_unref (part);
2966 confirmation_message = g_strdup_printf (ngettext("mcen_nc_purge_file_text",
2967 "mcen_nc_purge_files_text",
2968 n_attachments), n_attachments);
2970 response = modest_platform_run_confirmation_dialog (GTK_WINDOW (window),
2971 confirmation_message);
2972 g_free (confirmation_message);
2974 if (response != GTK_RESPONSE_OK) {
2975 g_object_unref (mime_parts);
2979 priv->purge_timeout = g_timeout_add (2000, show_remove_attachment_information, window);
2981 iter = tny_list_create_iterator (mime_parts);
2982 while (!tny_iterator_is_done (iter)) {
2985 part = (TnyMimePart *) tny_iterator_get_current (iter);
2986 tny_mime_part_set_purged (TNY_MIME_PART (part));
2987 g_object_unref (part);
2988 tny_iterator_next (iter);
2990 g_object_unref (iter);
2992 msg = tny_msg_view_get_msg (TNY_MSG_VIEW (priv->msg_view));
2993 tny_msg_view_clear (TNY_MSG_VIEW (priv->msg_view));
2994 tny_msg_rewrite_cache (msg);
2995 tny_msg_view_set_msg (TNY_MSG_VIEW (priv->msg_view), msg);
2996 g_object_unref (msg);
2998 g_object_unref (mime_parts);
3000 if (priv->purge_timeout > 0) {
3001 g_source_remove (priv->purge_timeout);
3002 priv->purge_timeout = 0;
3005 if (priv->remove_attachment_banner) {
3006 gtk_widget_destroy (priv->remove_attachment_banner);
3007 g_object_unref (priv->remove_attachment_banner);
3008 priv->remove_attachment_banner = NULL;
3016 update_window_title (ModestMsgViewWindow *window)
3018 ModestMsgViewWindowPrivate *priv;
3020 TnyHeader *header = NULL;
3021 gchar *subject = NULL;
3023 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
3025 /* Note that if the window is closed while we're retrieving
3026 the message, this widget could de deleted */
3027 if (!priv->msg_view)
3030 msg = tny_msg_view_get_msg (TNY_MSG_VIEW (priv->msg_view));
3033 header = tny_msg_get_header (msg);
3034 subject = tny_header_dup_subject (header);
3035 g_object_unref (header);
3036 g_object_unref (msg);
3039 if ((subject == NULL)||(subject[0] == '\0')) {
3041 subject = g_strdup (_("mail_va_no_subject"));
3044 gtk_window_set_title (GTK_WINDOW (window), subject);
3049 on_move_focus (GtkWidget *widget,
3050 GtkDirectionType direction,
3053 g_signal_stop_emission_by_name (G_OBJECT (widget), "move-focus");
3057 fetch_image_open_stream (TnyStreamCache *self, gint64 *expected_size, gchar *uri)
3059 GnomeVFSResult result;
3060 GnomeVFSHandle *handle = NULL;
3061 GnomeVFSFileInfo *info = NULL;
3064 result = gnome_vfs_open (&handle, uri, GNOME_VFS_OPEN_READ);
3065 if (result != GNOME_VFS_OK) {
3070 info = gnome_vfs_file_info_new ();
3071 result = gnome_vfs_get_file_info_from_handle (handle, info, GNOME_VFS_FILE_INFO_DEFAULT);
3072 if (result != GNOME_VFS_OK || ! (info->valid_fields & GNOME_VFS_FILE_INFO_FIELDS_SIZE)) {
3073 /* We put a "safe" default size for going to cache */
3074 *expected_size = (300*1024);
3076 *expected_size = info->size;
3078 gnome_vfs_file_info_unref (info);
3080 stream = tny_vfs_stream_new (handle);
3089 TnyStream *output_stream;
3090 GtkWidget *msg_view;
3095 on_fetch_image_idle_refresh_view (gpointer userdata)
3098 FetchImageData *fidata = (FetchImageData *) userdata;
3100 gdk_threads_enter ();
3101 if (GTK_WIDGET_DRAWABLE (fidata->msg_view)) {
3102 ModestMsgViewWindowPrivate *priv;
3104 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (fidata->window);
3105 priv->fetching_images--;
3106 gtk_widget_queue_draw (fidata->msg_view);
3107 update_progress_hint (MODEST_MSG_VIEW_WINDOW (fidata->window));
3109 gdk_threads_leave ();
3111 g_object_unref (fidata->msg_view);
3112 g_object_unref (fidata->window);
3113 g_slice_free (FetchImageData, fidata);
3118 on_fetch_image_thread (gpointer userdata)
3120 FetchImageData *fidata = (FetchImageData *) userdata;
3121 TnyStreamCache *cache;
3122 TnyStream *cache_stream;
3124 cache = modest_runtime_get_images_cache ();
3126 tny_stream_cache_get_stream (cache,
3128 (TnyStreamCacheOpenStreamFetcher) fetch_image_open_stream,
3129 (gpointer) fidata->uri);
3130 g_free (fidata->cache_id);
3131 g_free (fidata->uri);
3133 if (cache_stream != NULL) {
3136 while (G_LIKELY (!tny_stream_is_eos (cache_stream))) {
3139 nb_read = tny_stream_read (cache_stream, buffer, sizeof (buffer));
3140 if (G_UNLIKELY (nb_read < 0)) {
3142 } else if (G_LIKELY (nb_read > 0)) {
3143 gssize nb_written = 0;
3145 while (G_UNLIKELY (nb_written < nb_read)) {
3148 len = tny_stream_write (fidata->output_stream, buffer + nb_written,
3149 nb_read - nb_written);
3150 if (G_UNLIKELY (len < 0))
3156 tny_stream_close (cache_stream);
3157 g_object_unref (cache_stream);
3160 tny_stream_close (fidata->output_stream);
3161 g_object_unref (fidata->output_stream);
3163 g_idle_add (on_fetch_image_idle_refresh_view, fidata);
3169 on_fetch_image (ModestMsgView *msgview,
3172 ModestMsgViewWindow *window)
3174 const gchar *current_account;
3175 ModestMsgViewWindowPrivate *priv;
3176 FetchImageData *fidata;
3178 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
3180 current_account = modest_window_get_active_account (MODEST_WINDOW (window));
3182 fidata = g_slice_new0 (FetchImageData);
3183 fidata->msg_view = g_object_ref (msgview);
3184 fidata->window = g_object_ref (window);
3185 fidata->uri = g_strdup (uri);
3186 fidata->cache_id = modest_images_cache_get_id (current_account, uri);
3187 fidata->output_stream = g_object_ref (stream);
3189 priv->fetching_images++;
3190 if (g_thread_create (on_fetch_image_thread, fidata, FALSE, NULL) == NULL) {
3191 g_object_unref (fidata->output_stream);
3192 g_free (fidata->cache_id);
3193 g_free (fidata->uri);
3194 g_object_unref (fidata->msg_view);
3195 g_slice_free (FetchImageData, fidata);
3196 tny_stream_close (stream);
3197 priv->fetching_images--;
3198 update_progress_hint (window);
3201 update_progress_hint (window);
3207 setup_menu (ModestMsgViewWindow *self)
3209 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW(self));
3211 /* Settings menu buttons */
3212 modest_hildon2_window_add_to_menu (MODEST_HILDON2_WINDOW (self), _("mcen_me_inbox_replytoall"), NULL,
3213 APP_MENU_CALLBACK (modest_ui_actions_on_reply_all),
3214 MODEST_DIMMING_CALLBACK (modest_ui_dimming_rules_on_reply_msg));
3215 modest_hildon2_window_add_to_menu (MODEST_HILDON2_WINDOW (self), _("mcen_me_inbox_forward"), "<Control>d",
3216 APP_MENU_CALLBACK (modest_ui_actions_on_forward),
3217 MODEST_DIMMING_CALLBACK (modest_ui_dimming_rules_on_reply_msg));
3219 modest_hildon2_window_add_to_menu (MODEST_HILDON2_WINDOW (self), _("mcen_me_inbox_mark_as_read"), NULL,
3220 APP_MENU_CALLBACK (modest_ui_actions_on_mark_as_read),
3221 MODEST_DIMMING_CALLBACK (modest_ui_dimming_rules_on_mark_as_read_msg_in_view));
3222 modest_hildon2_window_add_to_menu (MODEST_HILDON2_WINDOW (self), _("mcen_me_inbox_mark_as_unread"), NULL,
3223 APP_MENU_CALLBACK (modest_ui_actions_on_mark_as_unread),
3224 MODEST_DIMMING_CALLBACK (modest_ui_dimming_rules_on_mark_as_unread_msg_in_view));
3226 modest_hildon2_window_add_to_menu (MODEST_HILDON2_WINDOW (self), _("mcen_me_viewer_save_attachments"), NULL,
3227 APP_MENU_CALLBACK (modest_ui_actions_save_attachments),
3228 MODEST_DIMMING_CALLBACK (modest_ui_dimming_rules_on_save_attachments));
3229 modest_hildon2_window_add_to_menu (MODEST_HILDON2_WINDOW (self), _("mcen_me_inbox_remove_attachments"), NULL,
3230 APP_MENU_CALLBACK (modest_ui_actions_remove_attachments),
3231 MODEST_DIMMING_CALLBACK (modest_ui_dimming_rules_on_remove_attachments));
3233 modest_hildon2_window_add_to_menu (MODEST_HILDON2_WINDOW (self), _("mcen_me_new_message"), "<Control>n",
3234 APP_MENU_CALLBACK (modest_ui_actions_on_new_msg),
3235 MODEST_DIMMING_CALLBACK (modest_ui_dimming_rules_on_new_msg));
3236 modest_hildon2_window_add_to_menu (MODEST_HILDON2_WINDOW (self), _("mcen_me_viewer_addtocontacts"), NULL,
3237 APP_MENU_CALLBACK (modest_ui_actions_add_to_contacts),
3238 MODEST_DIMMING_CALLBACK (modest_ui_dimming_rules_on_add_to_contacts));
3240 modest_hildon2_window_add_to_menu (MODEST_HILDON2_WINDOW (self), _("mail_bd_external_images"), NULL,
3241 APP_MENU_CALLBACK (modest_ui_actions_on_fetch_images),
3242 MODEST_DIMMING_CALLBACK (modest_ui_dimming_rules_on_fetch_images));
3243 modest_hildon2_window_add_to_menu (MODEST_HILDON2_WINDOW (self), _("mcen_ti_message_properties"), NULL,
3244 APP_MENU_CALLBACK (modest_ui_actions_on_details),
3245 MODEST_DIMMING_CALLBACK (modest_ui_dimming_rules_on_details));
3249 modest_msg_view_window_add_to_contacts (ModestMsgViewWindow *self)
3251 ModestMsgViewWindowPrivate *priv;
3252 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
3253 GSList *recipients = NULL;
3255 gboolean contacts_to_add = FALSE;
3257 msg = tny_msg_view_get_msg (TNY_MSG_VIEW (priv->msg_view));
3261 header = modest_msg_view_window_get_header (self);
3264 recipients = modest_tny_msg_header_get_all_recipients_list (header);
3265 g_object_unref (header);
3267 recipients = modest_tny_msg_get_all_recipients_list (msg);
3268 g_object_unref (msg);
3271 if (recipients != NULL) {
3272 GtkWidget *picker_dialog;
3273 GtkWidget *selector;
3275 gchar *selected = NULL;
3277 selector = hildon_touch_selector_new_text ();
3278 g_object_ref (selector);
3280 for (node = recipients; node != NULL; node = g_slist_next (node)) {
3281 if (!modest_address_book_has_address ((const gchar *) node->data)) {
3282 hildon_touch_selector_append_text (HILDON_TOUCH_SELECTOR (selector),
3283 (const gchar *) node->data);
3284 contacts_to_add = TRUE;
3288 if (contacts_to_add) {
3291 picker_dialog = hildon_picker_dialog_new (GTK_WINDOW (self));
3292 gtk_window_set_title (GTK_WINDOW (picker_dialog), _("mcen_me_viewer_addtocontacts"));
3294 hildon_picker_dialog_set_selector (HILDON_PICKER_DIALOG (picker_dialog),
3295 HILDON_TOUCH_SELECTOR (selector));
3297 picker_result = gtk_dialog_run (GTK_DIALOG (picker_dialog));
3299 if (picker_result == GTK_RESPONSE_OK) {
3300 selected = hildon_touch_selector_get_current_text (HILDON_TOUCH_SELECTOR (selector));
3302 gtk_widget_destroy (picker_dialog);
3305 modest_address_book_add_address (selected);
3310 g_object_unref (selector);
3315 if (recipients) {g_slist_foreach (recipients, (GFunc) g_free, NULL); g_slist_free (recipients);}
3319 _modest_msg_view_window_map_event (GtkWidget *widget,
3323 ModestMsgViewWindow *self = (ModestMsgViewWindow *) userdata;
3325 update_progress_hint (self);
3331 modest_msg_view_window_fetch_images (ModestMsgViewWindow *self)
3333 ModestMsgViewWindowPrivate *priv;
3334 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
3336 modest_msg_view_request_fetch_images (MODEST_MSG_VIEW (priv->msg_view));
3340 modest_msg_view_window_has_blocked_external_images (ModestMsgViewWindow *self)
3342 ModestMsgViewWindowPrivate *priv;
3343 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
3345 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self), FALSE);
3347 return modest_msg_view_has_blocked_external_images (MODEST_MSG_VIEW (priv->msg_view));