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);
997 gtk_tree_path_free (path);
999 /* Check dimming rules */
1000 modest_ui_actions_check_toolbar_dimming_rules (MODEST_WINDOW (window));
1001 modest_ui_actions_check_menu_dimming_rules (MODEST_WINDOW (window));
1002 modest_window_check_dimming_rules_group (MODEST_WINDOW (window), MODEST_DIMMING_RULES_CLIPBOARD);
1004 return MODEST_WINDOW(window);
1008 modest_msg_view_window_new_for_search_result (TnyMsg *msg,
1009 const gchar *modest_account_name,
1010 const gchar *mailbox,
1011 const gchar *msg_uid)
1013 ModestMsgViewWindow *window = NULL;
1014 ModestMsgViewWindowPrivate *priv = NULL;
1015 ModestWindowMgr *mgr = NULL;
1017 mgr = modest_runtime_get_window_mgr ();
1018 window = MODEST_MSG_VIEW_WINDOW (modest_window_mgr_get_msg_view_window (mgr));
1019 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), NULL);
1020 modest_msg_view_window_construct (window, modest_account_name, mailbox, msg_uid);
1022 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1024 /* Remember that this is a search result,
1025 * so we can disable some UI appropriately: */
1026 priv->is_search_result = TRUE;
1028 tny_msg_view_set_msg (TNY_MSG_VIEW (priv->msg_view), msg);
1030 update_window_title (window);
1031 /* gtk_widget_show_all (GTK_WIDGET (window));*/
1032 modest_msg_view_window_update_priority (window);
1034 /* Check dimming rules */
1035 modest_ui_actions_check_toolbar_dimming_rules (MODEST_WINDOW (window));
1036 modest_ui_actions_check_menu_dimming_rules (MODEST_WINDOW (window));
1037 modest_window_check_dimming_rules_group (MODEST_WINDOW (window), MODEST_DIMMING_RULES_CLIPBOARD);
1039 return MODEST_WINDOW(window);
1043 modest_msg_view_window_new_for_attachment (TnyMsg *msg,
1044 const gchar *modest_account_name,
1045 const gchar *mailbox,
1046 const gchar *msg_uid)
1048 GObject *obj = NULL;
1049 ModestMsgViewWindowPrivate *priv;
1050 ModestWindowMgr *mgr = NULL;
1052 g_return_val_if_fail (msg, NULL);
1053 mgr = modest_runtime_get_window_mgr ();
1054 obj = G_OBJECT (modest_window_mgr_get_msg_view_window (mgr));
1055 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (obj);
1056 modest_msg_view_window_construct (MODEST_MSG_VIEW_WINDOW (obj),
1057 modest_account_name, mailbox, msg_uid);
1059 tny_msg_view_set_msg (TNY_MSG_VIEW (priv->msg_view), msg);
1060 update_window_title (MODEST_MSG_VIEW_WINDOW (obj));
1062 /* gtk_widget_show_all (GTK_WIDGET (obj)); */
1064 /* Check dimming rules */
1065 modest_ui_actions_check_toolbar_dimming_rules (MODEST_WINDOW (obj));
1066 modest_ui_actions_check_menu_dimming_rules (MODEST_WINDOW (obj));
1067 modest_window_check_dimming_rules_group (MODEST_WINDOW (obj), MODEST_DIMMING_RULES_CLIPBOARD);
1069 return MODEST_WINDOW(obj);
1073 modest_msg_view_window_on_row_changed (GtkTreeModel *header_model,
1076 ModestMsgViewWindow *window)
1078 check_dimming_rules_after_change (window);
1082 modest_msg_view_window_on_row_deleted(GtkTreeModel *header_model,
1084 ModestMsgViewWindow *window)
1086 check_dimming_rules_after_change (window);
1088 /* The window could have dissapeared */
1091 check_dimming_rules_after_change (ModestMsgViewWindow *window)
1093 modest_ui_actions_check_menu_dimming_rules (MODEST_WINDOW (window));
1094 modest_ui_actions_check_toolbar_dimming_rules (MODEST_WINDOW (window));
1098 /* On insertions we check if the folder still has the message we are
1099 * showing or do not. If do not, we do nothing. Which means we are still
1100 * not attached to any header folder and thus next/prev buttons are
1101 * still dimmed. Once the message that is shown by msg-view is found, the
1102 * new model of header-view will be attached and the references will be set.
1103 * On each further insertions dimming rules will be checked. However
1104 * this requires extra CPU time at least works.
1105 * (An message might be deleted from TnyFolder and thus will not be
1106 * inserted into the model again for example if it is removed by the
1107 * imap server and the header view is refreshed.)
1110 modest_msg_view_window_on_row_inserted (GtkTreeModel *model,
1111 GtkTreePath *tree_path,
1112 GtkTreeIter *tree_iter,
1113 ModestMsgViewWindow *window)
1115 ModestMsgViewWindowPrivate *priv = NULL;
1116 TnyHeader *header = NULL;
1118 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window));
1119 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1121 g_assert (model == priv->header_model);
1123 /* Check if the newly inserted message is the same we are actually
1124 * showing. IF not, we should remain detached from the header model
1125 * and thus prev and next toolbar buttons should remain dimmed. */
1126 gtk_tree_model_get (model, tree_iter,
1127 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
1130 if (TNY_IS_HEADER (header)) {
1133 uid = modest_tny_folder_get_header_unique_id (header);
1134 if (!g_str_equal(priv->msg_uid, uid)) {
1135 check_dimming_rules_after_change (window);
1137 g_object_unref (G_OBJECT(header));
1141 g_object_unref(G_OBJECT(header));
1144 if (priv->row_reference) {
1145 gtk_tree_row_reference_free (priv->row_reference);
1148 /* Setup row_reference for the actual msg. */
1149 priv->row_reference = gtk_tree_row_reference_new (priv->header_model, tree_path);
1150 if (priv->row_reference == NULL) {
1151 g_warning("No reference for msg header item.");
1155 /* Now set up next_row_reference. */
1156 if (priv->next_row_reference) {
1157 gtk_tree_row_reference_free (priv->next_row_reference);
1160 priv->next_row_reference = gtk_tree_row_reference_copy (priv->row_reference);
1161 select_next_valid_row (priv->header_model,
1162 &(priv->next_row_reference), FALSE, priv->is_outbox);
1164 /* Connect the remaining callbacks to become able to detect
1165 * changes in header-view. */
1166 priv->row_changed_handler =
1167 g_signal_connect (priv->header_model, "row-changed",
1168 G_CALLBACK (modest_msg_view_window_on_row_changed),
1170 priv->row_deleted_handler =
1171 g_signal_connect (priv->header_model, "row-deleted",
1172 G_CALLBACK (modest_msg_view_window_on_row_deleted),
1174 priv->rows_reordered_handler =
1175 g_signal_connect (priv->header_model, "rows-reordered",
1176 G_CALLBACK (modest_msg_view_window_on_row_reordered),
1179 check_dimming_rules_after_change (window);
1183 modest_msg_view_window_on_row_reordered (GtkTreeModel *header_model,
1187 ModestMsgViewWindow *window)
1189 ModestMsgViewWindowPrivate *priv = NULL;
1190 gboolean already_changed = FALSE;
1192 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(window);
1194 /* If the current row was reordered select the proper next
1195 valid row. The same if the next row reference changes */
1196 if (priv->row_reference &&
1197 gtk_tree_row_reference_valid (priv->row_reference)) {
1199 path = gtk_tree_row_reference_get_path (priv->row_reference);
1200 if (gtk_tree_path_compare (path, arg1) == 0) {
1201 if (priv->next_row_reference) {
1202 gtk_tree_row_reference_free (priv->next_row_reference);
1204 priv->next_row_reference = gtk_tree_row_reference_copy (priv->row_reference);
1205 select_next_valid_row (header_model, &(priv->next_row_reference), FALSE, priv->is_outbox);
1206 already_changed = TRUE;
1208 gtk_tree_path_free (path);
1210 if (!already_changed &&
1211 priv->next_row_reference &&
1212 gtk_tree_row_reference_valid (priv->next_row_reference)) {
1214 path = gtk_tree_row_reference_get_path (priv->next_row_reference);
1215 if (gtk_tree_path_compare (path, arg1) == 0) {
1216 if (priv->next_row_reference) {
1217 gtk_tree_row_reference_free (priv->next_row_reference);
1219 priv->next_row_reference = gtk_tree_row_reference_copy (priv->row_reference);
1220 select_next_valid_row (header_model, &(priv->next_row_reference), FALSE, priv->is_outbox);
1222 gtk_tree_path_free (path);
1224 check_dimming_rules_after_change (window);
1227 /* The modest_msg_view_window_update_model_replaced implements update
1228 * function for ModestHeaderViewObserver. Checks whether the TnyFolder
1229 * actually belongs to the header-view is the same as the TnyFolder of
1230 * the message of msg-view or not. If they are different, there is
1231 * nothing to do. If they are the same, then the model has replaced and
1232 * the reference in msg-view shall be replaced from the old model to
1233 * the new model. In this case the view will be detached from it's
1234 * header folder. From this point the next/prev buttons are dimmed.
1237 modest_msg_view_window_update_model_replaced (ModestHeaderViewObserver *observer,
1238 GtkTreeModel *model,
1239 const gchar *tny_folder_id)
1241 ModestMsgViewWindowPrivate *priv = NULL;
1242 ModestMsgViewWindow *window = NULL;
1244 g_assert(MODEST_IS_HEADER_VIEW_OBSERVER(observer));
1245 g_assert(MODEST_IS_MSG_VIEW_WINDOW(observer));
1247 window = MODEST_MSG_VIEW_WINDOW(observer);
1248 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(window);
1250 /* If there is an other folder in the header-view then we do
1251 * not care about it's model (msg list). Else if the
1252 * header-view shows the folder the msg shown by us is in, we
1253 * shall replace our model reference and make some check. */
1254 if(model == NULL || tny_folder_id == NULL ||
1255 (priv->header_folder_id && !g_str_equal(tny_folder_id, priv->header_folder_id)))
1258 /* Model is changed(replaced), so we should forget the old
1259 * one. Because there might be other references and there
1260 * might be some change on the model even if we unreferenced
1261 * it, we need to disconnect our signals here. */
1262 if (priv->header_model) {
1263 if (g_signal_handler_is_connected(G_OBJECT (priv->header_model),
1264 priv->row_changed_handler))
1265 g_signal_handler_disconnect(G_OBJECT (priv->header_model),
1266 priv->row_changed_handler);
1267 if (g_signal_handler_is_connected(G_OBJECT (priv->header_model),
1268 priv->row_deleted_handler))
1269 g_signal_handler_disconnect(G_OBJECT (priv->header_model),
1270 priv->row_deleted_handler);
1271 if (g_signal_handler_is_connected(G_OBJECT (priv->header_model),
1272 priv->row_inserted_handler))
1273 g_signal_handler_disconnect(G_OBJECT (priv->header_model),
1274 priv->row_inserted_handler);
1275 if (g_signal_handler_is_connected(G_OBJECT (priv->header_model),
1276 priv->rows_reordered_handler))
1277 g_signal_handler_disconnect(G_OBJECT (priv->header_model),
1278 priv->rows_reordered_handler);
1281 if (priv->row_reference)
1282 gtk_tree_row_reference_free (priv->row_reference);
1283 if (priv->next_row_reference)
1284 gtk_tree_row_reference_free (priv->next_row_reference);
1285 g_object_unref(priv->header_model);
1288 priv->row_changed_handler = 0;
1289 priv->row_deleted_handler = 0;
1290 priv->row_inserted_handler = 0;
1291 priv->rows_reordered_handler = 0;
1292 priv->next_row_reference = NULL;
1293 priv->row_reference = NULL;
1294 priv->header_model = NULL;
1297 priv->header_model = g_object_ref (model);
1299 /* Also we must connect to the new model for row insertions.
1300 * Only for insertions now. We will need other ones only after
1301 * the msg is show by msg-view is added to the new model. */
1302 priv->row_inserted_handler =
1303 g_signal_connect (priv->header_model, "row-inserted",
1304 G_CALLBACK(modest_msg_view_window_on_row_inserted),
1307 modest_ui_actions_check_menu_dimming_rules(MODEST_WINDOW(window));
1308 modest_ui_actions_check_toolbar_dimming_rules(MODEST_WINDOW(window));
1312 modest_msg_view_window_toolbar_on_transfer_mode (ModestMsgViewWindow *self)
1314 ModestMsgViewWindowPrivate *priv= NULL;
1316 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self), FALSE);
1317 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
1319 return priv->progress_hint;
1323 modest_msg_view_window_get_header (ModestMsgViewWindow *self)
1325 ModestMsgViewWindowPrivate *priv= NULL;
1327 TnyHeader *header = NULL;
1328 GtkTreePath *path = NULL;
1331 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self), NULL);
1332 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
1334 /* If the message was not obtained from a treemodel,
1335 * for instance if it was opened directly by the search UI:
1337 if (priv->header_model == NULL ||
1338 priv->row_reference == NULL ||
1339 !gtk_tree_row_reference_valid (priv->row_reference)) {
1340 msg = modest_msg_view_window_get_message (self);
1342 header = tny_msg_get_header (msg);
1343 g_object_unref (msg);
1348 /* Get iter of the currently selected message in the header view: */
1349 path = gtk_tree_row_reference_get_path (priv->row_reference);
1350 g_return_val_if_fail (path != NULL, NULL);
1351 gtk_tree_model_get_iter (priv->header_model,
1355 /* Get current message header */
1356 gtk_tree_model_get (priv->header_model, &iter,
1357 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
1360 gtk_tree_path_free (path);
1365 modest_msg_view_window_get_message (ModestMsgViewWindow *self)
1367 ModestMsgViewWindowPrivate *priv;
1369 g_return_val_if_fail (self, NULL);
1371 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(self);
1373 return tny_msg_view_get_msg (TNY_MSG_VIEW (priv->msg_view));
1377 modest_msg_view_window_get_message_uid (ModestMsgViewWindow *self)
1379 ModestMsgViewWindowPrivate *priv;
1381 g_return_val_if_fail (self, NULL);
1383 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
1385 return (const gchar*) priv->msg_uid;
1389 modest_msg_view_window_toggle_find_toolbar (GtkToggleAction *toggle,
1392 ModestMsgViewWindow *window = MODEST_MSG_VIEW_WINDOW (data);
1393 ModestMsgViewWindowPrivate *priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1394 ModestWindowPrivate *parent_priv = MODEST_WINDOW_GET_PRIVATE (window);
1398 is_active = gtk_toggle_action_get_active (toggle);
1401 gtk_widget_show (priv->find_toolbar);
1402 hildon_find_toolbar_highlight_entry (HILDON_FIND_TOOLBAR (priv->find_toolbar), TRUE);
1404 gtk_widget_hide (priv->find_toolbar);
1405 modest_msg_view_grab_focus (MODEST_MSG_VIEW (priv->msg_view));
1408 /* update the toggle buttons status */
1409 action = gtk_ui_manager_get_action (parent_priv->ui_manager, "/ToolBar/FindInMessage");
1411 modest_utils_toggle_action_set_active_block_notify (GTK_TOGGLE_ACTION (action), is_active);
1416 modest_msg_view_window_find_toolbar_close (GtkWidget *widget,
1417 ModestMsgViewWindow *obj)
1419 GtkToggleAction *toggle;
1420 ModestWindowPrivate *parent_priv;
1421 ModestMsgViewWindowPrivate *priv;
1423 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (obj);
1424 parent_priv = MODEST_WINDOW_GET_PRIVATE (obj);
1426 toggle = GTK_TOGGLE_ACTION (gtk_ui_manager_get_action (parent_priv->ui_manager, "/ToolBar/FindInMessage"));
1427 gtk_toggle_action_set_active (toggle, FALSE);
1428 modest_msg_view_grab_focus (MODEST_MSG_VIEW (priv->msg_view));
1432 modest_msg_view_window_find_toolbar_search (GtkWidget *widget,
1433 ModestMsgViewWindow *obj)
1435 gchar *current_search;
1436 ModestMsgViewWindowPrivate *priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (obj);
1438 if (modest_mime_part_view_is_empty (MODEST_MIME_PART_VIEW (priv->msg_view))) {
1439 hildon_banner_show_information (NULL, NULL, _("mail_ib_nothing_to_find"));
1443 g_object_get (G_OBJECT (widget), "prefix", ¤t_search, NULL);
1445 if ((current_search == NULL) || (strcmp (current_search, "") == 0)) {
1446 g_free (current_search);
1447 hildon_banner_show_information (NULL, NULL, _CS("ecdg_ib_find_rep_enter_text"));
1451 if ((priv->last_search == NULL) || (strcmp (priv->last_search, current_search) != 0)) {
1453 g_free (priv->last_search);
1454 priv->last_search = g_strdup (current_search);
1455 result = modest_isearch_view_search (MODEST_ISEARCH_VIEW (priv->msg_view),
1458 hildon_banner_show_information (NULL, NULL,
1459 _HL("ckct_ib_find_no_matches"));
1460 g_free (priv->last_search);
1461 priv->last_search = NULL;
1463 hildon_find_toolbar_highlight_entry (HILDON_FIND_TOOLBAR (priv->find_toolbar), TRUE);
1466 if (!modest_isearch_view_search_next (MODEST_ISEARCH_VIEW (priv->msg_view))) {
1467 hildon_banner_show_information (NULL, NULL,
1468 _HL("ckct_ib_find_search_complete"));
1469 g_free (priv->last_search);
1470 priv->last_search = NULL;
1472 hildon_find_toolbar_highlight_entry (HILDON_FIND_TOOLBAR (priv->find_toolbar), TRUE);
1476 g_free (current_search);
1481 modest_msg_view_window_set_zoom (ModestWindow *window,
1484 ModestMsgViewWindowPrivate *priv;
1485 ModestWindowPrivate *parent_priv;
1487 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window));
1489 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1490 parent_priv = MODEST_WINDOW_GET_PRIVATE (window);
1491 modest_zoomable_set_zoom (MODEST_ZOOMABLE (priv->msg_view), zoom);
1496 modest_msg_view_window_get_zoom (ModestWindow *window)
1498 ModestMsgViewWindowPrivate *priv;
1500 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), 1.0);
1502 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1503 return modest_zoomable_get_zoom (MODEST_ZOOMABLE (priv->msg_view));
1507 modest_msg_view_window_zoom_plus (ModestWindow *window)
1510 ModestMsgViewWindowPrivate *priv;
1514 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), 1.0);
1515 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1517 zoom_level = modest_zoomable_get_zoom (MODEST_ZOOMABLE (priv->msg_view));
1519 if (zoom_level >= 2.0) {
1520 hildon_banner_show_information (NULL, NULL,
1521 _CS("ckct_ib_max_zoom_level_reached"));
1523 } else if (zoom_level >= 1.5) {
1525 } else if (zoom_level >= 1.2) {
1527 } else if (zoom_level >= 1.0) {
1529 } else if (zoom_level >= 0.8) {
1531 } else if (zoom_level >= 0.5) {
1537 /* set zoom level */
1538 int_zoom = (gint) rint (zoom_level*100.0+0.1);
1539 banner_text = g_strdup_printf (_HL("wdgt_ib_zoom"), int_zoom);
1540 modest_platform_information_banner (GTK_WIDGET (window), NULL, banner_text);
1541 g_free (banner_text);
1542 modest_zoomable_set_zoom (MODEST_ZOOMABLE (priv->msg_view), zoom_level);
1548 modest_msg_view_window_zoom_minus (ModestWindow *window)
1551 ModestMsgViewWindowPrivate *priv;
1555 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), 1.0);
1556 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1558 zoom_level = modest_zoomable_get_zoom (MODEST_ZOOMABLE (priv->msg_view));
1560 if (zoom_level <= 0.5) {
1561 hildon_banner_show_information (NULL, NULL,
1562 _CS("ckct_ib_min_zoom_level_reached"));
1564 } else if (zoom_level <= 0.8) {
1566 } else if (zoom_level <= 1.0) {
1568 } else if (zoom_level <= 1.2) {
1570 } else if (zoom_level <= 1.5) {
1572 } else if (zoom_level <= 2.0) {
1578 /* set zoom level */
1579 int_zoom = (gint) rint (zoom_level*100.0+0.1);
1580 banner_text = g_strdup_printf (_HL("wdgt_ib_zoom"), int_zoom);
1581 modest_platform_information_banner (GTK_WIDGET (window), NULL, banner_text);
1582 g_free (banner_text);
1583 modest_zoomable_set_zoom (MODEST_ZOOMABLE (priv->msg_view), zoom_level);
1590 modest_msg_view_window_key_event (GtkWidget *window,
1596 focus = gtk_window_get_focus (GTK_WINDOW (window));
1598 /* for the find toolbar case */
1599 if (focus && GTK_IS_ENTRY (focus)) {
1600 if (event->keyval == GDK_BackSpace) {
1602 copy = gdk_event_copy ((GdkEvent *) event);
1603 gtk_widget_event (focus, copy);
1604 gdk_event_free (copy);
1609 if (event->keyval == GDK_Up || event->keyval == GDK_KP_Up ||
1610 event->keyval == GDK_Down || event->keyval == GDK_KP_Down ||
1611 event->keyval == GDK_Page_Up || event->keyval == GDK_KP_Page_Up ||
1612 event->keyval == GDK_Page_Down || event->keyval == GDK_KP_Page_Down ||
1613 event->keyval == GDK_Home || event->keyval == GDK_KP_Home ||
1614 event->keyval == GDK_End || event->keyval == GDK_KP_End) {
1615 /* ModestMsgViewWindowPrivate *priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window); */
1616 /* gboolean return_value; */
1618 if (event->type == GDK_KEY_PRESS) {
1619 GtkScrollType scroll_type;
1621 switch (event->keyval) {
1624 scroll_type = GTK_SCROLL_STEP_UP; break;
1627 scroll_type = GTK_SCROLL_STEP_DOWN; break;
1629 case GDK_KP_Page_Up:
1630 scroll_type = GTK_SCROLL_PAGE_UP; break;
1632 case GDK_KP_Page_Down:
1633 scroll_type = GTK_SCROLL_PAGE_DOWN; break;
1636 scroll_type = GTK_SCROLL_START; break;
1639 scroll_type = GTK_SCROLL_END; break;
1640 default: scroll_type = GTK_SCROLL_NONE;
1643 /* g_signal_emit_by_name (G_OBJECT (priv->main_scroll), "scroll-child", */
1644 /* scroll_type, FALSE, &return_value); */
1655 modest_msg_view_window_last_message_selected (ModestMsgViewWindow *window)
1658 ModestMsgViewWindowPrivate *priv;
1659 GtkTreeIter tmp_iter;
1660 gboolean is_last_selected;
1662 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), TRUE);
1663 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1665 /*if no model (so no rows at all), then virtually we are the last*/
1666 if (!priv->header_model || !priv->row_reference)
1669 if (!gtk_tree_row_reference_valid (priv->row_reference))
1672 path = gtk_tree_row_reference_get_path (priv->row_reference);
1676 is_last_selected = TRUE;
1677 while (is_last_selected) {
1679 gtk_tree_path_next (path);
1680 if (!gtk_tree_model_get_iter (priv->header_model, &tmp_iter, path))
1682 gtk_tree_model_get (priv->header_model, &tmp_iter,
1683 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
1686 if (msg_is_visible (header, priv->is_outbox))
1687 is_last_selected = FALSE;
1688 g_object_unref(G_OBJECT(header));
1691 gtk_tree_path_free (path);
1692 return is_last_selected;
1696 modest_msg_view_window_has_headers_model (ModestMsgViewWindow *window)
1698 ModestMsgViewWindowPrivate *priv;
1700 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), TRUE);
1701 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1703 return priv->header_model != NULL;
1707 modest_msg_view_window_is_search_result (ModestMsgViewWindow *window)
1709 ModestMsgViewWindowPrivate *priv;
1711 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), TRUE);
1712 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1714 return priv->is_search_result;
1718 msg_is_visible (TnyHeader *header, gboolean check_outbox)
1720 if ((tny_header_get_flags(header) & TNY_HEADER_FLAG_DELETED))
1722 if (!check_outbox) {
1725 ModestTnySendQueueStatus status;
1726 status = modest_tny_all_send_queues_get_msg_status (header);
1727 return ((status != MODEST_TNY_SEND_QUEUE_FAILED) &&
1728 (status != MODEST_TNY_SEND_QUEUE_SENDING));
1733 modest_msg_view_window_first_message_selected (ModestMsgViewWindow *window)
1736 ModestMsgViewWindowPrivate *priv;
1737 gboolean is_first_selected;
1738 GtkTreeIter tmp_iter;
1740 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), TRUE);
1741 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1743 /*if no model (so no rows at all), then virtually we are the first*/
1744 if (!priv->header_model || !priv->row_reference)
1747 if (!gtk_tree_row_reference_valid (priv->row_reference))
1750 path = gtk_tree_row_reference_get_path (priv->row_reference);
1754 is_first_selected = TRUE;
1755 while (is_first_selected) {
1757 if(!gtk_tree_path_prev (path))
1759 /* Here the 'if' is needless for logic, but let make sure
1760 * iter is valid for gtk_tree_model_get. */
1761 if (!gtk_tree_model_get_iter (priv->header_model, &tmp_iter, path))
1763 gtk_tree_model_get (priv->header_model, &tmp_iter,
1764 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
1767 if (msg_is_visible (header, priv->is_outbox))
1768 is_first_selected = FALSE;
1769 g_object_unref(G_OBJECT(header));
1772 gtk_tree_path_free (path);
1773 return is_first_selected;
1778 GtkTreeRowReference *row_reference;
1782 message_reader_performer (gboolean canceled,
1784 GtkWindow *parent_window,
1785 TnyAccount *account,
1788 ModestMailOperation *mail_op = NULL;
1789 MsgReaderInfo *info;
1791 info = (MsgReaderInfo *) user_data;
1792 if (canceled || err) {
1793 update_window_title (MODEST_MSG_VIEW_WINDOW (parent_window));
1797 /* Register the header - it'll be unregistered in the callback */
1798 modest_window_mgr_register_header (modest_runtime_get_window_mgr (), info->header, NULL);
1800 /* New mail operation */
1801 mail_op = modest_mail_operation_new_with_error_handling (G_OBJECT(parent_window),
1802 modest_ui_actions_disk_operations_error_handler,
1805 modest_mail_operation_queue_add (modest_runtime_get_mail_operation_queue (), mail_op);
1806 modest_mail_operation_get_msg (mail_op, info->header, TRUE, view_msg_cb, info->row_reference);
1807 g_object_unref (mail_op);
1809 /* Update dimming rules */
1810 modest_ui_actions_check_toolbar_dimming_rules (MODEST_WINDOW (parent_window));
1811 modest_ui_actions_check_menu_dimming_rules (MODEST_WINDOW (parent_window));
1814 /* Frees. The row_reference will be freed by the view_msg_cb callback */
1815 g_object_unref (info->header);
1816 g_slice_free (MsgReaderInfo, info);
1821 * Reads the message whose summary item is @header. It takes care of
1822 * several things, among others:
1824 * If the message was not previously downloaded then ask the user
1825 * before downloading. If there is no connection launch the connection
1826 * dialog. Update toolbar dimming rules.
1828 * Returns: TRUE if the mail operation was started, otherwise if the
1829 * user do not want to download the message, or if the user do not
1830 * want to connect, then the operation is not issued
1833 message_reader (ModestMsgViewWindow *window,
1834 ModestMsgViewWindowPrivate *priv,
1836 GtkTreeRowReference *row_reference)
1838 ModestWindowMgr *mgr;
1839 TnyAccount *account;
1841 MsgReaderInfo *info;
1843 g_return_val_if_fail (row_reference != NULL, FALSE);
1845 mgr = modest_runtime_get_window_mgr ();
1846 /* Msg download completed */
1847 if (!(tny_header_get_flags (header) & TNY_HEADER_FLAG_CACHED)) {
1849 /* We set the header from model while we're loading */
1850 tny_header_view_set_header (TNY_HEADER_VIEW (priv->msg_view), header);
1851 gtk_window_set_title (GTK_WINDOW (window), _CS("ckdg_pb_updating"));
1853 /* Ask the user if he wants to download the message if
1855 if (!tny_device_is_online (modest_runtime_get_device())) {
1856 GtkResponseType response;
1858 response = modest_platform_run_confirmation_dialog (GTK_WINDOW (window),
1859 _("mcen_nc_get_msg"));
1860 if (response == GTK_RESPONSE_CANCEL) {
1861 update_window_title (window);
1865 folder = tny_header_get_folder (header);
1866 info = g_slice_new (MsgReaderInfo);
1867 info->header = g_object_ref (header);
1868 info->row_reference = gtk_tree_row_reference_copy (row_reference);
1870 /* Offer the connection dialog if necessary */
1871 modest_platform_connect_if_remote_and_perform ((GtkWindow *) window,
1873 TNY_FOLDER_STORE (folder),
1874 message_reader_performer,
1876 g_object_unref (folder);
1881 folder = tny_header_get_folder (header);
1882 account = tny_folder_get_account (folder);
1883 info = g_slice_new (MsgReaderInfo);
1884 info->header = g_object_ref (header);
1885 info->row_reference = gtk_tree_row_reference_copy (row_reference);
1887 message_reader_performer (FALSE, NULL, (GtkWindow *) window, account, info);
1888 g_object_unref (account);
1889 g_object_unref (folder);
1895 modest_msg_view_window_select_next_message (ModestMsgViewWindow *window)
1897 ModestMsgViewWindowPrivate *priv;
1898 GtkTreePath *path= NULL;
1899 GtkTreeIter tmp_iter;
1901 gboolean retval = TRUE;
1902 GtkTreeRowReference *row_reference = NULL;
1904 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), FALSE);
1905 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1907 if (!priv->row_reference)
1910 /* Update the next row reference if it's not valid. This could
1911 happen if for example the header which it was pointing to,
1912 was deleted. The best place to do it is in the row-deleted
1913 handler but the tinymail model do not work like the glib
1914 tree models and reports the deletion when the row is still
1916 if (!gtk_tree_row_reference_valid (priv->next_row_reference)) {
1917 if (gtk_tree_row_reference_valid (priv->row_reference)) {
1918 priv->next_row_reference = gtk_tree_row_reference_copy (priv->row_reference);
1919 select_next_valid_row (priv->header_model, &(priv->next_row_reference), FALSE, priv->is_outbox);
1922 if (priv->next_row_reference)
1923 path = gtk_tree_row_reference_get_path (priv->next_row_reference);
1927 row_reference = gtk_tree_row_reference_copy (priv->next_row_reference);
1929 gtk_tree_model_get_iter (priv->header_model,
1932 gtk_tree_path_free (path);
1934 gtk_tree_model_get (priv->header_model, &tmp_iter,
1935 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
1938 /* Read the message & show it */
1939 if (!message_reader (window, priv, header, row_reference)) {
1942 gtk_tree_row_reference_free (row_reference);
1945 g_object_unref (header);
1951 modest_msg_view_window_select_previous_message (ModestMsgViewWindow *window)
1953 ModestMsgViewWindowPrivate *priv = NULL;
1955 gboolean finished = FALSE;
1956 gboolean retval = FALSE;
1958 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), FALSE);
1959 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1961 /* Return inmediatly if there is no header model */
1962 if (!priv->header_model || !priv->row_reference)
1965 path = gtk_tree_row_reference_get_path (priv->row_reference);
1966 while (!finished && gtk_tree_path_prev (path)) {
1970 gtk_tree_model_get_iter (priv->header_model, &iter, path);
1971 gtk_tree_model_get (priv->header_model, &iter,
1972 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
1976 if (msg_is_visible (header, priv->is_outbox)) {
1977 GtkTreeRowReference *row_reference;
1978 row_reference = gtk_tree_row_reference_new (priv->header_model, path);
1979 /* Read the message & show it */
1980 retval = message_reader (window, priv, header, row_reference);
1981 gtk_tree_row_reference_free (row_reference);
1985 g_object_unref (header);
1989 gtk_tree_path_free (path);
1994 view_msg_cb (ModestMailOperation *mail_op,
2001 ModestMsgViewWindow *self = NULL;
2002 ModestMsgViewWindowPrivate *priv = NULL;
2003 GtkTreeRowReference *row_reference = NULL;
2005 /* Unregister the header (it was registered before creating the mail operation) */
2006 modest_window_mgr_unregister_header (modest_runtime_get_window_mgr (), header);
2008 row_reference = (GtkTreeRowReference *) user_data;
2010 gtk_tree_row_reference_free (row_reference);
2011 self = (ModestMsgViewWindow *) modest_mail_operation_get_source (mail_op);
2013 /* Restore window title */
2014 update_window_title (self);
2015 g_object_unref (self);
2020 /* If there was any error */
2021 if (!modest_ui_actions_msg_retrieval_check (mail_op, header, msg)) {
2022 gtk_tree_row_reference_free (row_reference);
2023 self = (ModestMsgViewWindow *) modest_mail_operation_get_source (mail_op);
2025 /* Restore window title */
2026 update_window_title (self);
2027 g_object_unref (self);
2032 /* Get the window */
2033 self = (ModestMsgViewWindow *) modest_mail_operation_get_source (mail_op);
2034 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self));
2035 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
2037 /* Update the row reference */
2038 if (priv->row_reference != NULL) {
2039 gtk_tree_row_reference_free (priv->row_reference);
2040 priv->row_reference = gtk_tree_row_reference_copy (row_reference);
2041 if (priv->next_row_reference != NULL) {
2042 gtk_tree_row_reference_free (priv->next_row_reference);
2044 priv->next_row_reference = gtk_tree_row_reference_copy (priv->row_reference);
2045 select_next_valid_row (priv->header_model, &(priv->next_row_reference), TRUE, priv->is_outbox);
2048 /* Mark header as read */
2049 if (!(tny_header_get_flags (header) & TNY_HEADER_FLAG_SEEN))
2050 tny_header_set_flag (header, TNY_HEADER_FLAG_SEEN);
2052 /* Set new message */
2053 if (priv->msg_view != NULL && TNY_IS_MSG_VIEW (priv->msg_view)) {
2054 tny_msg_view_set_msg (TNY_MSG_VIEW (priv->msg_view), msg);
2055 modest_msg_view_window_update_priority (self);
2056 update_window_title (MODEST_MSG_VIEW_WINDOW (self));
2057 modest_msg_view_grab_focus (MODEST_MSG_VIEW (priv->msg_view));
2060 /* Set the new message uid of the window */
2061 if (priv->msg_uid) {
2062 g_free (priv->msg_uid);
2063 priv->msg_uid = modest_tny_folder_get_header_unique_id (header);
2066 /* Notify the observers */
2067 g_signal_emit (G_OBJECT (self), signals[MSG_CHANGED_SIGNAL],
2068 0, priv->header_model, priv->row_reference);
2071 g_object_unref (self);
2072 gtk_tree_row_reference_free (row_reference);
2076 modest_msg_view_window_get_folder_type (ModestMsgViewWindow *window)
2078 ModestMsgViewWindowPrivate *priv;
2080 TnyFolderType folder_type;
2082 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
2084 folder_type = TNY_FOLDER_TYPE_UNKNOWN;
2086 msg = tny_msg_view_get_msg (TNY_MSG_VIEW (priv->msg_view));
2090 folder = tny_msg_get_folder (msg);
2092 folder_type = modest_tny_folder_guess_folder_type (folder);
2093 g_object_unref (folder);
2095 g_object_unref (msg);
2103 modest_msg_view_window_update_priority (ModestMsgViewWindow *window)
2105 ModestMsgViewWindowPrivate *priv;
2106 TnyHeader *header = NULL;
2107 TnyHeaderFlags flags = 0;
2109 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
2111 if (priv->header_model && priv->row_reference) {
2113 GtkTreePath *path = NULL;
2115 path = gtk_tree_row_reference_get_path (priv->row_reference);
2116 g_return_if_fail (path != NULL);
2117 gtk_tree_model_get_iter (priv->header_model,
2119 gtk_tree_row_reference_get_path (priv->row_reference));
2121 gtk_tree_model_get (priv->header_model, &iter, TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
2123 gtk_tree_path_free (path);
2126 msg = tny_msg_view_get_msg (TNY_MSG_VIEW (priv->msg_view));
2128 header = tny_msg_get_header (msg);
2129 g_object_unref (msg);
2134 flags = tny_header_get_flags (header);
2135 g_object_unref(G_OBJECT(header));
2138 modest_msg_view_set_priority (MODEST_MSG_VIEW(priv->msg_view), flags);
2143 toolbar_resize (ModestMsgViewWindow *self)
2145 ModestMsgViewWindowPrivate *priv = NULL;
2146 ModestWindowPrivate *parent_priv = NULL;
2148 gint static_button_size;
2149 ModestWindowMgr *mgr;
2151 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self));
2152 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
2153 parent_priv = MODEST_WINDOW_GET_PRIVATE(self);
2155 mgr = modest_runtime_get_window_mgr ();
2156 static_button_size = modest_window_mgr_get_fullscreen_mode (mgr)?120:120;
2158 if (parent_priv->toolbar) {
2159 /* left size buttons */
2160 widget = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/ToolbarMessageReply");
2161 gtk_tool_item_set_expand (GTK_TOOL_ITEM (widget), FALSE);
2162 gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (widget), FALSE);
2163 gtk_widget_set_size_request (GTK_WIDGET (widget), static_button_size, -1);
2164 widget = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/ToolbarMessageMoveTo");
2165 gtk_tool_item_set_expand (GTK_TOOL_ITEM (widget), FALSE);
2166 gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (widget), FALSE);
2167 gtk_widget_set_size_request (GTK_WIDGET (widget), static_button_size, -1);
2168 widget = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/ToolbarDeleteMessage");
2169 gtk_tool_item_set_expand (GTK_TOOL_ITEM (widget), FALSE);
2170 gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (widget), FALSE);
2171 gtk_widget_set_size_request (GTK_WIDGET (widget), static_button_size, -1);
2172 widget = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/FindInMessage");
2173 gtk_tool_item_set_expand (GTK_TOOL_ITEM (widget), FALSE);
2174 gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (widget), FALSE);
2175 gtk_widget_set_size_request (GTK_WIDGET (widget), static_button_size, -1);
2177 gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (priv->next_toolitem), TRUE);
2178 gtk_tool_item_set_expand (GTK_TOOL_ITEM (priv->next_toolitem), TRUE);
2179 gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (priv->prev_toolitem), TRUE);
2180 gtk_tool_item_set_expand (GTK_TOOL_ITEM (priv->prev_toolitem), TRUE);
2185 modest_msg_view_window_show_toolbar (ModestWindow *self,
2186 gboolean show_toolbar)
2188 ModestMsgViewWindowPrivate *priv = NULL;
2189 ModestWindowPrivate *parent_priv;
2191 parent_priv = MODEST_WINDOW_GET_PRIVATE(self);
2192 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(self);
2194 /* Set optimized view status */
2195 priv->optimized_view = !show_toolbar;
2197 if (!parent_priv->toolbar) {
2198 parent_priv->toolbar = gtk_ui_manager_get_widget (parent_priv->ui_manager,
2200 gtk_toolbar_set_icon_size (GTK_TOOLBAR (parent_priv->toolbar), HILDON_ICON_SIZE_FINGER);
2201 gtk_widget_set_no_show_all (parent_priv->toolbar, TRUE);
2203 priv->next_toolitem = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/ToolbarMessageNext");
2204 priv->prev_toolitem = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/ToolbarMessageBack");
2205 toolbar_resize (MODEST_MSG_VIEW_WINDOW (self));
2208 hildon_window_add_toolbar (HILDON_WINDOW (self),
2209 GTK_TOOLBAR (parent_priv->toolbar));
2214 /* Quick hack: this prevents toolbar icons "dance" when progress bar show status is changed */
2215 /* TODO: resize mode migth be GTK_RESIZE_QUEUE, in order to avoid unneccesary shows */
2216 gtk_container_set_resize_mode (GTK_CONTAINER(parent_priv->toolbar), GTK_RESIZE_IMMEDIATE);
2218 gtk_widget_show (GTK_WIDGET (parent_priv->toolbar));
2219 if (modest_msg_view_window_transfer_mode_enabled (MODEST_MSG_VIEW_WINDOW (self)))
2220 set_progress_hint (MODEST_MSG_VIEW_WINDOW (self), TRUE);
2222 set_progress_hint (MODEST_MSG_VIEW_WINDOW (self), FALSE);
2225 gtk_widget_set_no_show_all (parent_priv->toolbar, TRUE);
2226 gtk_widget_hide (GTK_WIDGET (parent_priv->toolbar));
2231 modest_msg_view_window_clipboard_owner_change (GtkClipboard *clipboard,
2233 ModestMsgViewWindow *window)
2235 if (!GTK_WIDGET_VISIBLE (window))
2238 modest_window_check_dimming_rules_group (MODEST_WINDOW (window), MODEST_DIMMING_RULES_CLIPBOARD);
2242 modest_msg_view_window_transfer_mode_enabled (ModestMsgViewWindow *self)
2244 ModestMsgViewWindowPrivate *priv;
2246 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self), FALSE);
2247 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(self);
2249 return priv->progress_hint;
2253 observers_empty (ModestMsgViewWindow *self)
2256 ModestMsgViewWindowPrivate *priv;
2257 gboolean is_empty = TRUE;
2258 guint pending_ops = 0;
2260 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(self);
2261 tmp = priv->progress_widgets;
2263 /* Check all observers */
2264 while (tmp && is_empty) {
2265 pending_ops = modest_progress_object_num_pending_operations (MODEST_PROGRESS_OBJECT(tmp->data));
2266 is_empty = pending_ops == 0;
2268 tmp = g_slist_next(tmp);
2275 on_account_removed (TnyAccountStore *account_store,
2276 TnyAccount *account,
2279 /* Do nothing if it's a transport account, because we only
2280 show the messages of a store account */
2281 if (tny_account_get_account_type(account) == TNY_ACCOUNT_TYPE_STORE) {
2282 const gchar *parent_acc = NULL;
2283 const gchar *our_acc = NULL;
2285 our_acc = modest_window_get_active_account (MODEST_WINDOW (user_data));
2286 parent_acc = modest_tny_account_get_parent_modest_account_name_for_server_account (account);
2288 /* Close this window if I'm showing a message of the removed account */
2289 if (our_acc && parent_acc && strcmp (parent_acc, our_acc) == 0)
2290 modest_ui_actions_on_close_window (NULL, MODEST_WINDOW (user_data));
2295 on_mail_operation_started (ModestMailOperation *mail_op,
2298 ModestMsgViewWindow *self;
2299 ModestMailOperationTypeOperation op_type;
2301 ModestMsgViewWindowPrivate *priv;
2302 GObject *source = NULL;
2304 self = MODEST_MSG_VIEW_WINDOW (user_data);
2305 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
2306 op_type = modest_mail_operation_get_type_operation (mail_op);
2307 tmp = priv->progress_widgets;
2308 source = modest_mail_operation_get_source(mail_op);
2309 if (G_OBJECT (self) == source) {
2310 if (op_type == MODEST_MAIL_OPERATION_TYPE_RECEIVE) {
2311 set_toolbar_transfer_mode(self);
2313 modest_progress_object_add_operation (
2314 MODEST_PROGRESS_OBJECT (tmp->data),
2316 tmp = g_slist_next (tmp);
2320 g_object_unref (source);
2324 on_mail_operation_finished (ModestMailOperation *mail_op,
2327 ModestMsgViewWindow *self;
2328 ModestMailOperationTypeOperation op_type;
2330 ModestMsgViewWindowPrivate *priv;
2332 self = MODEST_MSG_VIEW_WINDOW (user_data);
2333 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
2334 op_type = modest_mail_operation_get_type_operation (mail_op);
2335 tmp = priv->progress_widgets;
2337 if (op_type == MODEST_MAIL_OPERATION_TYPE_RECEIVE) {
2339 modest_progress_object_remove_operation (MODEST_PROGRESS_OBJECT (tmp->data),
2341 tmp = g_slist_next (tmp);
2344 /* If no more operations are being observed, NORMAL mode is enabled again */
2345 if (observers_empty (self)) {
2346 set_progress_hint (self, FALSE);
2350 /* Update dimming rules. We have to do this right here
2351 and not in view_msg_cb because at that point the
2352 transfer mode is still enabled so the dimming rule
2353 won't let the user delete the message that has been
2354 readed for example */
2355 check_dimming_rules_after_change (self);
2360 on_queue_changed (ModestMailOperationQueue *queue,
2361 ModestMailOperation *mail_op,
2362 ModestMailOperationQueueNotification type,
2363 ModestMsgViewWindow *self)
2365 ModestMsgViewWindowPrivate *priv;
2367 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
2369 /* If this operations was created by another window, do nothing */
2370 if (!modest_mail_operation_is_mine (mail_op, G_OBJECT(self)))
2373 if (type == MODEST_MAIL_OPERATION_QUEUE_OPERATION_ADDED) {
2374 priv->sighandlers = modest_signal_mgr_connect (priv->sighandlers,
2376 "operation-started",
2377 G_CALLBACK (on_mail_operation_started),
2379 priv->sighandlers = modest_signal_mgr_connect (priv->sighandlers,
2381 "operation-finished",
2382 G_CALLBACK (on_mail_operation_finished),
2384 } else if (type == MODEST_MAIL_OPERATION_QUEUE_OPERATION_REMOVED) {
2385 priv->sighandlers = modest_signal_mgr_disconnect (priv->sighandlers,
2387 "operation-started");
2388 priv->sighandlers = modest_signal_mgr_disconnect (priv->sighandlers,
2390 "operation-finished");
2395 modest_msg_view_window_get_attachments (ModestMsgViewWindow *win)
2397 ModestMsgViewWindowPrivate *priv;
2398 TnyList *selected_attachments = NULL;
2400 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (win), NULL);
2401 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (win);
2403 /* In Hildon 2.2 as there's no selection we assume we have all attachments selected */
2404 selected_attachments = modest_msg_view_get_attachments (MODEST_MSG_VIEW (priv->msg_view));
2406 return selected_attachments;
2410 ModestMsgViewWindow *self;
2412 } DecodeAsyncHelper;
2415 on_decode_to_stream_async_handler (TnyMimePart *mime_part,
2421 DecodeAsyncHelper *helper = (DecodeAsyncHelper *) user_data;
2423 /* It could happen that the window was closed */
2424 if (GTK_WIDGET_VISIBLE (helper->self))
2425 set_progress_hint (helper->self, FALSE);
2427 if (cancelled || err) {
2429 gchar *msg = g_strdup_printf (_KR("cerm_device_memory_full"), "");
2430 modest_platform_information_banner (NULL, NULL, msg);
2436 /* make the file read-only */
2437 g_chmod(helper->file_path, 0444);
2439 /* Activate the file */
2440 modest_platform_activate_file (helper->file_path, tny_mime_part_get_content_type (mime_part));
2444 g_object_unref (helper->self);
2445 g_free (helper->file_path);
2446 g_slice_free (DecodeAsyncHelper, helper);
2450 modest_msg_view_window_view_attachment (ModestMsgViewWindow *window,
2451 TnyMimePart *mime_part)
2453 ModestMsgViewWindowPrivate *priv;
2454 const gchar *msg_uid;
2455 gchar *attachment_uid = NULL;
2456 gint attachment_index = 0;
2457 TnyList *attachments;
2459 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window));
2460 g_return_if_fail (TNY_IS_MIME_PART (mime_part) || (mime_part == NULL));
2461 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
2463 msg_uid = modest_msg_view_window_get_message_uid (MODEST_MSG_VIEW_WINDOW (window));
2464 attachments = modest_msg_view_get_attachments (MODEST_MSG_VIEW (priv->msg_view));
2465 attachment_index = modest_list_index (attachments, (GObject *) mime_part);
2466 g_object_unref (attachments);
2468 if (msg_uid && attachment_index >= 0) {
2469 attachment_uid = g_strdup_printf ("%s/%d", msg_uid, attachment_index);
2472 if (mime_part == NULL) {
2473 gboolean error = FALSE;
2474 TnyList *selected_attachments = modest_msg_view_get_selected_attachments (MODEST_MSG_VIEW (priv->msg_view));
2475 if (selected_attachments == NULL || tny_list_get_length (selected_attachments) == 0) {
2477 } else if (tny_list_get_length (selected_attachments) > 1) {
2478 hildon_banner_show_information (NULL, NULL, _("mcen_ib_unable_to_display_more"));
2482 iter = tny_list_create_iterator (selected_attachments);
2483 mime_part = (TnyMimePart *) tny_iterator_get_current (iter);
2484 g_object_unref (iter);
2486 if (selected_attachments)
2487 g_object_unref (selected_attachments);
2492 g_object_ref (mime_part);
2495 if (tny_mime_part_is_purged (mime_part))
2498 if (!modest_tny_mime_part_is_msg (mime_part)) {
2499 gchar *filepath = NULL;
2500 const gchar *att_filename = tny_mime_part_get_filename (mime_part);
2501 gboolean show_error_banner = FALSE;
2502 TnyFsStream *temp_stream = NULL;
2503 temp_stream = modest_utils_create_temp_stream (att_filename, attachment_uid,
2506 if (temp_stream != NULL) {
2507 DecodeAsyncHelper *helper;
2509 /* Activate progress hint */
2510 set_progress_hint (window, TRUE);
2512 helper = g_slice_new0 (DecodeAsyncHelper);
2513 helper->self = g_object_ref (window);
2514 helper->file_path = g_strdup (filepath);
2516 tny_mime_part_decode_to_stream_async (mime_part, TNY_STREAM (temp_stream),
2517 on_decode_to_stream_async_handler,
2520 g_object_unref (temp_stream);
2521 /* NOTE: files in the temporary area will be automatically
2522 * cleaned after some time if they are no longer in use */
2525 const gchar *content_type;
2526 /* the file may already exist but it isn't writable,
2527 * let's try to open it anyway */
2528 content_type = tny_mime_part_get_content_type (mime_part);
2529 modest_platform_activate_file (filepath, content_type);
2531 g_warning ("%s: modest_utils_create_temp_stream failed", __FUNCTION__);
2532 show_error_banner = TRUE;
2537 if (show_error_banner)
2538 modest_platform_information_banner (NULL, NULL, _("mail_ib_file_operation_failed"));
2540 /* message attachment */
2541 TnyHeader *header = NULL;
2542 ModestWindowMgr *mgr;
2543 ModestWindow *msg_win = NULL;
2546 header = tny_msg_get_header (TNY_MSG (mime_part));
2547 mgr = modest_runtime_get_window_mgr ();
2548 found = modest_window_mgr_find_registered_header (mgr, header, &msg_win);
2551 /* if it's found, but there is no msg_win, it's probably in the process of being created;
2552 * thus, we don't do anything */
2553 g_warning ("window for is already being created");
2555 /* it's not found, so create a new window for it */
2556 modest_window_mgr_register_header (mgr, header, attachment_uid); /* register the uid before building the window */
2557 gchar *account = g_strdup (modest_window_get_active_account (MODEST_WINDOW (window)));
2558 const gchar *mailbox = modest_window_get_active_mailbox (MODEST_WINDOW (window));
2560 account = modest_account_mgr_get_default_account (modest_runtime_get_account_mgr ());
2561 msg_win = modest_msg_view_window_new_for_attachment (TNY_MSG (mime_part), account,
2562 mailbox, attachment_uid);
2563 modest_window_set_zoom (MODEST_WINDOW (msg_win),
2564 modest_window_get_zoom (MODEST_WINDOW (window)));
2565 if (modest_window_mgr_register_window (mgr, msg_win, MODEST_WINDOW (window)))
2566 gtk_widget_show_all (GTK_WIDGET (msg_win));
2568 gtk_widget_destroy (GTK_WIDGET (msg_win));
2574 g_free (attachment_uid);
2576 g_object_unref (mime_part);
2588 GnomeVFSResult result;
2591 static void save_mime_part_info_free (SaveMimePartInfo *info, gboolean with_struct);
2592 static gboolean idle_save_mime_part_show_result (SaveMimePartInfo *info);
2593 static gpointer save_mime_part_to_file (SaveMimePartInfo *info);
2594 static void save_mime_parts_to_file_with_checks (SaveMimePartInfo *info);
2597 save_mime_part_info_free (SaveMimePartInfo *info, gboolean with_struct)
2601 for (node = info->pairs; node != NULL; node = g_list_next (node)) {
2602 SaveMimePartPair *pair = (SaveMimePartPair *) node->data;
2603 g_free (pair->filename);
2604 g_object_unref (pair->part);
2605 g_slice_free (SaveMimePartPair, pair);
2607 g_list_free (info->pairs);
2610 g_slice_free (SaveMimePartInfo, info);
2615 idle_save_mime_part_show_result (SaveMimePartInfo *info)
2617 if (info->pairs != NULL) {
2618 save_mime_part_to_file (info);
2620 /* This is a GDK lock because we are an idle callback and
2621 * hildon_banner_show_information is or does Gtk+ code */
2623 gdk_threads_enter (); /* CHECKED */
2624 save_mime_part_info_free (info, TRUE);
2625 if (info->result == GNOME_VFS_OK) {
2626 hildon_banner_show_information (NULL, NULL, _CS("sfil_ib_saved"));
2627 } else if (info->result == GNOME_VFS_ERROR_NO_SPACE) {
2628 gchar *msg = g_strdup_printf (_KR("cerm_device_memory_full"), "");
2629 modest_platform_information_banner (NULL, NULL, msg);
2632 hildon_banner_show_information (NULL, NULL, _("mail_ib_file_operation_failed"));
2634 gdk_threads_leave (); /* CHECKED */
2641 save_mime_part_to_file (SaveMimePartInfo *info)
2643 GnomeVFSHandle *handle;
2645 SaveMimePartPair *pair = (SaveMimePartPair *) info->pairs->data;
2647 info->result = gnome_vfs_create (&handle, pair->filename, GNOME_VFS_OPEN_WRITE, FALSE, 0644);
2648 if (info->result == GNOME_VFS_OK) {
2649 GError *error = NULL;
2650 stream = tny_vfs_stream_new (handle);
2651 if (tny_mime_part_decode_to_stream (pair->part, stream, &error) < 0) {
2652 g_warning ("modest: could not save attachment %s: %d (%s)\n", pair->filename, error?error->code:-1, error?error->message:"Unknown error");
2654 if ((error->domain == TNY_ERROR_DOMAIN) &&
2655 (error->code == TNY_IO_ERROR_WRITE) &&
2656 (errno == ENOSPC)) {
2657 info->result = GNOME_VFS_ERROR_NO_SPACE;
2659 info->result = GNOME_VFS_ERROR_IO;
2662 g_object_unref (G_OBJECT (stream));
2663 g_object_unref (pair->part);
2664 g_slice_free (SaveMimePartPair, pair);
2665 info->pairs = g_list_delete_link (info->pairs, info->pairs);
2667 g_warning ("modest: could not create save attachment %s: %s\n", pair->filename, gnome_vfs_result_to_string (info->result));
2668 save_mime_part_info_free (info, FALSE);
2671 g_idle_add ((GSourceFunc) idle_save_mime_part_show_result, info);
2676 save_mime_parts_to_file_with_checks (SaveMimePartInfo *info)
2678 gboolean is_ok = TRUE;
2679 gint replaced_files = 0;
2680 const GList *files = info->pairs;
2683 for (iter = files; (iter != NULL) && (replaced_files < 2); iter = g_list_next(iter)) {
2684 SaveMimePartPair *pair = iter->data;
2685 if (modest_utils_file_exists (pair->filename)) {
2689 if (replaced_files) {
2690 GtkWidget *confirm_overwrite_dialog;
2692 if (replaced_files == 1) {
2693 SaveMimePartPair *pair = files->data;
2694 const gchar *filename = tny_mime_part_get_filename (pair->part);
2695 gchar *message = g_strdup_printf ("%s\n%s",
2696 _FM("docm_nc_replace_file"),
2697 (filename) ? filename : "");
2698 confirm_overwrite_dialog = hildon_note_new_confirmation (NULL, message);
2701 confirm_overwrite_dialog = hildon_note_new_confirmation (NULL,
2702 _FM("docm_nc_replace_multiple"));
2704 if (gtk_dialog_run (GTK_DIALOG (confirm_overwrite_dialog)) != GTK_RESPONSE_OK)
2707 gtk_widget_destroy (confirm_overwrite_dialog);
2711 save_mime_part_info_free (info, TRUE);
2713 g_thread_create ((GThreadFunc)save_mime_part_to_file, info, FALSE, NULL);
2719 save_attachments_response (GtkDialog *dialog,
2723 TnyList *mime_parts;
2725 GList *files_to_save = NULL;
2726 gchar *current_folder;
2728 mime_parts = TNY_LIST (user_data);
2730 if (arg1 != GTK_RESPONSE_OK)
2733 chooser_uri = gtk_file_chooser_get_uri (GTK_FILE_CHOOSER (dialog));
2734 current_folder = gtk_file_chooser_get_current_folder_uri (GTK_FILE_CHOOSER (dialog));
2735 if (current_folder && current_folder != '\0') {
2737 modest_conf_set_string (modest_runtime_get_conf (), MODEST_CONF_LATEST_SAVE_ATTACHMENT_PATH,
2738 current_folder,&err);
2740 g_debug ("Error storing latest used folder: %s", err->message);
2744 g_free (current_folder);
2746 if (!modest_utils_folder_writable (chooser_uri)) {
2747 hildon_banner_show_information
2748 (NULL, NULL, _FM("sfil_ib_readonly_location"));
2752 iter = tny_list_create_iterator (mime_parts);
2753 while (!tny_iterator_is_done (iter)) {
2754 TnyMimePart *mime_part = (TnyMimePart *) tny_iterator_get_current (iter);
2756 if ((modest_tny_mime_part_is_attachment_for_modest (mime_part)) &&
2757 !tny_mime_part_is_purged (mime_part) &&
2758 (tny_mime_part_get_filename (mime_part) != NULL)) {
2759 SaveMimePartPair *pair;
2761 pair = g_slice_new0 (SaveMimePartPair);
2763 if (tny_list_get_length (mime_parts) > 1) {
2765 gnome_vfs_escape_slashes (tny_mime_part_get_filename (mime_part));
2766 pair->filename = g_build_filename (chooser_uri, escaped, NULL);
2769 pair->filename = g_strdup (chooser_uri);
2771 pair->part = mime_part;
2772 files_to_save = g_list_prepend (files_to_save, pair);
2774 tny_iterator_next (iter);
2776 g_object_unref (iter);
2778 g_free (chooser_uri);
2780 if (files_to_save != NULL) {
2781 SaveMimePartInfo *info = g_slice_new0 (SaveMimePartInfo);
2782 info->pairs = files_to_save;
2783 info->result = TRUE;
2784 save_mime_parts_to_file_with_checks (info);
2788 /* Free and close the dialog */
2789 g_object_unref (mime_parts);
2790 gtk_widget_destroy (GTK_WIDGET (dialog));
2794 modest_msg_view_window_save_attachments (ModestMsgViewWindow *window,
2795 TnyList *mime_parts)
2797 ModestMsgViewWindowPrivate *priv;
2798 GtkWidget *save_dialog = NULL;
2799 gchar *conf_folder = NULL;
2800 gchar *filename = NULL;
2801 gchar *save_multiple_str = NULL;
2803 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window));
2804 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
2806 if (mime_parts == NULL) {
2807 /* In Hildon 2.2 save and delete operate over all the attachments as there's no
2808 * selection available */
2809 mime_parts = modest_msg_view_get_attachments (MODEST_MSG_VIEW (priv->msg_view));
2810 if (mime_parts && !modest_maemo_utils_select_attachments (GTK_WINDOW (window), mime_parts, FALSE)) {
2811 g_object_unref (mime_parts);
2814 if (mime_parts == NULL || tny_list_get_length (mime_parts) == 0) {
2816 g_object_unref (mime_parts);
2822 g_object_ref (mime_parts);
2825 /* prepare dialog */
2826 if (tny_list_get_length (mime_parts) == 1) {
2828 /* only one attachment selected */
2829 iter = tny_list_create_iterator (mime_parts);
2830 TnyMimePart *mime_part = (TnyMimePart *) tny_iterator_get_current (iter);
2831 g_object_unref (iter);
2832 if (!modest_tny_mime_part_is_msg (mime_part) &&
2833 modest_tny_mime_part_is_attachment_for_modest (mime_part) &&
2834 !tny_mime_part_is_purged (mime_part)) {
2835 filename = g_strdup (tny_mime_part_get_filename (mime_part));
2837 /* TODO: show any error? */
2838 g_warning ("Tried to save a non-file attachment");
2839 g_object_unref (mime_parts);
2842 g_object_unref (mime_part);
2844 save_multiple_str = g_strdup_printf (_FM("sfil_va_number_of_objects_attachments"),
2845 tny_list_get_length (mime_parts));
2848 save_dialog = hildon_file_chooser_dialog_new (GTK_WINDOW (window),
2849 GTK_FILE_CHOOSER_ACTION_SAVE);
2852 conf_folder = modest_conf_get_string (modest_runtime_get_conf (), MODEST_CONF_LATEST_SAVE_ATTACHMENT_PATH, NULL);
2853 if (conf_folder && conf_folder[0] != '\0') {
2854 gtk_file_chooser_set_current_folder_uri (GTK_FILE_CHOOSER (save_dialog), conf_folder);
2857 /* Set the default folder to images folder */
2858 docs_folder = g_build_filename (g_getenv (MYDOCS_ENV), DOCS_FOLDER, NULL);
2859 gtk_file_chooser_set_current_folder (GTK_FILE_CHOOSER (save_dialog), docs_folder);
2860 g_free (docs_folder);
2862 g_free (conf_folder);
2866 gtk_file_chooser_set_current_name (GTK_FILE_CHOOSER (save_dialog),
2871 /* if multiple, set multiple string */
2872 if (save_multiple_str) {
2873 g_object_set (G_OBJECT (save_dialog), "save-multiple", save_multiple_str, NULL);
2874 gtk_window_set_title (GTK_WINDOW (save_dialog), _FM("sfil_ti_save_objects_files"));
2877 /* We must run this asynchronously, because the hildon dialog
2878 performs a gtk_dialog_run by itself which leads to gdk
2880 g_signal_connect (save_dialog, "response",
2881 G_CALLBACK (save_attachments_response), mime_parts);
2883 gtk_widget_show_all (save_dialog);
2887 show_remove_attachment_information (gpointer userdata)
2889 ModestMsgViewWindow *window = (ModestMsgViewWindow *) userdata;
2890 ModestMsgViewWindowPrivate *priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
2892 /* We're outside the main lock */
2893 gdk_threads_enter ();
2895 if (priv->remove_attachment_banner != NULL) {
2896 gtk_widget_destroy (priv->remove_attachment_banner);
2897 g_object_unref (priv->remove_attachment_banner);
2900 priv->remove_attachment_banner = g_object_ref (
2901 hildon_banner_show_animation (NULL, NULL, _("mcen_me_inbox_remove_attachments")));
2903 gdk_threads_leave ();
2909 modest_msg_view_window_remove_attachments (ModestMsgViewWindow *window, gboolean get_all)
2911 ModestMsgViewWindowPrivate *priv;
2912 TnyList *mime_parts = NULL, *tmp;
2913 gchar *confirmation_message;
2919 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window));
2920 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
2922 /* In hildon 2.2 we ignore the get_all flag as we always get all attachments. This is
2923 * because we don't have selection
2925 mime_parts = modest_msg_view_get_attachments (MODEST_MSG_VIEW (priv->msg_view));
2927 /* Remove already purged messages from mime parts list. We use
2928 a copy of the list to remove items in the original one */
2929 tmp = tny_list_copy (mime_parts);
2930 iter = tny_list_create_iterator (tmp);
2931 while (!tny_iterator_is_done (iter)) {
2932 TnyMimePart *part = TNY_MIME_PART (tny_iterator_get_current (iter));
2933 if (tny_mime_part_is_purged (part))
2934 tny_list_remove (mime_parts, (GObject *) part);
2936 g_object_unref (part);
2937 tny_iterator_next (iter);
2939 g_object_unref (tmp);
2940 g_object_unref (iter);
2942 if (!modest_maemo_utils_select_attachments (GTK_WINDOW (window), mime_parts, TRUE) ||
2943 tny_list_get_length (mime_parts) == 0) {
2944 g_object_unref (mime_parts);
2948 n_attachments = tny_list_get_length (mime_parts);
2949 if (n_attachments == 1) {
2953 iter = tny_list_create_iterator (mime_parts);
2954 part = (TnyMimePart *) tny_iterator_get_current (iter);
2955 g_object_unref (iter);
2956 if (modest_tny_mime_part_is_msg (part)) {
2958 header = tny_msg_get_header (TNY_MSG (part));
2959 filename = tny_header_dup_subject (header);
2960 g_object_unref (header);
2961 if (filename == NULL)
2962 filename = g_strdup (_("mail_va_no_subject"));
2964 filename = g_strdup (tny_mime_part_get_filename (TNY_MIME_PART (part)));
2966 confirmation_message = g_strdup_printf (_("mcen_nc_purge_file_text"), filename);
2968 g_object_unref (part);
2970 confirmation_message = g_strdup_printf (ngettext("mcen_nc_purge_file_text",
2971 "mcen_nc_purge_files_text",
2972 n_attachments), n_attachments);
2974 response = modest_platform_run_confirmation_dialog (GTK_WINDOW (window),
2975 confirmation_message);
2976 g_free (confirmation_message);
2978 if (response != GTK_RESPONSE_OK) {
2979 g_object_unref (mime_parts);
2983 priv->purge_timeout = g_timeout_add (2000, show_remove_attachment_information, window);
2985 iter = tny_list_create_iterator (mime_parts);
2986 while (!tny_iterator_is_done (iter)) {
2989 part = (TnyMimePart *) tny_iterator_get_current (iter);
2990 tny_mime_part_set_purged (TNY_MIME_PART (part));
2991 g_object_unref (part);
2992 tny_iterator_next (iter);
2994 g_object_unref (iter);
2996 msg = tny_msg_view_get_msg (TNY_MSG_VIEW (priv->msg_view));
2997 tny_msg_view_clear (TNY_MSG_VIEW (priv->msg_view));
2998 tny_msg_rewrite_cache (msg);
2999 tny_msg_view_set_msg (TNY_MSG_VIEW (priv->msg_view), msg);
3000 g_object_unref (msg);
3002 g_object_unref (mime_parts);
3004 if (priv->purge_timeout > 0) {
3005 g_source_remove (priv->purge_timeout);
3006 priv->purge_timeout = 0;
3009 if (priv->remove_attachment_banner) {
3010 gtk_widget_destroy (priv->remove_attachment_banner);
3011 g_object_unref (priv->remove_attachment_banner);
3012 priv->remove_attachment_banner = NULL;
3018 update_window_title (ModestMsgViewWindow *window)
3020 ModestMsgViewWindowPrivate *priv;
3022 TnyHeader *header = NULL;
3023 gchar *subject = NULL;
3025 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
3027 /* Note that if the window is closed while we're retrieving
3028 the message, this widget could de deleted */
3029 if (!priv->msg_view)
3032 msg = tny_msg_view_get_msg (TNY_MSG_VIEW (priv->msg_view));
3035 header = tny_msg_get_header (msg);
3036 subject = tny_header_dup_subject (header);
3037 g_object_unref (header);
3038 g_object_unref (msg);
3041 if ((subject == NULL)||(subject[0] == '\0')) {
3043 subject = g_strdup (_("mail_va_no_subject"));
3046 gtk_window_set_title (GTK_WINDOW (window), subject);
3051 on_move_focus (GtkWidget *widget,
3052 GtkDirectionType direction,
3055 g_signal_stop_emission_by_name (G_OBJECT (widget), "move-focus");
3059 fetch_image_open_stream (TnyStreamCache *self, gint64 *expected_size, gchar *uri)
3061 GnomeVFSResult result;
3062 GnomeVFSHandle *handle = NULL;
3063 GnomeVFSFileInfo *info = NULL;
3066 result = gnome_vfs_open (&handle, uri, GNOME_VFS_OPEN_READ);
3067 if (result != GNOME_VFS_OK) {
3072 info = gnome_vfs_file_info_new ();
3073 result = gnome_vfs_get_file_info_from_handle (handle, info, GNOME_VFS_FILE_INFO_DEFAULT);
3074 if (result != GNOME_VFS_OK || ! (info->valid_fields & GNOME_VFS_FILE_INFO_FIELDS_SIZE)) {
3075 /* We put a "safe" default size for going to cache */
3076 *expected_size = (300*1024);
3078 *expected_size = info->size;
3080 gnome_vfs_file_info_unref (info);
3082 stream = tny_vfs_stream_new (handle);
3091 TnyStream *output_stream;
3092 GtkWidget *msg_view;
3097 on_fetch_image_idle_refresh_view (gpointer userdata)
3100 FetchImageData *fidata = (FetchImageData *) userdata;
3102 gdk_threads_enter ();
3103 if (GTK_WIDGET_DRAWABLE (fidata->msg_view)) {
3104 ModestMsgViewWindowPrivate *priv;
3106 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (fidata->window);
3107 priv->fetching_images--;
3108 gtk_widget_queue_draw (fidata->msg_view);
3109 update_progress_hint (MODEST_MSG_VIEW_WINDOW (fidata->window));
3111 gdk_threads_leave ();
3113 g_object_unref (fidata->msg_view);
3114 g_object_unref (fidata->window);
3115 g_slice_free (FetchImageData, fidata);
3120 on_fetch_image_thread (gpointer userdata)
3122 FetchImageData *fidata = (FetchImageData *) userdata;
3123 TnyStreamCache *cache;
3124 TnyStream *cache_stream;
3126 cache = modest_runtime_get_images_cache ();
3128 tny_stream_cache_get_stream (cache,
3130 (TnyStreamCacheOpenStreamFetcher) fetch_image_open_stream,
3131 (gpointer) fidata->uri);
3132 g_free (fidata->cache_id);
3133 g_free (fidata->uri);
3135 if (cache_stream != NULL) {
3138 while (G_LIKELY (!tny_stream_is_eos (cache_stream))) {
3141 nb_read = tny_stream_read (cache_stream, buffer, sizeof (buffer));
3142 if (G_UNLIKELY (nb_read < 0)) {
3144 } else if (G_LIKELY (nb_read > 0)) {
3145 gssize nb_written = 0;
3147 while (G_UNLIKELY (nb_written < nb_read)) {
3150 len = tny_stream_write (fidata->output_stream, buffer + nb_written,
3151 nb_read - nb_written);
3152 if (G_UNLIKELY (len < 0))
3158 tny_stream_close (cache_stream);
3159 g_object_unref (cache_stream);
3162 tny_stream_close (fidata->output_stream);
3163 g_object_unref (fidata->output_stream);
3165 g_idle_add (on_fetch_image_idle_refresh_view, fidata);
3171 on_fetch_image (ModestMsgView *msgview,
3174 ModestMsgViewWindow *window)
3176 const gchar *current_account;
3177 ModestMsgViewWindowPrivate *priv;
3178 FetchImageData *fidata;
3180 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
3182 current_account = modest_window_get_active_account (MODEST_WINDOW (window));
3184 fidata = g_slice_new0 (FetchImageData);
3185 fidata->msg_view = g_object_ref (msgview);
3186 fidata->window = g_object_ref (window);
3187 fidata->uri = g_strdup (uri);
3188 fidata->cache_id = modest_images_cache_get_id (current_account, uri);
3189 fidata->output_stream = g_object_ref (stream);
3191 priv->fetching_images++;
3192 if (g_thread_create (on_fetch_image_thread, fidata, FALSE, NULL) == NULL) {
3193 g_object_unref (fidata->output_stream);
3194 g_free (fidata->cache_id);
3195 g_free (fidata->uri);
3196 g_object_unref (fidata->msg_view);
3197 g_slice_free (FetchImageData, fidata);
3198 tny_stream_close (stream);
3199 priv->fetching_images--;
3200 update_progress_hint (window);
3203 update_progress_hint (window);
3209 setup_menu (ModestMsgViewWindow *self)
3211 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW(self));
3213 /* Settings menu buttons */
3214 modest_hildon2_window_add_to_menu (MODEST_HILDON2_WINDOW (self), _("mcen_me_inbox_replytoall"), NULL,
3215 APP_MENU_CALLBACK (modest_ui_actions_on_reply_all),
3216 MODEST_DIMMING_CALLBACK (modest_ui_dimming_rules_on_reply_msg));
3217 modest_hildon2_window_add_to_menu (MODEST_HILDON2_WINDOW (self), _("mcen_me_inbox_forward"), "<Control>d",
3218 APP_MENU_CALLBACK (modest_ui_actions_on_forward),
3219 MODEST_DIMMING_CALLBACK (modest_ui_dimming_rules_on_reply_msg));
3221 modest_hildon2_window_add_to_menu (MODEST_HILDON2_WINDOW (self), _("mcen_me_inbox_mark_as_read"), NULL,
3222 APP_MENU_CALLBACK (modest_ui_actions_on_mark_as_read),
3223 MODEST_DIMMING_CALLBACK (modest_ui_dimming_rules_on_mark_as_read_msg_in_view));
3224 modest_hildon2_window_add_to_menu (MODEST_HILDON2_WINDOW (self), _("mcen_me_inbox_mark_as_unread"), NULL,
3225 APP_MENU_CALLBACK (modest_ui_actions_on_mark_as_unread),
3226 MODEST_DIMMING_CALLBACK (modest_ui_dimming_rules_on_mark_as_unread_msg_in_view));
3228 modest_hildon2_window_add_to_menu (MODEST_HILDON2_WINDOW (self), _("mcen_me_viewer_save_attachments"), NULL,
3229 APP_MENU_CALLBACK (modest_ui_actions_save_attachments),
3230 MODEST_DIMMING_CALLBACK (modest_ui_dimming_rules_on_save_attachments));
3231 modest_hildon2_window_add_to_menu (MODEST_HILDON2_WINDOW (self), _("mcen_me_inbox_remove_attachments"), NULL,
3232 APP_MENU_CALLBACK (modest_ui_actions_remove_attachments),
3233 MODEST_DIMMING_CALLBACK (modest_ui_dimming_rules_on_remove_attachments));
3235 modest_hildon2_window_add_to_menu (MODEST_HILDON2_WINDOW (self), _("mcen_me_new_message"), "<Control>n",
3236 APP_MENU_CALLBACK (modest_ui_actions_on_new_msg),
3237 MODEST_DIMMING_CALLBACK (modest_ui_dimming_rules_on_new_msg));
3238 modest_hildon2_window_add_to_menu (MODEST_HILDON2_WINDOW (self), _("mcen_me_viewer_addtocontacts"), NULL,
3239 APP_MENU_CALLBACK (modest_ui_actions_add_to_contacts),
3240 MODEST_DIMMING_CALLBACK (modest_ui_dimming_rules_on_add_to_contacts));
3242 modest_hildon2_window_add_to_menu (MODEST_HILDON2_WINDOW (self), _("mail_bd_external_images"), NULL,
3243 APP_MENU_CALLBACK (modest_ui_actions_on_fetch_images),
3244 MODEST_DIMMING_CALLBACK (modest_ui_dimming_rules_on_fetch_images));
3245 modest_hildon2_window_add_to_menu (MODEST_HILDON2_WINDOW (self), _("mcen_ti_message_properties"), NULL,
3246 APP_MENU_CALLBACK (modest_ui_actions_on_details),
3247 MODEST_DIMMING_CALLBACK (modest_ui_dimming_rules_on_details));
3251 modest_msg_view_window_add_to_contacts (ModestMsgViewWindow *self)
3253 ModestMsgViewWindowPrivate *priv;
3254 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
3255 GSList *recipients = NULL;
3257 gboolean contacts_to_add = FALSE;
3259 msg = tny_msg_view_get_msg (TNY_MSG_VIEW (priv->msg_view));
3263 header = modest_msg_view_window_get_header (self);
3266 recipients = modest_tny_msg_header_get_all_recipients_list (header);
3267 g_object_unref (header);
3269 recipients = modest_tny_msg_get_all_recipients_list (msg);
3270 g_object_unref (msg);
3273 if (recipients != NULL) {
3274 GtkWidget *picker_dialog;
3275 GtkWidget *selector;
3277 gchar *selected = NULL;
3279 selector = hildon_touch_selector_new_text ();
3280 g_object_ref (selector);
3282 for (node = recipients; node != NULL; node = g_slist_next (node)) {
3283 if (!modest_address_book_has_address ((const gchar *) node->data)) {
3284 hildon_touch_selector_append_text (HILDON_TOUCH_SELECTOR (selector),
3285 (const gchar *) node->data);
3286 contacts_to_add = TRUE;
3290 if (contacts_to_add) {
3293 picker_dialog = hildon_picker_dialog_new (GTK_WINDOW (self));
3294 gtk_window_set_title (GTK_WINDOW (picker_dialog), _("mcen_me_viewer_addtocontacts"));
3296 hildon_picker_dialog_set_selector (HILDON_PICKER_DIALOG (picker_dialog),
3297 HILDON_TOUCH_SELECTOR (selector));
3299 picker_result = gtk_dialog_run (GTK_DIALOG (picker_dialog));
3301 if (picker_result == GTK_RESPONSE_OK) {
3302 selected = hildon_touch_selector_get_current_text (HILDON_TOUCH_SELECTOR (selector));
3304 gtk_widget_destroy (picker_dialog);
3307 modest_address_book_add_address (selected);
3312 g_object_unref (selector);
3317 if (recipients) {g_slist_foreach (recipients, (GFunc) g_free, NULL); g_slist_free (recipients);}
3321 _modest_msg_view_window_map_event (GtkWidget *widget,
3325 ModestMsgViewWindow *self = (ModestMsgViewWindow *) userdata;
3327 update_progress_hint (self);
3333 modest_msg_view_window_fetch_images (ModestMsgViewWindow *self)
3335 ModestMsgViewWindowPrivate *priv;
3336 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
3338 modest_msg_view_request_fetch_images (MODEST_MSG_VIEW (priv->msg_view));
3342 modest_msg_view_window_has_blocked_external_images (ModestMsgViewWindow *self)
3344 ModestMsgViewWindowPrivate *priv;
3345 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
3347 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self), FALSE);
3349 return modest_msg_view_has_blocked_external_images (MODEST_MSG_VIEW (priv->msg_view));
3353 modest_msg_view_window_reload (ModestMsgViewWindow *self)
3355 ModestMsgViewWindowPrivate *priv;
3358 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self));
3360 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
3361 header = modest_msg_view_window_get_header (MODEST_MSG_VIEW_WINDOW (self));
3363 if (!message_reader (self, priv, header, priv->row_reference)) {
3364 g_warning ("Shouldn't happen, trying to reload a message failed");
3367 g_object_unref (header);