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 priv->main_scroll = hildon_pannable_area_new ();
528 gtk_container_add (GTK_CONTAINER (priv->main_scroll), priv->msg_view);
529 gtk_box_pack_start (GTK_BOX(main_vbox), priv->main_scroll, TRUE, TRUE, 0);
530 gtk_container_add (GTK_CONTAINER(obj), main_vbox);
532 priv->find_toolbar = hildon_find_toolbar_new (NULL);
533 hildon_window_add_toolbar (HILDON_WINDOW (obj), GTK_TOOLBAR (priv->find_toolbar));
534 gtk_widget_set_no_show_all (priv->find_toolbar, TRUE);
536 /* NULL-ize fields if the window is destroyed */
537 g_signal_connect (priv->msg_view, "destroy", G_CALLBACK (gtk_widget_destroyed), &(priv->msg_view));
539 gtk_widget_show_all (GTK_WIDGET(main_vbox));
543 modest_msg_view_window_disconnect_signals (ModestWindow *self)
545 ModestMsgViewWindowPrivate *priv;
546 GtkWidget *header_view = NULL;
547 GtkWindow *parent_window = NULL;
549 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
551 if (gtk_clipboard_get (GDK_SELECTION_PRIMARY) &&
552 g_signal_handler_is_connected (gtk_clipboard_get (GDK_SELECTION_PRIMARY),
553 priv->clipboard_change_handler))
554 g_signal_handler_disconnect (gtk_clipboard_get (GDK_SELECTION_PRIMARY),
555 priv->clipboard_change_handler);
557 if (g_signal_handler_is_connected (G_OBJECT (modest_runtime_get_mail_operation_queue ()),
558 priv->queue_change_handler))
559 g_signal_handler_disconnect (G_OBJECT (modest_runtime_get_mail_operation_queue ()),
560 priv->queue_change_handler);
562 if (g_signal_handler_is_connected (G_OBJECT (modest_runtime_get_account_store ()),
563 priv->account_removed_handler))
564 g_signal_handler_disconnect (G_OBJECT (modest_runtime_get_account_store ()),
565 priv->account_removed_handler);
567 if (priv->header_model) {
568 if (g_signal_handler_is_connected(G_OBJECT (priv->header_model),
569 priv->row_changed_handler))
570 g_signal_handler_disconnect(G_OBJECT (priv->header_model),
571 priv->row_changed_handler);
573 if (g_signal_handler_is_connected(G_OBJECT (priv->header_model),
574 priv->row_deleted_handler))
575 g_signal_handler_disconnect(G_OBJECT (priv->header_model),
576 priv->row_deleted_handler);
578 if (g_signal_handler_is_connected(G_OBJECT (priv->header_model),
579 priv->row_inserted_handler))
580 g_signal_handler_disconnect(G_OBJECT (priv->header_model),
581 priv->row_inserted_handler);
583 if (g_signal_handler_is_connected(G_OBJECT (priv->header_model),
584 priv->rows_reordered_handler))
585 g_signal_handler_disconnect(G_OBJECT (priv->header_model),
586 priv->rows_reordered_handler);
589 modest_signal_mgr_disconnect_all_and_destroy (priv->sighandlers);
590 priv->sighandlers = NULL;
592 parent_window = gtk_window_get_transient_for (GTK_WINDOW (self));
593 if (parent_window && MODEST_IS_HEADER_WINDOW (parent_window)) {
594 header_view = GTK_WIDGET (modest_header_window_get_header_view (MODEST_HEADER_WINDOW (parent_window)));
596 modest_header_view_remove_observer(MODEST_HEADER_VIEW (header_view),
597 MODEST_HEADER_VIEW_OBSERVER(self));
603 modest_msg_view_window_finalize (GObject *obj)
605 ModestMsgViewWindowPrivate *priv;
607 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (obj);
609 /* Sanity check: shouldn't be needed, the window mgr should
610 call this function before */
611 modest_msg_view_window_disconnect_signals (MODEST_WINDOW (obj));
613 if (priv->header_model != NULL) {
614 g_object_unref (priv->header_model);
615 priv->header_model = NULL;
618 if (priv->remove_attachment_banner) {
619 gtk_widget_destroy (priv->remove_attachment_banner);
620 g_object_unref (priv->remove_attachment_banner);
621 priv->remove_attachment_banner = NULL;
624 if (priv->purge_timeout > 0) {
625 g_source_remove (priv->purge_timeout);
626 priv->purge_timeout = 0;
629 if (priv->row_reference) {
630 gtk_tree_row_reference_free (priv->row_reference);
631 priv->row_reference = NULL;
634 if (priv->next_row_reference) {
635 gtk_tree_row_reference_free (priv->next_row_reference);
636 priv->next_row_reference = NULL;
640 g_free (priv->msg_uid);
641 priv->msg_uid = NULL;
644 G_OBJECT_CLASS(parent_class)->finalize (obj);
648 select_next_valid_row (GtkTreeModel *model,
649 GtkTreeRowReference **row_reference,
653 GtkTreeIter tmp_iter;
655 GtkTreePath *next = NULL;
656 gboolean retval = FALSE, finished;
658 g_return_val_if_fail (gtk_tree_row_reference_valid (*row_reference), FALSE);
660 path = gtk_tree_row_reference_get_path (*row_reference);
661 gtk_tree_model_get_iter (model, &tmp_iter, path);
662 gtk_tree_row_reference_free (*row_reference);
663 *row_reference = NULL;
667 TnyHeader *header = NULL;
669 if (gtk_tree_model_iter_next (model, &tmp_iter)) {
670 gtk_tree_model_get (model, &tmp_iter,
671 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
675 if (msg_is_visible (header, is_outbox)) {
676 next = gtk_tree_model_get_path (model, &tmp_iter);
677 *row_reference = gtk_tree_row_reference_new (model, next);
678 gtk_tree_path_free (next);
682 g_object_unref (header);
685 } else if (cycle && gtk_tree_model_get_iter_first (model, &tmp_iter)) {
686 next = gtk_tree_model_get_path (model, &tmp_iter);
688 /* Ensure that we are not selecting the same */
689 if (gtk_tree_path_compare (path, next) != 0) {
690 gtk_tree_model_get (model, &tmp_iter,
691 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
694 if (msg_is_visible (header, is_outbox)) {
695 *row_reference = gtk_tree_row_reference_new (model, next);
699 g_object_unref (header);
703 /* If we ended up in the same message
704 then there is no valid next
708 gtk_tree_path_free (next);
710 /* If there are no more messages and we don't
711 want to start again in the first one then
712 there is no valid next message */
718 gtk_tree_path_free (path);
723 /* TODO: This should be in _init(), with the parameters as properties. */
725 modest_msg_view_window_construct (ModestMsgViewWindow *self,
726 const gchar *modest_account_name,
727 const gchar *mailbox,
728 const gchar *msg_uid)
731 ModestMsgViewWindowPrivate *priv = NULL;
732 ModestWindowPrivate *parent_priv = NULL;
733 ModestDimmingRulesGroup *toolbar_rules_group = NULL;
734 ModestDimmingRulesGroup *clipboard_rules_group = NULL;
736 obj = G_OBJECT (self);
737 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(obj);
738 parent_priv = MODEST_WINDOW_GET_PRIVATE(obj);
740 priv->msg_uid = g_strdup (msg_uid);
743 parent_priv->menubar = NULL;
745 toolbar_rules_group = modest_dimming_rules_group_new (MODEST_DIMMING_RULES_TOOLBAR, TRUE);
746 clipboard_rules_group = modest_dimming_rules_group_new (MODEST_DIMMING_RULES_CLIPBOARD, FALSE);
749 /* Add common dimming rules */
750 modest_dimming_rules_group_add_rules (toolbar_rules_group,
751 modest_msg_view_toolbar_dimming_entries,
752 G_N_ELEMENTS (modest_msg_view_toolbar_dimming_entries),
753 MODEST_WINDOW (self));
754 modest_dimming_rules_group_add_rules (clipboard_rules_group,
755 modest_msg_view_clipboard_dimming_entries,
756 G_N_ELEMENTS (modest_msg_view_clipboard_dimming_entries),
757 MODEST_WINDOW (self));
759 /* Insert dimming rules group for this window */
760 modest_ui_dimming_manager_insert_rules_group (parent_priv->ui_dimming_manager, toolbar_rules_group);
761 modest_ui_dimming_manager_insert_rules_group (parent_priv->ui_dimming_manager, clipboard_rules_group);
762 g_object_unref (toolbar_rules_group);
763 g_object_unref (clipboard_rules_group);
765 /* g_signal_connect (G_OBJECT(obj), "delete-event", G_CALLBACK(on_delete_event), obj); */
767 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);
768 g_signal_connect (G_OBJECT(priv->msg_view), "activate_link",
769 G_CALLBACK (modest_ui_actions_on_msg_link_clicked), obj);
770 g_signal_connect (G_OBJECT(priv->msg_view), "link_hover",
771 G_CALLBACK (modest_ui_actions_on_msg_link_hover), obj);
772 g_signal_connect (G_OBJECT(priv->msg_view), "attachment_clicked",
773 G_CALLBACK (modest_ui_actions_on_msg_attachment_clicked), obj);
774 g_signal_connect (G_OBJECT(priv->msg_view), "recpt_activated",
775 G_CALLBACK (modest_ui_actions_on_msg_recpt_activated), obj);
776 g_signal_connect (G_OBJECT(priv->msg_view), "show_details",
777 G_CALLBACK (modest_ui_actions_on_details), obj);
778 g_signal_connect (G_OBJECT(priv->msg_view), "link_contextual",
779 G_CALLBACK (modest_ui_actions_on_msg_link_contextual), obj);
780 g_signal_connect (G_OBJECT (priv->msg_view), "fetch_image",
781 G_CALLBACK (on_fetch_image), obj);
783 g_signal_connect (G_OBJECT (obj), "key-release-event",
784 G_CALLBACK (modest_msg_view_window_key_event),
787 g_signal_connect (G_OBJECT (obj), "key-press-event",
788 G_CALLBACK (modest_msg_view_window_key_event),
791 g_signal_connect (G_OBJECT (obj), "move-focus",
792 G_CALLBACK (on_move_focus), obj);
794 g_signal_connect (G_OBJECT (obj), "map-event",
795 G_CALLBACK (_modest_msg_view_window_map_event),
798 /* Mail Operation Queue */
799 priv->queue_change_handler = g_signal_connect (G_OBJECT (modest_runtime_get_mail_operation_queue ()),
801 G_CALLBACK (on_queue_changed),
804 /* Account manager */
805 priv->account_removed_handler = g_signal_connect (G_OBJECT (modest_runtime_get_account_store ()),
807 G_CALLBACK(on_account_removed),
810 modest_window_set_active_account (MODEST_WINDOW(obj), modest_account_name);
811 modest_window_set_active_mailbox (MODEST_WINDOW(obj), mailbox);
813 g_signal_connect (G_OBJECT (priv->find_toolbar), "close", G_CALLBACK (modest_msg_view_window_find_toolbar_close), obj);
814 g_signal_connect (G_OBJECT (priv->find_toolbar), "search", G_CALLBACK (modest_msg_view_window_find_toolbar_search), obj);
815 priv->last_search = NULL;
817 modest_msg_view_window_show_toolbar (MODEST_WINDOW (obj), TRUE);
819 /* Init the clipboard actions dim status */
820 modest_msg_view_grab_focus(MODEST_MSG_VIEW (priv->msg_view));
822 update_window_title (MODEST_MSG_VIEW_WINDOW (obj));
827 /* FIXME: parameter checks */
829 modest_msg_view_window_new_with_header_model (TnyMsg *msg,
830 const gchar *modest_account_name,
831 const gchar *mailbox,
832 const gchar *msg_uid,
834 GtkTreeRowReference *row_reference)
836 ModestMsgViewWindow *window = NULL;
837 ModestMsgViewWindowPrivate *priv = NULL;
838 TnyFolder *header_folder = NULL;
839 ModestHeaderView *header_view = NULL;
840 ModestWindowMgr *mgr = NULL;
843 modest_tny_mime_part_to_string (TNY_MIME_PART (msg), 0);
846 mgr = modest_runtime_get_window_mgr ();
847 window = MODEST_MSG_VIEW_WINDOW (modest_window_mgr_get_msg_view_window (mgr));
848 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), NULL);
850 modest_msg_view_window_construct (window, modest_account_name, mailbox, msg_uid);
852 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
854 /* Remember the message list's TreeModel so we can detect changes
855 * and change the list selection when necessary: */
856 header_folder = modest_header_view_get_folder (header_view);
858 priv->is_outbox = (modest_tny_folder_guess_folder_type (header_folder) ==
859 TNY_FOLDER_TYPE_OUTBOX);
860 priv->header_folder_id = tny_folder_get_id (header_folder);
861 g_object_unref(header_folder);
864 /* Setup row references and connect signals */
865 priv->header_model = g_object_ref (model);
868 priv->row_reference = gtk_tree_row_reference_copy (row_reference);
869 priv->next_row_reference = gtk_tree_row_reference_copy (row_reference);
870 select_next_valid_row (model, &(priv->next_row_reference), TRUE, priv->is_outbox);
872 priv->row_reference = NULL;
873 priv->next_row_reference = NULL;
876 /* Connect signals */
877 priv->row_changed_handler =
878 g_signal_connect (GTK_TREE_MODEL(model), "row-changed",
879 G_CALLBACK(modest_msg_view_window_on_row_changed),
881 priv->row_deleted_handler =
882 g_signal_connect (GTK_TREE_MODEL(model), "row-deleted",
883 G_CALLBACK(modest_msg_view_window_on_row_deleted),
885 priv->row_inserted_handler =
886 g_signal_connect (GTK_TREE_MODEL(model), "row-inserted",
887 G_CALLBACK(modest_msg_view_window_on_row_inserted),
889 priv->rows_reordered_handler =
890 g_signal_connect(GTK_TREE_MODEL(model), "rows-reordered",
891 G_CALLBACK(modest_msg_view_window_on_row_reordered),
894 if (header_view != NULL){
895 modest_header_view_add_observer(header_view,
896 MODEST_HEADER_VIEW_OBSERVER(window));
899 tny_msg_view_set_msg (TNY_MSG_VIEW (priv->msg_view), msg);
900 update_window_title (MODEST_MSG_VIEW_WINDOW (window));
902 /* gtk_widget_show_all (GTK_WIDGET (window)); */
903 modest_msg_view_window_update_priority (window);
904 /* Check dimming rules */
905 modest_ui_actions_check_toolbar_dimming_rules (MODEST_WINDOW (window));
906 modest_ui_actions_check_menu_dimming_rules (MODEST_WINDOW (window));
907 modest_window_check_dimming_rules_group (MODEST_WINDOW (window), MODEST_DIMMING_RULES_CLIPBOARD);
909 return MODEST_WINDOW(window);
913 modest_msg_view_window_new_from_header_view (ModestHeaderView *header_view,
914 const gchar *modest_account_name,
915 const gchar *mailbox,
916 const gchar *msg_uid,
917 GtkTreeRowReference *row_reference)
919 ModestMsgViewWindow *window = NULL;
920 ModestMsgViewWindowPrivate *priv = NULL;
921 TnyFolder *header_folder = NULL;
922 ModestWindowMgr *mgr = NULL;
926 mgr = modest_runtime_get_window_mgr ();
927 window = MODEST_MSG_VIEW_WINDOW (modest_window_mgr_get_msg_view_window (mgr));
928 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), NULL);
930 modest_msg_view_window_construct (window, modest_account_name, mailbox, msg_uid);
932 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
934 /* Remember the message list's TreeModel so we can detect changes
935 * and change the list selection when necessary: */
937 if (header_view != NULL){
938 header_folder = modest_header_view_get_folder(header_view);
939 /* This could happen if the header folder was
940 unseleted before opening this msg window (for
941 example if the user selects an account in the
942 folder view of the main window */
944 priv->is_outbox = (modest_tny_folder_guess_folder_type (header_folder) ==
945 TNY_FOLDER_TYPE_OUTBOX);
946 priv->header_folder_id = tny_folder_get_id(header_folder);
947 g_object_unref(header_folder);
951 /* Setup row references and connect signals */
952 priv->header_model = gtk_tree_view_get_model (GTK_TREE_VIEW (header_view));
953 g_object_ref (priv->header_model);
956 priv->row_reference = gtk_tree_row_reference_copy (row_reference);
957 priv->next_row_reference = gtk_tree_row_reference_copy (row_reference);
958 select_next_valid_row (priv->header_model, &(priv->next_row_reference), TRUE, priv->is_outbox);
960 priv->row_reference = NULL;
961 priv->next_row_reference = NULL;
964 /* Connect signals */
965 priv->row_changed_handler =
966 g_signal_connect (GTK_TREE_MODEL(priv->header_model), "row-changed",
967 G_CALLBACK(modest_msg_view_window_on_row_changed),
969 priv->row_deleted_handler =
970 g_signal_connect (GTK_TREE_MODEL(priv->header_model), "row-deleted",
971 G_CALLBACK(modest_msg_view_window_on_row_deleted),
973 priv->row_inserted_handler =
974 g_signal_connect (GTK_TREE_MODEL(priv->header_model), "row-inserted",
975 G_CALLBACK(modest_msg_view_window_on_row_inserted),
977 priv->rows_reordered_handler =
978 g_signal_connect(GTK_TREE_MODEL(priv->header_model), "rows-reordered",
979 G_CALLBACK(modest_msg_view_window_on_row_reordered),
982 if (header_view != NULL){
983 modest_header_view_add_observer(header_view,
984 MODEST_HEADER_VIEW_OBSERVER(window));
987 tny_msg_view_set_msg (TNY_MSG_VIEW (priv->msg_view), NULL);
989 path = gtk_tree_row_reference_get_path (row_reference);
990 if (gtk_tree_model_get_iter (priv->header_model, &iter, path)) {
992 gtk_tree_model_get (priv->header_model, &iter,
993 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
995 message_reader (window, priv, header, row_reference);
996 g_object_unref (header);
998 gtk_tree_path_free (path);
1000 /* Check dimming rules */
1001 modest_ui_actions_check_toolbar_dimming_rules (MODEST_WINDOW (window));
1002 modest_ui_actions_check_menu_dimming_rules (MODEST_WINDOW (window));
1003 modest_window_check_dimming_rules_group (MODEST_WINDOW (window), MODEST_DIMMING_RULES_CLIPBOARD);
1005 return MODEST_WINDOW(window);
1009 modest_msg_view_window_new_for_search_result (TnyMsg *msg,
1010 const gchar *modest_account_name,
1011 const gchar *mailbox,
1012 const gchar *msg_uid)
1014 ModestMsgViewWindow *window = NULL;
1015 ModestMsgViewWindowPrivate *priv = NULL;
1016 ModestWindowMgr *mgr = NULL;
1018 mgr = modest_runtime_get_window_mgr ();
1019 window = MODEST_MSG_VIEW_WINDOW (modest_window_mgr_get_msg_view_window (mgr));
1020 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), NULL);
1021 modest_msg_view_window_construct (window, modest_account_name, mailbox, msg_uid);
1023 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1025 /* Remember that this is a search result,
1026 * so we can disable some UI appropriately: */
1027 priv->is_search_result = TRUE;
1029 tny_msg_view_set_msg (TNY_MSG_VIEW (priv->msg_view), msg);
1031 update_window_title (window);
1032 /* gtk_widget_show_all (GTK_WIDGET (window));*/
1033 modest_msg_view_window_update_priority (window);
1035 /* Check dimming rules */
1036 modest_ui_actions_check_toolbar_dimming_rules (MODEST_WINDOW (window));
1037 modest_ui_actions_check_menu_dimming_rules (MODEST_WINDOW (window));
1038 modest_window_check_dimming_rules_group (MODEST_WINDOW (window), MODEST_DIMMING_RULES_CLIPBOARD);
1040 return MODEST_WINDOW(window);
1044 modest_msg_view_window_new_for_attachment (TnyMsg *msg,
1045 const gchar *modest_account_name,
1046 const gchar *mailbox,
1047 const gchar *msg_uid)
1049 GObject *obj = NULL;
1050 ModestMsgViewWindowPrivate *priv;
1051 ModestWindowMgr *mgr = NULL;
1053 g_return_val_if_fail (msg, NULL);
1054 mgr = modest_runtime_get_window_mgr ();
1055 obj = G_OBJECT (modest_window_mgr_get_msg_view_window (mgr));
1056 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (obj);
1057 modest_msg_view_window_construct (MODEST_MSG_VIEW_WINDOW (obj),
1058 modest_account_name, mailbox, msg_uid);
1060 tny_msg_view_set_msg (TNY_MSG_VIEW (priv->msg_view), msg);
1061 update_window_title (MODEST_MSG_VIEW_WINDOW (obj));
1063 /* gtk_widget_show_all (GTK_WIDGET (obj)); */
1065 /* Check dimming rules */
1066 modest_ui_actions_check_toolbar_dimming_rules (MODEST_WINDOW (obj));
1067 modest_ui_actions_check_menu_dimming_rules (MODEST_WINDOW (obj));
1068 modest_window_check_dimming_rules_group (MODEST_WINDOW (obj), MODEST_DIMMING_RULES_CLIPBOARD);
1070 return MODEST_WINDOW(obj);
1074 modest_msg_view_window_on_row_changed (GtkTreeModel *header_model,
1077 ModestMsgViewWindow *window)
1079 check_dimming_rules_after_change (window);
1083 modest_msg_view_window_on_row_deleted(GtkTreeModel *header_model,
1085 ModestMsgViewWindow *window)
1087 check_dimming_rules_after_change (window);
1089 /* The window could have dissapeared */
1092 check_dimming_rules_after_change (ModestMsgViewWindow *window)
1094 modest_ui_actions_check_menu_dimming_rules (MODEST_WINDOW (window));
1095 modest_ui_actions_check_toolbar_dimming_rules (MODEST_WINDOW (window));
1099 /* On insertions we check if the folder still has the message we are
1100 * showing or do not. If do not, we do nothing. Which means we are still
1101 * not attached to any header folder and thus next/prev buttons are
1102 * still dimmed. Once the message that is shown by msg-view is found, the
1103 * new model of header-view will be attached and the references will be set.
1104 * On each further insertions dimming rules will be checked. However
1105 * this requires extra CPU time at least works.
1106 * (An message might be deleted from TnyFolder and thus will not be
1107 * inserted into the model again for example if it is removed by the
1108 * imap server and the header view is refreshed.)
1111 modest_msg_view_window_on_row_inserted (GtkTreeModel *model,
1112 GtkTreePath *tree_path,
1113 GtkTreeIter *tree_iter,
1114 ModestMsgViewWindow *window)
1116 ModestMsgViewWindowPrivate *priv = NULL;
1117 TnyHeader *header = NULL;
1119 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window));
1120 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1122 g_assert (model == priv->header_model);
1124 /* Check if the newly inserted message is the same we are actually
1125 * showing. IF not, we should remain detached from the header model
1126 * and thus prev and next toolbar buttons should remain dimmed. */
1127 gtk_tree_model_get (model, tree_iter,
1128 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
1131 if (TNY_IS_HEADER (header)) {
1134 uid = modest_tny_folder_get_header_unique_id (header);
1135 if (!g_str_equal(priv->msg_uid, uid)) {
1136 check_dimming_rules_after_change (window);
1138 g_object_unref (G_OBJECT(header));
1142 g_object_unref(G_OBJECT(header));
1145 if (priv->row_reference) {
1146 gtk_tree_row_reference_free (priv->row_reference);
1149 /* Setup row_reference for the actual msg. */
1150 priv->row_reference = gtk_tree_row_reference_new (priv->header_model, tree_path);
1151 if (priv->row_reference == NULL) {
1152 g_warning("No reference for msg header item.");
1156 /* Now set up next_row_reference. */
1157 if (priv->next_row_reference) {
1158 gtk_tree_row_reference_free (priv->next_row_reference);
1161 priv->next_row_reference = gtk_tree_row_reference_copy (priv->row_reference);
1162 select_next_valid_row (priv->header_model,
1163 &(priv->next_row_reference), FALSE, priv->is_outbox);
1165 /* Connect the remaining callbacks to become able to detect
1166 * changes in header-view. */
1167 priv->row_changed_handler =
1168 g_signal_connect (priv->header_model, "row-changed",
1169 G_CALLBACK (modest_msg_view_window_on_row_changed),
1171 priv->row_deleted_handler =
1172 g_signal_connect (priv->header_model, "row-deleted",
1173 G_CALLBACK (modest_msg_view_window_on_row_deleted),
1175 priv->rows_reordered_handler =
1176 g_signal_connect (priv->header_model, "rows-reordered",
1177 G_CALLBACK (modest_msg_view_window_on_row_reordered),
1180 check_dimming_rules_after_change (window);
1184 modest_msg_view_window_on_row_reordered (GtkTreeModel *header_model,
1188 ModestMsgViewWindow *window)
1190 ModestMsgViewWindowPrivate *priv = NULL;
1191 gboolean already_changed = FALSE;
1193 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(window);
1195 /* If the current row was reordered select the proper next
1196 valid row. The same if the next row reference changes */
1197 if (priv->row_reference &&
1198 gtk_tree_row_reference_valid (priv->row_reference)) {
1200 path = gtk_tree_row_reference_get_path (priv->row_reference);
1201 if (gtk_tree_path_compare (path, arg1) == 0) {
1202 if (priv->next_row_reference) {
1203 gtk_tree_row_reference_free (priv->next_row_reference);
1205 priv->next_row_reference = gtk_tree_row_reference_copy (priv->row_reference);
1206 select_next_valid_row (header_model, &(priv->next_row_reference), FALSE, priv->is_outbox);
1207 already_changed = TRUE;
1209 gtk_tree_path_free (path);
1211 if (!already_changed &&
1212 priv->next_row_reference &&
1213 gtk_tree_row_reference_valid (priv->next_row_reference)) {
1215 path = gtk_tree_row_reference_get_path (priv->next_row_reference);
1216 if (gtk_tree_path_compare (path, arg1) == 0) {
1217 if (priv->next_row_reference) {
1218 gtk_tree_row_reference_free (priv->next_row_reference);
1220 priv->next_row_reference = gtk_tree_row_reference_copy (priv->row_reference);
1221 select_next_valid_row (header_model, &(priv->next_row_reference), FALSE, priv->is_outbox);
1223 gtk_tree_path_free (path);
1225 check_dimming_rules_after_change (window);
1228 /* The modest_msg_view_window_update_model_replaced implements update
1229 * function for ModestHeaderViewObserver. Checks whether the TnyFolder
1230 * actually belongs to the header-view is the same as the TnyFolder of
1231 * the message of msg-view or not. If they are different, there is
1232 * nothing to do. If they are the same, then the model has replaced and
1233 * the reference in msg-view shall be replaced from the old model to
1234 * the new model. In this case the view will be detached from it's
1235 * header folder. From this point the next/prev buttons are dimmed.
1238 modest_msg_view_window_update_model_replaced (ModestHeaderViewObserver *observer,
1239 GtkTreeModel *model,
1240 const gchar *tny_folder_id)
1242 ModestMsgViewWindowPrivate *priv = NULL;
1243 ModestMsgViewWindow *window = NULL;
1245 g_assert(MODEST_IS_HEADER_VIEW_OBSERVER(observer));
1246 g_assert(MODEST_IS_MSG_VIEW_WINDOW(observer));
1248 window = MODEST_MSG_VIEW_WINDOW(observer);
1249 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(window);
1251 /* If there is an other folder in the header-view then we do
1252 * not care about it's model (msg list). Else if the
1253 * header-view shows the folder the msg shown by us is in, we
1254 * shall replace our model reference and make some check. */
1255 if(model == NULL || tny_folder_id == NULL ||
1256 (priv->header_folder_id && !g_str_equal(tny_folder_id, priv->header_folder_id)))
1259 /* Model is changed(replaced), so we should forget the old
1260 * one. Because there might be other references and there
1261 * might be some change on the model even if we unreferenced
1262 * it, we need to disconnect our signals here. */
1263 if (priv->header_model) {
1264 if (g_signal_handler_is_connected(G_OBJECT (priv->header_model),
1265 priv->row_changed_handler))
1266 g_signal_handler_disconnect(G_OBJECT (priv->header_model),
1267 priv->row_changed_handler);
1268 if (g_signal_handler_is_connected(G_OBJECT (priv->header_model),
1269 priv->row_deleted_handler))
1270 g_signal_handler_disconnect(G_OBJECT (priv->header_model),
1271 priv->row_deleted_handler);
1272 if (g_signal_handler_is_connected(G_OBJECT (priv->header_model),
1273 priv->row_inserted_handler))
1274 g_signal_handler_disconnect(G_OBJECT (priv->header_model),
1275 priv->row_inserted_handler);
1276 if (g_signal_handler_is_connected(G_OBJECT (priv->header_model),
1277 priv->rows_reordered_handler))
1278 g_signal_handler_disconnect(G_OBJECT (priv->header_model),
1279 priv->rows_reordered_handler);
1282 if (priv->row_reference)
1283 gtk_tree_row_reference_free (priv->row_reference);
1284 if (priv->next_row_reference)
1285 gtk_tree_row_reference_free (priv->next_row_reference);
1286 g_object_unref(priv->header_model);
1289 priv->row_changed_handler = 0;
1290 priv->row_deleted_handler = 0;
1291 priv->row_inserted_handler = 0;
1292 priv->rows_reordered_handler = 0;
1293 priv->next_row_reference = NULL;
1294 priv->row_reference = NULL;
1295 priv->header_model = NULL;
1298 priv->header_model = g_object_ref (model);
1300 /* Also we must connect to the new model for row insertions.
1301 * Only for insertions now. We will need other ones only after
1302 * the msg is show by msg-view is added to the new model. */
1303 priv->row_inserted_handler =
1304 g_signal_connect (priv->header_model, "row-inserted",
1305 G_CALLBACK(modest_msg_view_window_on_row_inserted),
1308 modest_ui_actions_check_menu_dimming_rules(MODEST_WINDOW(window));
1309 modest_ui_actions_check_toolbar_dimming_rules(MODEST_WINDOW(window));
1313 modest_msg_view_window_toolbar_on_transfer_mode (ModestMsgViewWindow *self)
1315 ModestMsgViewWindowPrivate *priv= NULL;
1317 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self), FALSE);
1318 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
1320 return priv->progress_hint;
1324 modest_msg_view_window_get_header (ModestMsgViewWindow *self)
1326 ModestMsgViewWindowPrivate *priv= NULL;
1328 TnyHeader *header = NULL;
1329 GtkTreePath *path = NULL;
1332 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self), NULL);
1333 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
1335 /* If the message was not obtained from a treemodel,
1336 * for instance if it was opened directly by the search UI:
1338 if (priv->header_model == NULL ||
1339 priv->row_reference == NULL ||
1340 !gtk_tree_row_reference_valid (priv->row_reference)) {
1341 msg = modest_msg_view_window_get_message (self);
1343 header = tny_msg_get_header (msg);
1344 g_object_unref (msg);
1349 /* Get iter of the currently selected message in the header view: */
1350 path = gtk_tree_row_reference_get_path (priv->row_reference);
1351 g_return_val_if_fail (path != NULL, NULL);
1352 gtk_tree_model_get_iter (priv->header_model,
1356 /* Get current message header */
1357 gtk_tree_model_get (priv->header_model, &iter,
1358 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
1361 gtk_tree_path_free (path);
1366 modest_msg_view_window_get_message (ModestMsgViewWindow *self)
1368 ModestMsgViewWindowPrivate *priv;
1370 g_return_val_if_fail (self, NULL);
1372 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(self);
1374 return tny_msg_view_get_msg (TNY_MSG_VIEW (priv->msg_view));
1378 modest_msg_view_window_get_message_uid (ModestMsgViewWindow *self)
1380 ModestMsgViewWindowPrivate *priv;
1382 g_return_val_if_fail (self, NULL);
1384 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
1386 return (const gchar*) priv->msg_uid;
1390 modest_msg_view_window_toggle_find_toolbar (GtkToggleAction *toggle,
1393 ModestMsgViewWindow *window = MODEST_MSG_VIEW_WINDOW (data);
1394 ModestMsgViewWindowPrivate *priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1395 ModestWindowPrivate *parent_priv = MODEST_WINDOW_GET_PRIVATE (window);
1399 is_active = gtk_toggle_action_get_active (toggle);
1402 gtk_widget_show (priv->find_toolbar);
1403 hildon_find_toolbar_highlight_entry (HILDON_FIND_TOOLBAR (priv->find_toolbar), TRUE);
1405 gtk_widget_hide (priv->find_toolbar);
1406 modest_msg_view_grab_focus (MODEST_MSG_VIEW (priv->msg_view));
1409 /* update the toggle buttons status */
1410 action = gtk_ui_manager_get_action (parent_priv->ui_manager, "/ToolBar/FindInMessage");
1412 modest_utils_toggle_action_set_active_block_notify (GTK_TOGGLE_ACTION (action), is_active);
1417 modest_msg_view_window_find_toolbar_close (GtkWidget *widget,
1418 ModestMsgViewWindow *obj)
1420 GtkToggleAction *toggle;
1421 ModestWindowPrivate *parent_priv;
1422 ModestMsgViewWindowPrivate *priv;
1424 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (obj);
1425 parent_priv = MODEST_WINDOW_GET_PRIVATE (obj);
1427 toggle = GTK_TOGGLE_ACTION (gtk_ui_manager_get_action (parent_priv->ui_manager, "/ToolBar/FindInMessage"));
1428 gtk_toggle_action_set_active (toggle, FALSE);
1429 modest_msg_view_grab_focus (MODEST_MSG_VIEW (priv->msg_view));
1433 modest_msg_view_window_find_toolbar_search (GtkWidget *widget,
1434 ModestMsgViewWindow *obj)
1436 gchar *current_search;
1437 ModestMsgViewWindowPrivate *priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (obj);
1439 if (modest_mime_part_view_is_empty (MODEST_MIME_PART_VIEW (priv->msg_view))) {
1440 hildon_banner_show_information (NULL, NULL, _("mail_ib_nothing_to_find"));
1444 g_object_get (G_OBJECT (widget), "prefix", ¤t_search, NULL);
1446 if ((current_search == NULL) || (strcmp (current_search, "") == 0)) {
1447 g_free (current_search);
1448 hildon_banner_show_information (NULL, NULL, _CS("ecdg_ib_find_rep_enter_text"));
1452 if ((priv->last_search == NULL) || (strcmp (priv->last_search, current_search) != 0)) {
1454 g_free (priv->last_search);
1455 priv->last_search = g_strdup (current_search);
1456 result = modest_isearch_view_search (MODEST_ISEARCH_VIEW (priv->msg_view),
1459 hildon_banner_show_information (NULL, NULL,
1460 _HL("ckct_ib_find_no_matches"));
1461 g_free (priv->last_search);
1462 priv->last_search = NULL;
1464 hildon_find_toolbar_highlight_entry (HILDON_FIND_TOOLBAR (priv->find_toolbar), TRUE);
1467 if (!modest_isearch_view_search_next (MODEST_ISEARCH_VIEW (priv->msg_view))) {
1468 hildon_banner_show_information (NULL, NULL,
1469 _HL("ckct_ib_find_search_complete"));
1470 g_free (priv->last_search);
1471 priv->last_search = NULL;
1473 hildon_find_toolbar_highlight_entry (HILDON_FIND_TOOLBAR (priv->find_toolbar), TRUE);
1477 g_free (current_search);
1482 modest_msg_view_window_set_zoom (ModestWindow *window,
1485 ModestMsgViewWindowPrivate *priv;
1486 ModestWindowPrivate *parent_priv;
1488 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window));
1490 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1491 parent_priv = MODEST_WINDOW_GET_PRIVATE (window);
1492 modest_zoomable_set_zoom (MODEST_ZOOMABLE (priv->msg_view), zoom);
1497 modest_msg_view_window_get_zoom (ModestWindow *window)
1499 ModestMsgViewWindowPrivate *priv;
1501 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), 1.0);
1503 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1504 return modest_zoomable_get_zoom (MODEST_ZOOMABLE (priv->msg_view));
1508 modest_msg_view_window_zoom_plus (ModestWindow *window)
1511 ModestMsgViewWindowPrivate *priv;
1515 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), 1.0);
1516 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1518 zoom_level = modest_zoomable_get_zoom (MODEST_ZOOMABLE (priv->msg_view));
1520 if (zoom_level >= 2.0) {
1521 hildon_banner_show_information (NULL, NULL,
1522 _CS("ckct_ib_max_zoom_level_reached"));
1524 } else if (zoom_level >= 1.5) {
1526 } else if (zoom_level >= 1.2) {
1528 } else if (zoom_level >= 1.0) {
1530 } else if (zoom_level >= 0.8) {
1532 } else if (zoom_level >= 0.5) {
1538 /* set zoom level */
1539 int_zoom = (gint) rint (zoom_level*100.0+0.1);
1540 banner_text = g_strdup_printf (_HL("wdgt_ib_zoom"), int_zoom);
1541 modest_platform_information_banner (GTK_WIDGET (window), NULL, banner_text);
1542 g_free (banner_text);
1543 modest_zoomable_set_zoom (MODEST_ZOOMABLE (priv->msg_view), zoom_level);
1549 modest_msg_view_window_zoom_minus (ModestWindow *window)
1552 ModestMsgViewWindowPrivate *priv;
1556 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), 1.0);
1557 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1559 zoom_level = modest_zoomable_get_zoom (MODEST_ZOOMABLE (priv->msg_view));
1561 if (zoom_level <= 0.5) {
1562 hildon_banner_show_information (NULL, NULL,
1563 _CS("ckct_ib_min_zoom_level_reached"));
1565 } else if (zoom_level <= 0.8) {
1567 } else if (zoom_level <= 1.0) {
1569 } else if (zoom_level <= 1.2) {
1571 } else if (zoom_level <= 1.5) {
1573 } else if (zoom_level <= 2.0) {
1579 /* set zoom level */
1580 int_zoom = (gint) rint (zoom_level*100.0+0.1);
1581 banner_text = g_strdup_printf (_HL("wdgt_ib_zoom"), int_zoom);
1582 modest_platform_information_banner (GTK_WIDGET (window), NULL, banner_text);
1583 g_free (banner_text);
1584 modest_zoomable_set_zoom (MODEST_ZOOMABLE (priv->msg_view), zoom_level);
1591 modest_msg_view_window_key_event (GtkWidget *window,
1597 focus = gtk_window_get_focus (GTK_WINDOW (window));
1599 /* for the find toolbar case */
1600 if (focus && GTK_IS_ENTRY (focus)) {
1601 if (event->keyval == GDK_BackSpace) {
1603 copy = gdk_event_copy ((GdkEvent *) event);
1604 gtk_widget_event (focus, copy);
1605 gdk_event_free (copy);
1610 if (event->keyval == GDK_Up || event->keyval == GDK_KP_Up ||
1611 event->keyval == GDK_Down || event->keyval == GDK_KP_Down ||
1612 event->keyval == GDK_Page_Up || event->keyval == GDK_KP_Page_Up ||
1613 event->keyval == GDK_Page_Down || event->keyval == GDK_KP_Page_Down ||
1614 event->keyval == GDK_Home || event->keyval == GDK_KP_Home ||
1615 event->keyval == GDK_End || event->keyval == GDK_KP_End) {
1616 /* ModestMsgViewWindowPrivate *priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window); */
1617 /* gboolean return_value; */
1619 if (event->type == GDK_KEY_PRESS) {
1620 GtkScrollType scroll_type;
1622 switch (event->keyval) {
1625 scroll_type = GTK_SCROLL_STEP_UP; break;
1628 scroll_type = GTK_SCROLL_STEP_DOWN; break;
1630 case GDK_KP_Page_Up:
1631 scroll_type = GTK_SCROLL_PAGE_UP; break;
1633 case GDK_KP_Page_Down:
1634 scroll_type = GTK_SCROLL_PAGE_DOWN; break;
1637 scroll_type = GTK_SCROLL_START; break;
1640 scroll_type = GTK_SCROLL_END; break;
1641 default: scroll_type = GTK_SCROLL_NONE;
1644 /* g_signal_emit_by_name (G_OBJECT (priv->main_scroll), "scroll-child", */
1645 /* scroll_type, FALSE, &return_value); */
1656 modest_msg_view_window_last_message_selected (ModestMsgViewWindow *window)
1659 ModestMsgViewWindowPrivate *priv;
1660 GtkTreeIter tmp_iter;
1661 gboolean is_last_selected;
1663 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), TRUE);
1664 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1666 /*if no model (so no rows at all), then virtually we are the last*/
1667 if (!priv->header_model || !priv->row_reference)
1670 if (!gtk_tree_row_reference_valid (priv->row_reference))
1673 path = gtk_tree_row_reference_get_path (priv->row_reference);
1677 is_last_selected = TRUE;
1678 while (is_last_selected) {
1680 gtk_tree_path_next (path);
1681 if (!gtk_tree_model_get_iter (priv->header_model, &tmp_iter, path))
1683 gtk_tree_model_get (priv->header_model, &tmp_iter,
1684 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
1687 if (msg_is_visible (header, priv->is_outbox))
1688 is_last_selected = FALSE;
1689 g_object_unref(G_OBJECT(header));
1692 gtk_tree_path_free (path);
1693 return is_last_selected;
1697 modest_msg_view_window_has_headers_model (ModestMsgViewWindow *window)
1699 ModestMsgViewWindowPrivate *priv;
1701 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), TRUE);
1702 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1704 return priv->header_model != NULL;
1708 modest_msg_view_window_is_search_result (ModestMsgViewWindow *window)
1710 ModestMsgViewWindowPrivate *priv;
1712 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), TRUE);
1713 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1715 return priv->is_search_result;
1719 msg_is_visible (TnyHeader *header, gboolean check_outbox)
1721 if ((tny_header_get_flags(header) & TNY_HEADER_FLAG_DELETED))
1723 if (!check_outbox) {
1726 ModestTnySendQueueStatus status;
1727 status = modest_tny_all_send_queues_get_msg_status (header);
1728 return ((status != MODEST_TNY_SEND_QUEUE_FAILED) &&
1729 (status != MODEST_TNY_SEND_QUEUE_SENDING));
1734 modest_msg_view_window_first_message_selected (ModestMsgViewWindow *window)
1737 ModestMsgViewWindowPrivate *priv;
1738 gboolean is_first_selected;
1739 GtkTreeIter tmp_iter;
1741 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), TRUE);
1742 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1744 /*if no model (so no rows at all), then virtually we are the first*/
1745 if (!priv->header_model || !priv->row_reference)
1748 if (!gtk_tree_row_reference_valid (priv->row_reference))
1751 path = gtk_tree_row_reference_get_path (priv->row_reference);
1755 is_first_selected = TRUE;
1756 while (is_first_selected) {
1758 if(!gtk_tree_path_prev (path))
1760 /* Here the 'if' is needless for logic, but let make sure
1761 * iter is valid for gtk_tree_model_get. */
1762 if (!gtk_tree_model_get_iter (priv->header_model, &tmp_iter, path))
1764 gtk_tree_model_get (priv->header_model, &tmp_iter,
1765 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
1768 if (msg_is_visible (header, priv->is_outbox))
1769 is_first_selected = FALSE;
1770 g_object_unref(G_OBJECT(header));
1773 gtk_tree_path_free (path);
1774 return is_first_selected;
1779 GtkTreeRowReference *row_reference;
1783 message_reader_performer (gboolean canceled,
1785 GtkWindow *parent_window,
1786 TnyAccount *account,
1789 ModestMailOperation *mail_op = NULL;
1790 MsgReaderInfo *info;
1792 info = (MsgReaderInfo *) user_data;
1793 if (canceled || err) {
1794 update_window_title (MODEST_MSG_VIEW_WINDOW (parent_window));
1798 /* Register the header - it'll be unregistered in the callback */
1799 modest_window_mgr_register_header (modest_runtime_get_window_mgr (), info->header, NULL);
1801 /* New mail operation */
1802 mail_op = modest_mail_operation_new_with_error_handling (G_OBJECT(parent_window),
1803 modest_ui_actions_disk_operations_error_handler,
1806 modest_mail_operation_queue_add (modest_runtime_get_mail_operation_queue (), mail_op);
1807 modest_mail_operation_get_msg (mail_op, info->header, TRUE, view_msg_cb, info->row_reference);
1808 g_object_unref (mail_op);
1810 /* Update dimming rules */
1811 modest_ui_actions_check_toolbar_dimming_rules (MODEST_WINDOW (parent_window));
1812 modest_ui_actions_check_menu_dimming_rules (MODEST_WINDOW (parent_window));
1815 /* Frees. The row_reference will be freed by the view_msg_cb callback */
1816 g_object_unref (info->header);
1817 g_slice_free (MsgReaderInfo, info);
1822 * Reads the message whose summary item is @header. It takes care of
1823 * several things, among others:
1825 * If the message was not previously downloaded then ask the user
1826 * before downloading. If there is no connection launch the connection
1827 * dialog. Update toolbar dimming rules.
1829 * Returns: TRUE if the mail operation was started, otherwise if the
1830 * user do not want to download the message, or if the user do not
1831 * want to connect, then the operation is not issued
1834 message_reader (ModestMsgViewWindow *window,
1835 ModestMsgViewWindowPrivate *priv,
1837 GtkTreeRowReference *row_reference)
1839 ModestWindowMgr *mgr;
1840 TnyAccount *account;
1842 MsgReaderInfo *info;
1844 g_return_val_if_fail (row_reference != NULL, FALSE);
1846 /* We set the header from model while we're loading */
1847 tny_header_view_set_header (TNY_HEADER_VIEW (priv->msg_view), header);
1848 gtk_window_set_title (GTK_WINDOW (window), _CS("ckdg_pb_updating"));
1850 mgr = modest_runtime_get_window_mgr ();
1851 /* Msg download completed */
1852 if (!(tny_header_get_flags (header) & TNY_HEADER_FLAG_CACHED)) {
1854 /* Ask the user if he wants to download the message if
1856 if (!tny_device_is_online (modest_runtime_get_device())) {
1857 GtkResponseType response;
1859 response = modest_platform_run_confirmation_dialog (GTK_WINDOW (window),
1860 _("mcen_nc_get_msg"));
1861 if (response == GTK_RESPONSE_CANCEL) {
1862 update_window_title (window);
1866 folder = tny_header_get_folder (header);
1867 info = g_slice_new (MsgReaderInfo);
1868 info->header = g_object_ref (header);
1869 info->row_reference = gtk_tree_row_reference_copy (row_reference);
1871 /* Offer the connection dialog if necessary */
1872 modest_platform_connect_if_remote_and_perform ((GtkWindow *) window,
1874 TNY_FOLDER_STORE (folder),
1875 message_reader_performer,
1877 g_object_unref (folder);
1882 folder = tny_header_get_folder (header);
1883 account = tny_folder_get_account (folder);
1884 info = g_slice_new (MsgReaderInfo);
1885 info->header = g_object_ref (header);
1886 info->row_reference = gtk_tree_row_reference_copy (row_reference);
1888 message_reader_performer (FALSE, NULL, (GtkWindow *) window, account, info);
1889 g_object_unref (account);
1890 g_object_unref (folder);
1896 modest_msg_view_window_select_next_message (ModestMsgViewWindow *window)
1898 ModestMsgViewWindowPrivate *priv;
1899 GtkTreePath *path= NULL;
1900 GtkTreeIter tmp_iter;
1902 gboolean retval = TRUE;
1903 GtkTreeRowReference *row_reference = NULL;
1905 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), FALSE);
1906 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1908 if (!priv->row_reference)
1911 /* Update the next row reference if it's not valid. This could
1912 happen if for example the header which it was pointing to,
1913 was deleted. The best place to do it is in the row-deleted
1914 handler but the tinymail model do not work like the glib
1915 tree models and reports the deletion when the row is still
1917 if (!gtk_tree_row_reference_valid (priv->next_row_reference)) {
1918 if (gtk_tree_row_reference_valid (priv->row_reference)) {
1919 priv->next_row_reference = gtk_tree_row_reference_copy (priv->row_reference);
1920 select_next_valid_row (priv->header_model, &(priv->next_row_reference), FALSE, priv->is_outbox);
1923 if (priv->next_row_reference)
1924 path = gtk_tree_row_reference_get_path (priv->next_row_reference);
1928 row_reference = gtk_tree_row_reference_copy (priv->next_row_reference);
1930 gtk_tree_model_get_iter (priv->header_model,
1933 gtk_tree_path_free (path);
1935 gtk_tree_model_get (priv->header_model, &tmp_iter,
1936 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
1939 /* Read the message & show it */
1940 if (!message_reader (window, priv, header, row_reference)) {
1943 gtk_tree_row_reference_free (row_reference);
1946 g_object_unref (header);
1952 modest_msg_view_window_select_previous_message (ModestMsgViewWindow *window)
1954 ModestMsgViewWindowPrivate *priv = NULL;
1956 gboolean finished = FALSE;
1957 gboolean retval = FALSE;
1959 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), FALSE);
1960 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1962 /* Return inmediatly if there is no header model */
1963 if (!priv->header_model || !priv->row_reference)
1966 path = gtk_tree_row_reference_get_path (priv->row_reference);
1967 while (!finished && gtk_tree_path_prev (path)) {
1971 gtk_tree_model_get_iter (priv->header_model, &iter, path);
1972 gtk_tree_model_get (priv->header_model, &iter,
1973 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
1977 if (msg_is_visible (header, priv->is_outbox)) {
1978 GtkTreeRowReference *row_reference;
1979 row_reference = gtk_tree_row_reference_new (priv->header_model, path);
1980 /* Read the message & show it */
1981 retval = message_reader (window, priv, header, row_reference);
1982 gtk_tree_row_reference_free (row_reference);
1986 g_object_unref (header);
1990 gtk_tree_path_free (path);
1995 view_msg_cb (ModestMailOperation *mail_op,
2002 ModestMsgViewWindow *self = NULL;
2003 ModestMsgViewWindowPrivate *priv = NULL;
2004 GtkTreeRowReference *row_reference = NULL;
2006 /* Unregister the header (it was registered before creating the mail operation) */
2007 modest_window_mgr_unregister_header (modest_runtime_get_window_mgr (), header);
2009 row_reference = (GtkTreeRowReference *) user_data;
2011 gtk_tree_row_reference_free (row_reference);
2012 self = (ModestMsgViewWindow *) modest_mail_operation_get_source (mail_op);
2014 /* Restore window title */
2015 update_window_title (self);
2016 g_object_unref (self);
2021 /* If there was any error */
2022 if (!modest_ui_actions_msg_retrieval_check (mail_op, header, msg)) {
2023 gtk_tree_row_reference_free (row_reference);
2024 self = (ModestMsgViewWindow *) modest_mail_operation_get_source (mail_op);
2026 /* Restore window title */
2027 update_window_title (self);
2028 g_object_unref (self);
2033 /* Get the window */
2034 self = (ModestMsgViewWindow *) modest_mail_operation_get_source (mail_op);
2035 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self));
2036 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
2038 /* Update the row reference */
2039 if (priv->row_reference != NULL) {
2040 gtk_tree_row_reference_free (priv->row_reference);
2041 priv->row_reference = gtk_tree_row_reference_copy (row_reference);
2042 if (priv->next_row_reference != NULL) {
2043 gtk_tree_row_reference_free (priv->next_row_reference);
2045 priv->next_row_reference = gtk_tree_row_reference_copy (priv->row_reference);
2046 select_next_valid_row (priv->header_model, &(priv->next_row_reference), TRUE, priv->is_outbox);
2049 /* Mark header as read */
2050 if (!(tny_header_get_flags (header) & TNY_HEADER_FLAG_SEEN))
2051 tny_header_set_flag (header, TNY_HEADER_FLAG_SEEN);
2053 /* Set new message */
2054 if (priv->msg_view != NULL && TNY_IS_MSG_VIEW (priv->msg_view)) {
2055 tny_msg_view_set_msg (TNY_MSG_VIEW (priv->msg_view), msg);
2056 modest_msg_view_window_update_priority (self);
2057 update_window_title (MODEST_MSG_VIEW_WINDOW (self));
2058 modest_msg_view_grab_focus (MODEST_MSG_VIEW (priv->msg_view));
2061 /* Set the new message uid of the window */
2062 if (priv->msg_uid) {
2063 g_free (priv->msg_uid);
2064 priv->msg_uid = modest_tny_folder_get_header_unique_id (header);
2067 /* Notify the observers */
2068 g_signal_emit (G_OBJECT (self), signals[MSG_CHANGED_SIGNAL],
2069 0, priv->header_model, priv->row_reference);
2072 g_object_unref (self);
2073 gtk_tree_row_reference_free (row_reference);
2077 modest_msg_view_window_get_folder_type (ModestMsgViewWindow *window)
2079 ModestMsgViewWindowPrivate *priv;
2081 TnyFolderType folder_type;
2083 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
2085 folder_type = TNY_FOLDER_TYPE_UNKNOWN;
2087 msg = tny_msg_view_get_msg (TNY_MSG_VIEW (priv->msg_view));
2091 folder = tny_msg_get_folder (msg);
2093 folder_type = modest_tny_folder_guess_folder_type (folder);
2094 g_object_unref (folder);
2096 g_object_unref (msg);
2104 modest_msg_view_window_update_priority (ModestMsgViewWindow *window)
2106 ModestMsgViewWindowPrivate *priv;
2107 TnyHeader *header = NULL;
2108 TnyHeaderFlags flags = 0;
2110 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
2112 if (priv->header_model && priv->row_reference) {
2114 GtkTreePath *path = NULL;
2116 path = gtk_tree_row_reference_get_path (priv->row_reference);
2117 g_return_if_fail (path != NULL);
2118 gtk_tree_model_get_iter (priv->header_model,
2120 gtk_tree_row_reference_get_path (priv->row_reference));
2122 gtk_tree_model_get (priv->header_model, &iter, TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
2124 gtk_tree_path_free (path);
2127 msg = tny_msg_view_get_msg (TNY_MSG_VIEW (priv->msg_view));
2129 header = tny_msg_get_header (msg);
2130 g_object_unref (msg);
2135 flags = tny_header_get_flags (header);
2136 g_object_unref(G_OBJECT(header));
2139 modest_msg_view_set_priority (MODEST_MSG_VIEW(priv->msg_view), flags);
2144 toolbar_resize (ModestMsgViewWindow *self)
2146 ModestMsgViewWindowPrivate *priv = NULL;
2147 ModestWindowPrivate *parent_priv = NULL;
2149 gint static_button_size;
2150 ModestWindowMgr *mgr;
2152 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self));
2153 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
2154 parent_priv = MODEST_WINDOW_GET_PRIVATE(self);
2156 mgr = modest_runtime_get_window_mgr ();
2157 static_button_size = modest_window_mgr_get_fullscreen_mode (mgr)?120:120;
2159 if (parent_priv->toolbar) {
2160 /* left size buttons */
2161 widget = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/ToolbarMessageReply");
2162 gtk_tool_item_set_expand (GTK_TOOL_ITEM (widget), FALSE);
2163 gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (widget), FALSE);
2164 gtk_widget_set_size_request (GTK_WIDGET (widget), static_button_size, -1);
2165 widget = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/ToolbarMessageMoveTo");
2166 gtk_tool_item_set_expand (GTK_TOOL_ITEM (widget), FALSE);
2167 gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (widget), FALSE);
2168 gtk_widget_set_size_request (GTK_WIDGET (widget), static_button_size, -1);
2169 widget = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/ToolbarDeleteMessage");
2170 gtk_tool_item_set_expand (GTK_TOOL_ITEM (widget), FALSE);
2171 gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (widget), FALSE);
2172 gtk_widget_set_size_request (GTK_WIDGET (widget), static_button_size, -1);
2173 widget = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/FindInMessage");
2174 gtk_tool_item_set_expand (GTK_TOOL_ITEM (widget), FALSE);
2175 gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (widget), FALSE);
2176 gtk_widget_set_size_request (GTK_WIDGET (widget), static_button_size, -1);
2178 gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (priv->next_toolitem), TRUE);
2179 gtk_tool_item_set_expand (GTK_TOOL_ITEM (priv->next_toolitem), TRUE);
2180 gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (priv->prev_toolitem), TRUE);
2181 gtk_tool_item_set_expand (GTK_TOOL_ITEM (priv->prev_toolitem), TRUE);
2186 modest_msg_view_window_show_toolbar (ModestWindow *self,
2187 gboolean show_toolbar)
2189 ModestMsgViewWindowPrivate *priv = NULL;
2190 ModestWindowPrivate *parent_priv;
2192 parent_priv = MODEST_WINDOW_GET_PRIVATE(self);
2193 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(self);
2195 /* Set optimized view status */
2196 priv->optimized_view = !show_toolbar;
2198 if (!parent_priv->toolbar) {
2199 parent_priv->toolbar = gtk_ui_manager_get_widget (parent_priv->ui_manager,
2201 gtk_toolbar_set_icon_size (GTK_TOOLBAR (parent_priv->toolbar), HILDON_ICON_SIZE_FINGER);
2202 gtk_widget_set_no_show_all (parent_priv->toolbar, TRUE);
2204 priv->next_toolitem = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/ToolbarMessageNext");
2205 priv->prev_toolitem = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/ToolbarMessageBack");
2206 toolbar_resize (MODEST_MSG_VIEW_WINDOW (self));
2209 hildon_window_add_toolbar (HILDON_WINDOW (self),
2210 GTK_TOOLBAR (parent_priv->toolbar));
2215 /* Quick hack: this prevents toolbar icons "dance" when progress bar show status is changed */
2216 /* TODO: resize mode migth be GTK_RESIZE_QUEUE, in order to avoid unneccesary shows */
2217 gtk_container_set_resize_mode (GTK_CONTAINER(parent_priv->toolbar), GTK_RESIZE_IMMEDIATE);
2219 gtk_widget_show (GTK_WIDGET (parent_priv->toolbar));
2220 if (modest_msg_view_window_transfer_mode_enabled (MODEST_MSG_VIEW_WINDOW (self)))
2221 set_progress_hint (MODEST_MSG_VIEW_WINDOW (self), TRUE);
2223 set_progress_hint (MODEST_MSG_VIEW_WINDOW (self), FALSE);
2226 gtk_widget_set_no_show_all (parent_priv->toolbar, TRUE);
2227 gtk_widget_hide (GTK_WIDGET (parent_priv->toolbar));
2232 modest_msg_view_window_clipboard_owner_change (GtkClipboard *clipboard,
2234 ModestMsgViewWindow *window)
2236 if (!GTK_WIDGET_VISIBLE (window))
2239 modest_window_check_dimming_rules_group (MODEST_WINDOW (window), MODEST_DIMMING_RULES_CLIPBOARD);
2243 modest_msg_view_window_transfer_mode_enabled (ModestMsgViewWindow *self)
2245 ModestMsgViewWindowPrivate *priv;
2247 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self), FALSE);
2248 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(self);
2250 return priv->progress_hint;
2254 observers_empty (ModestMsgViewWindow *self)
2257 ModestMsgViewWindowPrivate *priv;
2258 gboolean is_empty = TRUE;
2259 guint pending_ops = 0;
2261 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(self);
2262 tmp = priv->progress_widgets;
2264 /* Check all observers */
2265 while (tmp && is_empty) {
2266 pending_ops = modest_progress_object_num_pending_operations (MODEST_PROGRESS_OBJECT(tmp->data));
2267 is_empty = pending_ops == 0;
2269 tmp = g_slist_next(tmp);
2276 on_account_removed (TnyAccountStore *account_store,
2277 TnyAccount *account,
2280 /* Do nothing if it's a transport account, because we only
2281 show the messages of a store account */
2282 if (tny_account_get_account_type(account) == TNY_ACCOUNT_TYPE_STORE) {
2283 const gchar *parent_acc = NULL;
2284 const gchar *our_acc = NULL;
2286 our_acc = modest_window_get_active_account (MODEST_WINDOW (user_data));
2287 parent_acc = modest_tny_account_get_parent_modest_account_name_for_server_account (account);
2289 /* Close this window if I'm showing a message of the removed account */
2290 if (our_acc && parent_acc && strcmp (parent_acc, our_acc) == 0)
2291 modest_ui_actions_on_close_window (NULL, MODEST_WINDOW (user_data));
2296 on_mail_operation_started (ModestMailOperation *mail_op,
2299 ModestMsgViewWindow *self;
2300 ModestMailOperationTypeOperation op_type;
2302 ModestMsgViewWindowPrivate *priv;
2303 GObject *source = NULL;
2305 self = MODEST_MSG_VIEW_WINDOW (user_data);
2306 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
2307 op_type = modest_mail_operation_get_type_operation (mail_op);
2308 tmp = priv->progress_widgets;
2309 source = modest_mail_operation_get_source(mail_op);
2310 if (G_OBJECT (self) == source) {
2311 if (op_type == MODEST_MAIL_OPERATION_TYPE_RECEIVE) {
2312 set_toolbar_transfer_mode(self);
2314 modest_progress_object_add_operation (
2315 MODEST_PROGRESS_OBJECT (tmp->data),
2317 tmp = g_slist_next (tmp);
2321 g_object_unref (source);
2325 on_mail_operation_finished (ModestMailOperation *mail_op,
2328 ModestMsgViewWindow *self;
2329 ModestMailOperationTypeOperation op_type;
2331 ModestMsgViewWindowPrivate *priv;
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;
2338 if (op_type == MODEST_MAIL_OPERATION_TYPE_RECEIVE) {
2340 modest_progress_object_remove_operation (MODEST_PROGRESS_OBJECT (tmp->data),
2342 tmp = g_slist_next (tmp);
2345 /* If no more operations are being observed, NORMAL mode is enabled again */
2346 if (observers_empty (self)) {
2347 set_progress_hint (self, FALSE);
2351 /* Update dimming rules. We have to do this right here
2352 and not in view_msg_cb because at that point the
2353 transfer mode is still enabled so the dimming rule
2354 won't let the user delete the message that has been
2355 readed for example */
2356 check_dimming_rules_after_change (self);
2361 on_queue_changed (ModestMailOperationQueue *queue,
2362 ModestMailOperation *mail_op,
2363 ModestMailOperationQueueNotification type,
2364 ModestMsgViewWindow *self)
2366 ModestMsgViewWindowPrivate *priv;
2368 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
2370 /* If this operations was created by another window, do nothing */
2371 if (!modest_mail_operation_is_mine (mail_op, G_OBJECT(self)))
2374 if (type == MODEST_MAIL_OPERATION_QUEUE_OPERATION_ADDED) {
2375 priv->sighandlers = modest_signal_mgr_connect (priv->sighandlers,
2377 "operation-started",
2378 G_CALLBACK (on_mail_operation_started),
2380 priv->sighandlers = modest_signal_mgr_connect (priv->sighandlers,
2382 "operation-finished",
2383 G_CALLBACK (on_mail_operation_finished),
2385 } else if (type == MODEST_MAIL_OPERATION_QUEUE_OPERATION_REMOVED) {
2386 priv->sighandlers = modest_signal_mgr_disconnect (priv->sighandlers,
2388 "operation-started");
2389 priv->sighandlers = modest_signal_mgr_disconnect (priv->sighandlers,
2391 "operation-finished");
2396 modest_msg_view_window_get_attachments (ModestMsgViewWindow *win)
2398 ModestMsgViewWindowPrivate *priv;
2399 TnyList *selected_attachments = NULL;
2401 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (win), NULL);
2402 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (win);
2404 /* In Hildon 2.2 as there's no selection we assume we have all attachments selected */
2405 selected_attachments = modest_msg_view_get_attachments (MODEST_MSG_VIEW (priv->msg_view));
2407 return selected_attachments;
2411 ModestMsgViewWindow *self;
2413 } DecodeAsyncHelper;
2416 on_decode_to_stream_async_handler (TnyMimePart *mime_part,
2422 DecodeAsyncHelper *helper = (DecodeAsyncHelper *) user_data;
2424 /* It could happen that the window was closed */
2425 if (GTK_WIDGET_VISIBLE (helper->self))
2426 set_progress_hint (helper->self, FALSE);
2428 if (cancelled || err) {
2430 gchar *msg = g_strdup_printf (_KR("cerm_device_memory_full"), "");
2431 modest_platform_information_banner (NULL, NULL, msg);
2437 /* make the file read-only */
2438 g_chmod(helper->file_path, 0444);
2440 /* Activate the file */
2441 modest_platform_activate_file (helper->file_path, tny_mime_part_get_content_type (mime_part));
2445 g_object_unref (helper->self);
2446 g_free (helper->file_path);
2447 g_slice_free (DecodeAsyncHelper, helper);
2451 modest_msg_view_window_view_attachment (ModestMsgViewWindow *window,
2452 TnyMimePart *mime_part)
2454 ModestMsgViewWindowPrivate *priv;
2455 const gchar *msg_uid;
2456 gchar *attachment_uid = NULL;
2457 gint attachment_index = 0;
2458 TnyList *attachments;
2460 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window));
2461 g_return_if_fail (TNY_IS_MIME_PART (mime_part) || (mime_part == NULL));
2462 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
2464 msg_uid = modest_msg_view_window_get_message_uid (MODEST_MSG_VIEW_WINDOW (window));
2465 attachments = modest_msg_view_get_attachments (MODEST_MSG_VIEW (priv->msg_view));
2466 attachment_index = modest_list_index (attachments, (GObject *) mime_part);
2467 g_object_unref (attachments);
2469 if (msg_uid && attachment_index >= 0) {
2470 attachment_uid = g_strdup_printf ("%s/%d", msg_uid, attachment_index);
2473 if (mime_part == NULL) {
2474 gboolean error = FALSE;
2475 TnyList *selected_attachments = modest_msg_view_get_selected_attachments (MODEST_MSG_VIEW (priv->msg_view));
2476 if (selected_attachments == NULL || tny_list_get_length (selected_attachments) == 0) {
2478 } else if (tny_list_get_length (selected_attachments) > 1) {
2479 hildon_banner_show_information (NULL, NULL, _("mcen_ib_unable_to_display_more"));
2483 iter = tny_list_create_iterator (selected_attachments);
2484 mime_part = (TnyMimePart *) tny_iterator_get_current (iter);
2485 g_object_unref (iter);
2487 if (selected_attachments)
2488 g_object_unref (selected_attachments);
2493 g_object_ref (mime_part);
2496 if (tny_mime_part_is_purged (mime_part))
2499 if (!modest_tny_mime_part_is_msg (mime_part)) {
2500 gchar *filepath = NULL;
2501 const gchar *att_filename = tny_mime_part_get_filename (mime_part);
2502 gboolean show_error_banner = FALSE;
2503 TnyFsStream *temp_stream = NULL;
2504 temp_stream = modest_utils_create_temp_stream (att_filename, attachment_uid,
2507 if (temp_stream != NULL) {
2508 DecodeAsyncHelper *helper;
2510 /* Activate progress hint */
2511 set_progress_hint (window, TRUE);
2513 helper = g_slice_new0 (DecodeAsyncHelper);
2514 helper->self = g_object_ref (window);
2515 helper->file_path = g_strdup (filepath);
2517 tny_mime_part_decode_to_stream_async (mime_part, TNY_STREAM (temp_stream),
2518 on_decode_to_stream_async_handler,
2521 g_object_unref (temp_stream);
2522 /* NOTE: files in the temporary area will be automatically
2523 * cleaned after some time if they are no longer in use */
2526 const gchar *content_type;
2527 /* the file may already exist but it isn't writable,
2528 * let's try to open it anyway */
2529 content_type = tny_mime_part_get_content_type (mime_part);
2530 modest_platform_activate_file (filepath, content_type);
2532 g_warning ("%s: modest_utils_create_temp_stream failed", __FUNCTION__);
2533 show_error_banner = TRUE;
2538 if (show_error_banner)
2539 modest_platform_information_banner (NULL, NULL, _("mail_ib_file_operation_failed"));
2541 /* message attachment */
2542 TnyHeader *header = NULL;
2543 ModestWindowMgr *mgr;
2544 ModestWindow *msg_win = NULL;
2547 header = tny_msg_get_header (TNY_MSG (mime_part));
2548 mgr = modest_runtime_get_window_mgr ();
2549 found = modest_window_mgr_find_registered_header (mgr, header, &msg_win);
2552 /* if it's found, but there is no msg_win, it's probably in the process of being created;
2553 * thus, we don't do anything */
2554 g_warning ("window for is already being created");
2556 /* it's not found, so create a new window for it */
2557 modest_window_mgr_register_header (mgr, header, attachment_uid); /* register the uid before building the window */
2558 gchar *account = g_strdup (modest_window_get_active_account (MODEST_WINDOW (window)));
2559 const gchar *mailbox = modest_window_get_active_mailbox (MODEST_WINDOW (window));
2561 account = modest_account_mgr_get_default_account (modest_runtime_get_account_mgr ());
2562 msg_win = modest_msg_view_window_new_for_attachment (TNY_MSG (mime_part), account,
2563 mailbox, attachment_uid);
2564 modest_window_set_zoom (MODEST_WINDOW (msg_win),
2565 modest_window_get_zoom (MODEST_WINDOW (window)));
2566 if (modest_window_mgr_register_window (mgr, msg_win, MODEST_WINDOW (window)))
2567 gtk_widget_show_all (GTK_WIDGET (msg_win));
2569 gtk_widget_destroy (GTK_WIDGET (msg_win));
2575 g_free (attachment_uid);
2577 g_object_unref (mime_part);
2589 GnomeVFSResult result;
2592 static void save_mime_part_info_free (SaveMimePartInfo *info, gboolean with_struct);
2593 static gboolean idle_save_mime_part_show_result (SaveMimePartInfo *info);
2594 static gpointer save_mime_part_to_file (SaveMimePartInfo *info);
2595 static void save_mime_parts_to_file_with_checks (SaveMimePartInfo *info);
2598 save_mime_part_info_free (SaveMimePartInfo *info, gboolean with_struct)
2602 for (node = info->pairs; node != NULL; node = g_list_next (node)) {
2603 SaveMimePartPair *pair = (SaveMimePartPair *) node->data;
2604 g_free (pair->filename);
2605 g_object_unref (pair->part);
2606 g_slice_free (SaveMimePartPair, pair);
2608 g_list_free (info->pairs);
2611 g_slice_free (SaveMimePartInfo, info);
2616 idle_save_mime_part_show_result (SaveMimePartInfo *info)
2618 if (info->pairs != NULL) {
2619 save_mime_part_to_file (info);
2621 /* This is a GDK lock because we are an idle callback and
2622 * hildon_banner_show_information is or does Gtk+ code */
2624 gdk_threads_enter (); /* CHECKED */
2625 save_mime_part_info_free (info, TRUE);
2626 if (info->result == GNOME_VFS_OK) {
2627 hildon_banner_show_information (NULL, NULL, _CS("sfil_ib_saved"));
2628 } else if (info->result == GNOME_VFS_ERROR_NO_SPACE) {
2629 gchar *msg = g_strdup_printf (_KR("cerm_device_memory_full"), "");
2630 modest_platform_information_banner (NULL, NULL, msg);
2633 hildon_banner_show_information (NULL, NULL, _("mail_ib_file_operation_failed"));
2635 gdk_threads_leave (); /* CHECKED */
2642 save_mime_part_to_file (SaveMimePartInfo *info)
2644 GnomeVFSHandle *handle;
2646 SaveMimePartPair *pair = (SaveMimePartPair *) info->pairs->data;
2648 info->result = gnome_vfs_create (&handle, pair->filename, GNOME_VFS_OPEN_WRITE, FALSE, 0644);
2649 if (info->result == GNOME_VFS_OK) {
2650 GError *error = NULL;
2651 stream = tny_vfs_stream_new (handle);
2652 if (tny_mime_part_decode_to_stream (pair->part, stream, &error) < 0) {
2653 g_warning ("modest: could not save attachment %s: %d (%s)\n", pair->filename, error?error->code:-1, error?error->message:"Unknown error");
2655 if ((error->domain == TNY_ERROR_DOMAIN) &&
2656 (error->code == TNY_IO_ERROR_WRITE) &&
2657 (errno == ENOSPC)) {
2658 info->result = GNOME_VFS_ERROR_NO_SPACE;
2660 info->result = GNOME_VFS_ERROR_IO;
2663 g_object_unref (G_OBJECT (stream));
2664 g_object_unref (pair->part);
2665 g_slice_free (SaveMimePartPair, pair);
2666 info->pairs = g_list_delete_link (info->pairs, info->pairs);
2668 g_warning ("modest: could not create save attachment %s: %s\n", pair->filename, gnome_vfs_result_to_string (info->result));
2669 save_mime_part_info_free (info, FALSE);
2672 g_idle_add ((GSourceFunc) idle_save_mime_part_show_result, info);
2677 save_mime_parts_to_file_with_checks (SaveMimePartInfo *info)
2679 gboolean is_ok = TRUE;
2680 gint replaced_files = 0;
2681 const GList *files = info->pairs;
2684 for (iter = files; (iter != NULL) && (replaced_files < 2); iter = g_list_next(iter)) {
2685 SaveMimePartPair *pair = iter->data;
2686 if (modest_utils_file_exists (pair->filename)) {
2690 if (replaced_files) {
2691 GtkWidget *confirm_overwrite_dialog;
2693 if (replaced_files == 1) {
2694 SaveMimePartPair *pair = files->data;
2695 const gchar *basename = strrchr (pair->filename, G_DIR_SEPARATOR) + 1;
2697 gchar *message = g_strdup_printf ("%s\n%s",
2698 _FM("docm_nc_replace_file"),
2699 (basename) ? basename : "");
2700 confirm_overwrite_dialog = hildon_note_new_confirmation (NULL, message);
2703 confirm_overwrite_dialog = hildon_note_new_confirmation (NULL,
2704 _FM("docm_nc_replace_multiple"));
2706 if (gtk_dialog_run (GTK_DIALOG (confirm_overwrite_dialog)) != GTK_RESPONSE_OK)
2709 gtk_widget_destroy (confirm_overwrite_dialog);
2713 save_mime_part_info_free (info, TRUE);
2715 g_thread_create ((GThreadFunc)save_mime_part_to_file, info, FALSE, NULL);
2721 save_attachments_response (GtkDialog *dialog,
2725 TnyList *mime_parts;
2727 GList *files_to_save = NULL;
2728 gchar *current_folder;
2730 mime_parts = TNY_LIST (user_data);
2732 if (arg1 != GTK_RESPONSE_OK)
2735 chooser_uri = gtk_file_chooser_get_uri (GTK_FILE_CHOOSER (dialog));
2736 current_folder = gtk_file_chooser_get_current_folder_uri (GTK_FILE_CHOOSER (dialog));
2737 if (current_folder && current_folder != '\0') {
2739 modest_conf_set_string (modest_runtime_get_conf (), MODEST_CONF_LATEST_SAVE_ATTACHMENT_PATH,
2740 current_folder,&err);
2742 g_debug ("Error storing latest used folder: %s", err->message);
2746 g_free (current_folder);
2748 if (!modest_utils_folder_writable (chooser_uri)) {
2749 hildon_banner_show_information
2750 (NULL, NULL, _FM("sfil_ib_readonly_location"));
2754 iter = tny_list_create_iterator (mime_parts);
2755 while (!tny_iterator_is_done (iter)) {
2756 TnyMimePart *mime_part = (TnyMimePart *) tny_iterator_get_current (iter);
2758 if ((modest_tny_mime_part_is_attachment_for_modest (mime_part)) &&
2759 !tny_mime_part_is_purged (mime_part) &&
2760 (tny_mime_part_get_filename (mime_part) != NULL)) {
2761 SaveMimePartPair *pair;
2763 pair = g_slice_new0 (SaveMimePartPair);
2765 if (tny_list_get_length (mime_parts) > 1) {
2767 gnome_vfs_escape_slashes (tny_mime_part_get_filename (mime_part));
2768 pair->filename = g_build_filename (chooser_uri, escaped, NULL);
2771 pair->filename = g_strdup (chooser_uri);
2773 pair->part = mime_part;
2774 files_to_save = g_list_prepend (files_to_save, pair);
2776 tny_iterator_next (iter);
2778 g_object_unref (iter);
2780 g_free (chooser_uri);
2782 if (files_to_save != NULL) {
2783 SaveMimePartInfo *info = g_slice_new0 (SaveMimePartInfo);
2784 info->pairs = files_to_save;
2785 info->result = TRUE;
2786 save_mime_parts_to_file_with_checks (info);
2790 /* Free and close the dialog */
2791 g_object_unref (mime_parts);
2792 gtk_widget_destroy (GTK_WIDGET (dialog));
2796 modest_msg_view_window_save_attachments (ModestMsgViewWindow *window,
2797 TnyList *mime_parts)
2799 ModestMsgViewWindowPrivate *priv;
2800 GtkWidget *save_dialog = NULL;
2801 gchar *conf_folder = NULL;
2802 gchar *filename = NULL;
2803 gchar *save_multiple_str = NULL;
2805 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window));
2806 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
2808 if (mime_parts == NULL) {
2809 /* In Hildon 2.2 save and delete operate over all the attachments as there's no
2810 * selection available */
2811 mime_parts = modest_msg_view_get_attachments (MODEST_MSG_VIEW (priv->msg_view));
2812 if (mime_parts && !modest_maemo_utils_select_attachments (GTK_WINDOW (window), mime_parts, FALSE)) {
2813 g_object_unref (mime_parts);
2816 if (mime_parts == NULL || tny_list_get_length (mime_parts) == 0) {
2818 g_object_unref (mime_parts);
2824 g_object_ref (mime_parts);
2827 /* prepare dialog */
2828 if (tny_list_get_length (mime_parts) == 1) {
2830 /* only one attachment selected */
2831 iter = tny_list_create_iterator (mime_parts);
2832 TnyMimePart *mime_part = (TnyMimePart *) tny_iterator_get_current (iter);
2833 g_object_unref (iter);
2834 if (!modest_tny_mime_part_is_msg (mime_part) &&
2835 modest_tny_mime_part_is_attachment_for_modest (mime_part) &&
2836 !tny_mime_part_is_purged (mime_part)) {
2837 filename = g_strdup (tny_mime_part_get_filename (mime_part));
2839 /* TODO: show any error? */
2840 g_warning ("Tried to save a non-file attachment");
2841 g_object_unref (mime_parts);
2844 g_object_unref (mime_part);
2846 save_multiple_str = g_strdup_printf (_FM("sfil_va_number_of_objects_attachments"),
2847 tny_list_get_length (mime_parts));
2850 save_dialog = hildon_file_chooser_dialog_new (GTK_WINDOW (window),
2851 GTK_FILE_CHOOSER_ACTION_SAVE);
2854 conf_folder = modest_conf_get_string (modest_runtime_get_conf (), MODEST_CONF_LATEST_SAVE_ATTACHMENT_PATH, NULL);
2855 if (conf_folder && conf_folder[0] != '\0') {
2856 gtk_file_chooser_set_current_folder_uri (GTK_FILE_CHOOSER (save_dialog), conf_folder);
2859 /* Set the default folder to images folder */
2860 docs_folder = g_build_filename (g_getenv (MYDOCS_ENV), DOCS_FOLDER, NULL);
2861 gtk_file_chooser_set_current_folder (GTK_FILE_CHOOSER (save_dialog), docs_folder);
2862 g_free (docs_folder);
2864 g_free (conf_folder);
2868 gtk_file_chooser_set_current_name (GTK_FILE_CHOOSER (save_dialog),
2873 /* if multiple, set multiple string */
2874 if (save_multiple_str) {
2875 g_object_set (G_OBJECT (save_dialog), "save-multiple", save_multiple_str, NULL);
2876 gtk_window_set_title (GTK_WINDOW (save_dialog), _FM("sfil_ti_save_objects_files"));
2879 /* We must run this asynchronously, because the hildon dialog
2880 performs a gtk_dialog_run by itself which leads to gdk
2882 g_signal_connect (save_dialog, "response",
2883 G_CALLBACK (save_attachments_response), mime_parts);
2885 gtk_widget_show_all (save_dialog);
2889 show_remove_attachment_information (gpointer userdata)
2891 ModestMsgViewWindow *window = (ModestMsgViewWindow *) userdata;
2892 ModestMsgViewWindowPrivate *priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
2894 /* We're outside the main lock */
2895 gdk_threads_enter ();
2897 if (priv->remove_attachment_banner != NULL) {
2898 gtk_widget_destroy (priv->remove_attachment_banner);
2899 g_object_unref (priv->remove_attachment_banner);
2902 priv->remove_attachment_banner = g_object_ref (
2903 hildon_banner_show_animation (NULL, NULL, _("mcen_me_inbox_remove_attachments")));
2905 gdk_threads_leave ();
2911 modest_msg_view_window_remove_attachments (ModestMsgViewWindow *window, gboolean get_all)
2913 ModestMsgViewWindowPrivate *priv;
2914 TnyList *mime_parts = NULL, *tmp;
2915 gchar *confirmation_message;
2921 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window));
2922 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
2924 /* In hildon 2.2 we ignore the get_all flag as we always get all attachments. This is
2925 * because we don't have selection
2927 mime_parts = modest_msg_view_get_attachments (MODEST_MSG_VIEW (priv->msg_view));
2929 /* Remove already purged messages from mime parts list. We use
2930 a copy of the list to remove items in the original one */
2931 tmp = tny_list_copy (mime_parts);
2932 iter = tny_list_create_iterator (tmp);
2933 while (!tny_iterator_is_done (iter)) {
2934 TnyMimePart *part = TNY_MIME_PART (tny_iterator_get_current (iter));
2935 if (tny_mime_part_is_purged (part))
2936 tny_list_remove (mime_parts, (GObject *) part);
2938 g_object_unref (part);
2939 tny_iterator_next (iter);
2941 g_object_unref (tmp);
2942 g_object_unref (iter);
2944 if (!modest_maemo_utils_select_attachments (GTK_WINDOW (window), mime_parts, TRUE) ||
2945 tny_list_get_length (mime_parts) == 0) {
2946 g_object_unref (mime_parts);
2950 n_attachments = tny_list_get_length (mime_parts);
2951 if (n_attachments == 1) {
2955 iter = tny_list_create_iterator (mime_parts);
2956 part = (TnyMimePart *) tny_iterator_get_current (iter);
2957 g_object_unref (iter);
2958 if (modest_tny_mime_part_is_msg (part)) {
2960 header = tny_msg_get_header (TNY_MSG (part));
2961 filename = tny_header_dup_subject (header);
2962 g_object_unref (header);
2963 if (filename == NULL)
2964 filename = g_strdup (_("mail_va_no_subject"));
2966 filename = g_strdup (tny_mime_part_get_filename (TNY_MIME_PART (part)));
2968 confirmation_message = g_strdup_printf (_("mcen_nc_purge_file_text"), filename);
2970 g_object_unref (part);
2972 confirmation_message = g_strdup_printf (ngettext("mcen_nc_purge_file_text",
2973 "mcen_nc_purge_files_text",
2974 n_attachments), n_attachments);
2976 response = modest_platform_run_confirmation_dialog (GTK_WINDOW (window),
2977 confirmation_message);
2978 g_free (confirmation_message);
2980 if (response != GTK_RESPONSE_OK) {
2981 g_object_unref (mime_parts);
2985 priv->purge_timeout = g_timeout_add (2000, show_remove_attachment_information, window);
2987 iter = tny_list_create_iterator (mime_parts);
2988 while (!tny_iterator_is_done (iter)) {
2991 part = (TnyMimePart *) tny_iterator_get_current (iter);
2992 tny_mime_part_set_purged (TNY_MIME_PART (part));
2993 g_object_unref (part);
2994 tny_iterator_next (iter);
2996 g_object_unref (iter);
2998 msg = tny_msg_view_get_msg (TNY_MSG_VIEW (priv->msg_view));
2999 tny_msg_view_clear (TNY_MSG_VIEW (priv->msg_view));
3000 tny_msg_rewrite_cache (msg);
3001 tny_msg_view_set_msg (TNY_MSG_VIEW (priv->msg_view), msg);
3002 g_object_unref (msg);
3004 g_object_unref (mime_parts);
3006 if (priv->purge_timeout > 0) {
3007 g_source_remove (priv->purge_timeout);
3008 priv->purge_timeout = 0;
3011 if (priv->remove_attachment_banner) {
3012 gtk_widget_destroy (priv->remove_attachment_banner);
3013 g_object_unref (priv->remove_attachment_banner);
3014 priv->remove_attachment_banner = NULL;
3020 update_window_title (ModestMsgViewWindow *window)
3022 ModestMsgViewWindowPrivate *priv;
3024 TnyHeader *header = NULL;
3025 gchar *subject = NULL;
3027 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
3029 /* Note that if the window is closed while we're retrieving
3030 the message, this widget could de deleted */
3031 if (!priv->msg_view)
3034 msg = tny_msg_view_get_msg (TNY_MSG_VIEW (priv->msg_view));
3037 header = tny_msg_get_header (msg);
3038 subject = tny_header_dup_subject (header);
3039 g_object_unref (header);
3040 g_object_unref (msg);
3043 if ((subject == NULL)||(subject[0] == '\0')) {
3045 subject = g_strdup (_("mail_va_no_subject"));
3048 gtk_window_set_title (GTK_WINDOW (window), subject);
3053 on_move_focus (GtkWidget *widget,
3054 GtkDirectionType direction,
3057 g_signal_stop_emission_by_name (G_OBJECT (widget), "move-focus");
3061 fetch_image_open_stream (TnyStreamCache *self, gint64 *expected_size, gchar *uri)
3063 GnomeVFSResult result;
3064 GnomeVFSHandle *handle = NULL;
3065 GnomeVFSFileInfo *info = NULL;
3068 result = gnome_vfs_open (&handle, uri, GNOME_VFS_OPEN_READ);
3069 if (result != GNOME_VFS_OK) {
3074 info = gnome_vfs_file_info_new ();
3075 result = gnome_vfs_get_file_info_from_handle (handle, info, GNOME_VFS_FILE_INFO_DEFAULT);
3076 if (result != GNOME_VFS_OK || ! (info->valid_fields & GNOME_VFS_FILE_INFO_FIELDS_SIZE)) {
3077 /* We put a "safe" default size for going to cache */
3078 *expected_size = (300*1024);
3080 *expected_size = info->size;
3082 gnome_vfs_file_info_unref (info);
3084 stream = tny_vfs_stream_new (handle);
3093 TnyStream *output_stream;
3094 GtkWidget *msg_view;
3099 on_fetch_image_idle_refresh_view (gpointer userdata)
3102 FetchImageData *fidata = (FetchImageData *) userdata;
3104 gdk_threads_enter ();
3105 if (GTK_WIDGET_DRAWABLE (fidata->msg_view)) {
3106 ModestMsgViewWindowPrivate *priv;
3108 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (fidata->window);
3109 priv->fetching_images--;
3110 gtk_widget_queue_draw (fidata->msg_view);
3111 update_progress_hint (MODEST_MSG_VIEW_WINDOW (fidata->window));
3113 gdk_threads_leave ();
3115 g_object_unref (fidata->msg_view);
3116 g_object_unref (fidata->window);
3117 g_slice_free (FetchImageData, fidata);
3122 on_fetch_image_thread (gpointer userdata)
3124 FetchImageData *fidata = (FetchImageData *) userdata;
3125 TnyStreamCache *cache;
3126 TnyStream *cache_stream;
3128 cache = modest_runtime_get_images_cache ();
3130 tny_stream_cache_get_stream (cache,
3132 (TnyStreamCacheOpenStreamFetcher) fetch_image_open_stream,
3133 (gpointer) fidata->uri);
3134 g_free (fidata->cache_id);
3135 g_free (fidata->uri);
3137 if (cache_stream != NULL) {
3140 while (G_LIKELY (!tny_stream_is_eos (cache_stream))) {
3143 nb_read = tny_stream_read (cache_stream, buffer, sizeof (buffer));
3144 if (G_UNLIKELY (nb_read < 0)) {
3146 } else if (G_LIKELY (nb_read > 0)) {
3147 gssize nb_written = 0;
3149 while (G_UNLIKELY (nb_written < nb_read)) {
3152 len = tny_stream_write (fidata->output_stream, buffer + nb_written,
3153 nb_read - nb_written);
3154 if (G_UNLIKELY (len < 0))
3160 tny_stream_close (cache_stream);
3161 g_object_unref (cache_stream);
3164 tny_stream_close (fidata->output_stream);
3165 g_object_unref (fidata->output_stream);
3167 g_idle_add (on_fetch_image_idle_refresh_view, fidata);
3173 on_fetch_image (ModestMsgView *msgview,
3176 ModestMsgViewWindow *window)
3178 const gchar *current_account;
3179 ModestMsgViewWindowPrivate *priv;
3180 FetchImageData *fidata;
3182 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
3184 current_account = modest_window_get_active_account (MODEST_WINDOW (window));
3186 fidata = g_slice_new0 (FetchImageData);
3187 fidata->msg_view = g_object_ref (msgview);
3188 fidata->window = g_object_ref (window);
3189 fidata->uri = g_strdup (uri);
3190 fidata->cache_id = modest_images_cache_get_id (current_account, uri);
3191 fidata->output_stream = g_object_ref (stream);
3193 priv->fetching_images++;
3194 if (g_thread_create (on_fetch_image_thread, fidata, FALSE, NULL) == NULL) {
3195 g_object_unref (fidata->output_stream);
3196 g_free (fidata->cache_id);
3197 g_free (fidata->uri);
3198 g_object_unref (fidata->msg_view);
3199 g_slice_free (FetchImageData, fidata);
3200 tny_stream_close (stream);
3201 priv->fetching_images--;
3202 update_progress_hint (window);
3205 update_progress_hint (window);
3211 setup_menu (ModestMsgViewWindow *self)
3213 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW(self));
3215 /* Settings menu buttons */
3216 modest_hildon2_window_add_to_menu (MODEST_HILDON2_WINDOW (self), _("mcen_me_inbox_replytoall"), NULL,
3217 APP_MENU_CALLBACK (modest_ui_actions_on_reply_all),
3218 MODEST_DIMMING_CALLBACK (modest_ui_dimming_rules_on_reply_msg));
3219 modest_hildon2_window_add_to_menu (MODEST_HILDON2_WINDOW (self), _("mcen_me_inbox_forward"), "<Control>d",
3220 APP_MENU_CALLBACK (modest_ui_actions_on_forward),
3221 MODEST_DIMMING_CALLBACK (modest_ui_dimming_rules_on_reply_msg));
3223 modest_hildon2_window_add_to_menu (MODEST_HILDON2_WINDOW (self), _("mcen_me_inbox_mark_as_read"), NULL,
3224 APP_MENU_CALLBACK (modest_ui_actions_on_mark_as_read),
3225 MODEST_DIMMING_CALLBACK (modest_ui_dimming_rules_on_mark_as_read_msg_in_view));
3226 modest_hildon2_window_add_to_menu (MODEST_HILDON2_WINDOW (self), _("mcen_me_inbox_mark_as_unread"), NULL,
3227 APP_MENU_CALLBACK (modest_ui_actions_on_mark_as_unread),
3228 MODEST_DIMMING_CALLBACK (modest_ui_dimming_rules_on_mark_as_unread_msg_in_view));
3230 modest_hildon2_window_add_to_menu (MODEST_HILDON2_WINDOW (self), _("mcen_me_viewer_save_attachments"), NULL,
3231 APP_MENU_CALLBACK (modest_ui_actions_save_attachments),
3232 MODEST_DIMMING_CALLBACK (modest_ui_dimming_rules_on_save_attachments));
3233 modest_hildon2_window_add_to_menu (MODEST_HILDON2_WINDOW (self), _("mcen_me_inbox_remove_attachments"), NULL,
3234 APP_MENU_CALLBACK (modest_ui_actions_remove_attachments),
3235 MODEST_DIMMING_CALLBACK (modest_ui_dimming_rules_on_remove_attachments));
3237 modest_hildon2_window_add_to_menu (MODEST_HILDON2_WINDOW (self), _("mcen_me_new_message"), "<Control>n",
3238 APP_MENU_CALLBACK (modest_ui_actions_on_new_msg),
3239 MODEST_DIMMING_CALLBACK (modest_ui_dimming_rules_on_new_msg));
3240 modest_hildon2_window_add_to_menu (MODEST_HILDON2_WINDOW (self), _("mcen_me_viewer_addtocontacts"), NULL,
3241 APP_MENU_CALLBACK (modest_ui_actions_add_to_contacts),
3242 MODEST_DIMMING_CALLBACK (modest_ui_dimming_rules_on_add_to_contacts));
3244 modest_hildon2_window_add_to_menu (MODEST_HILDON2_WINDOW (self), _("mail_bd_external_images"), NULL,
3245 APP_MENU_CALLBACK (modest_ui_actions_on_fetch_images),
3246 MODEST_DIMMING_CALLBACK (modest_ui_dimming_rules_on_fetch_images));
3247 modest_hildon2_window_add_to_menu (MODEST_HILDON2_WINDOW (self), _("mcen_ti_message_properties"), NULL,
3248 APP_MENU_CALLBACK (modest_ui_actions_on_details),
3249 MODEST_DIMMING_CALLBACK (modest_ui_dimming_rules_on_details));
3253 modest_msg_view_window_add_to_contacts (ModestMsgViewWindow *self)
3255 ModestMsgViewWindowPrivate *priv;
3256 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
3257 GSList *recipients = NULL;
3259 gboolean contacts_to_add = FALSE;
3261 msg = tny_msg_view_get_msg (TNY_MSG_VIEW (priv->msg_view));
3265 header = modest_msg_view_window_get_header (self);
3268 recipients = modest_tny_msg_header_get_all_recipients_list (header);
3269 g_object_unref (header);
3271 recipients = modest_tny_msg_get_all_recipients_list (msg);
3272 g_object_unref (msg);
3275 if (recipients != NULL) {
3276 GtkWidget *picker_dialog;
3277 GtkWidget *selector;
3279 gchar *selected = NULL;
3281 selector = hildon_touch_selector_new_text ();
3282 g_object_ref (selector);
3284 for (node = recipients; node != NULL; node = g_slist_next (node)) {
3285 if (!modest_address_book_has_address ((const gchar *) node->data)) {
3286 hildon_touch_selector_append_text (HILDON_TOUCH_SELECTOR (selector),
3287 (const gchar *) node->data);
3288 contacts_to_add = TRUE;
3292 if (contacts_to_add) {
3295 picker_dialog = hildon_picker_dialog_new (GTK_WINDOW (self));
3296 gtk_window_set_title (GTK_WINDOW (picker_dialog), _("mcen_me_viewer_addtocontacts"));
3298 hildon_picker_dialog_set_selector (HILDON_PICKER_DIALOG (picker_dialog),
3299 HILDON_TOUCH_SELECTOR (selector));
3301 picker_result = gtk_dialog_run (GTK_DIALOG (picker_dialog));
3303 if (picker_result == GTK_RESPONSE_OK) {
3304 selected = hildon_touch_selector_get_current_text (HILDON_TOUCH_SELECTOR (selector));
3306 gtk_widget_destroy (picker_dialog);
3309 modest_address_book_add_address (selected);
3314 g_object_unref (selector);
3319 if (recipients) {g_slist_foreach (recipients, (GFunc) g_free, NULL); g_slist_free (recipients);}
3323 _modest_msg_view_window_map_event (GtkWidget *widget,
3327 ModestMsgViewWindow *self = (ModestMsgViewWindow *) userdata;
3329 update_progress_hint (self);
3335 modest_msg_view_window_fetch_images (ModestMsgViewWindow *self)
3337 ModestMsgViewWindowPrivate *priv;
3338 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
3340 modest_msg_view_request_fetch_images (MODEST_MSG_VIEW (priv->msg_view));
3344 modest_msg_view_window_has_blocked_external_images (ModestMsgViewWindow *self)
3346 ModestMsgViewWindowPrivate *priv;
3347 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
3349 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self), FALSE);
3351 return modest_msg_view_has_blocked_external_images (MODEST_MSG_VIEW (priv->msg_view));
3355 modest_msg_view_window_reload (ModestMsgViewWindow *self)
3357 ModestMsgViewWindowPrivate *priv;
3360 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self));
3362 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
3363 header = modest_msg_view_window_get_header (MODEST_MSG_VIEW_WINDOW (self));
3365 if (!message_reader (self, priv, header, priv->row_reference)) {
3366 g_warning ("Shouldn't happen, trying to reload a message failed");
3369 g_object_unref (header);