1 /* Copyright (c) 2006, Nokia Corporation
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions are
8 * * Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 * * Redistributions in binary form must reproduce the above copyright
11 * notice, this list of conditions and the following disclaimer in the
12 * documentation and/or other materials provided with the distribution.
13 * * Neither the name of the Nokia Corporation nor the names of its
14 * contributors may be used to endorse or promote products derived from
15 * this software without specific prior written permission.
17 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
18 * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
19 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
20 * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
21 * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
22 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
23 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
24 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
25 * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
26 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
27 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29 #include <glib/gi18n.h>
31 #include <tny-account-store.h>
32 #include <tny-simple-list.h>
34 #include <tny-mime-part.h>
35 #include <tny-vfs-stream.h>
36 #include "modest-marshal.h"
37 #include "modest-platform.h"
38 #include <modest-utils.h>
39 #include <modest-maemo-utils.h>
40 #include <modest-tny-msg.h>
41 #include <modest-msg-view-window.h>
42 #include <modest-main-window-ui.h>
43 #include "modest-msg-view-window-ui-dimming.h"
44 #include <modest-widget-memory.h>
45 #include <modest-progress-object.h>
46 #include <modest-runtime.h>
47 #include <modest-window-priv.h>
48 #include <modest-tny-folder.h>
49 #include <modest-text-utils.h>
50 #include <modest-account-mgr-helpers.h>
51 #include <hildon/hildon-pannable-area.h>
52 #include <hildon/hildon-picker-dialog.h>
53 #include <hildon/hildon-app-menu.h>
54 #include "modest-defs.h"
55 #include "modest-hildon-includes.h"
56 #include "modest-ui-dimming-manager.h"
57 #include <gdk/gdkkeysyms.h>
58 #include <modest-tny-account.h>
59 #include <modest-mime-part-view.h>
60 #include <modest-isearch-view.h>
61 #include <modest-tny-mime-part.h>
62 #include <modest-address-book.h>
65 #include <glib/gstdio.h>
66 #include <modest-debug.h>
67 #include <modest-header-window.h>
69 #define MYDOCS_ENV "MYDOCSDIR"
70 #define DOCS_FOLDER ".documents"
72 typedef struct _ModestMsgViewWindowPrivate ModestMsgViewWindowPrivate;
73 struct _ModestMsgViewWindowPrivate {
76 GtkWidget *main_scroll;
77 GtkWidget *find_toolbar;
80 /* Progress observers */
81 GSList *progress_widgets;
84 GtkWidget *prev_toolitem;
85 GtkWidget *next_toolitem;
86 gboolean progress_hint;
88 /* Optimized view enabled */
89 gboolean optimized_view;
91 /* Whether this was created via the *_new_for_search_result() function. */
92 gboolean is_search_result;
94 /* Whether the message is in outbox */
97 /* A reference to the @model of the header view
98 * to allow selecting previous/next messages,
99 * if the message is currently selected in the header view.
101 const gchar *header_folder_id;
102 GtkTreeModel *header_model;
103 GtkTreeRowReference *row_reference;
104 GtkTreeRowReference *next_row_reference;
106 gulong clipboard_change_handler;
107 gulong queue_change_handler;
108 gulong account_removed_handler;
109 gulong row_changed_handler;
110 gulong row_deleted_handler;
111 gulong row_inserted_handler;
112 gulong rows_reordered_handler;
115 GtkWidget *remove_attachment_banner;
122 static void modest_msg_view_window_class_init (ModestMsgViewWindowClass *klass);
123 static void modest_msg_view_window_init (ModestMsgViewWindow *obj);
124 static void modest_header_view_observer_init(
125 ModestHeaderViewObserverIface *iface_class);
126 static void modest_msg_view_window_finalize (GObject *obj);
127 static void modest_msg_view_window_toggle_find_toolbar (GtkToggleAction *obj,
129 static void modest_msg_view_window_find_toolbar_close (GtkWidget *widget,
130 ModestMsgViewWindow *obj);
131 static void modest_msg_view_window_find_toolbar_search (GtkWidget *widget,
132 ModestMsgViewWindow *obj);
134 static void modest_msg_view_window_disconnect_signals (ModestWindow *self);
136 static gdouble modest_msg_view_window_get_zoom (ModestWindow *window);
137 static void modest_msg_view_window_set_zoom (ModestWindow *window,
139 static gboolean modest_msg_view_window_zoom_minus (ModestWindow *window);
140 static gboolean modest_msg_view_window_zoom_plus (ModestWindow *window);
141 static gboolean modest_msg_view_window_key_event (GtkWidget *window,
144 static gboolean modest_msg_view_window_toggle_menu (HildonWindow *window,
147 static void modest_msg_view_window_update_priority (ModestMsgViewWindow *window);
149 static void modest_msg_view_window_show_toolbar (ModestWindow *window,
150 gboolean show_toolbar);
152 static void modest_msg_view_window_clipboard_owner_change (GtkClipboard *clipboard,
154 ModestMsgViewWindow *window);
156 static void modest_msg_view_window_on_row_changed (GtkTreeModel *header_model,
159 ModestMsgViewWindow *window);
161 static void modest_msg_view_window_on_row_deleted (GtkTreeModel *header_model,
163 ModestMsgViewWindow *window);
165 static void modest_msg_view_window_on_row_inserted (GtkTreeModel *header_model,
166 GtkTreePath *tree_path,
167 GtkTreeIter *tree_iter,
168 ModestMsgViewWindow *window);
170 static void modest_msg_view_window_on_row_reordered (GtkTreeModel *header_model,
174 ModestMsgViewWindow *window);
176 static void modest_msg_view_window_update_model_replaced (ModestHeaderViewObserver *window,
178 const gchar *tny_folder_id);
180 static void on_queue_changed (ModestMailOperationQueue *queue,
181 ModestMailOperation *mail_op,
182 ModestMailOperationQueueNotification type,
183 ModestMsgViewWindow *self);
185 static void on_account_removed (TnyAccountStore *account_store,
189 static void on_move_focus (GtkWidget *widget,
190 GtkDirectionType direction,
193 static void view_msg_cb (ModestMailOperation *mail_op,
200 static void set_progress_hint (ModestMsgViewWindow *self,
203 static void update_window_title (ModestMsgViewWindow *window);
205 static gboolean set_toolbar_transfer_mode (ModestMsgViewWindow *self);
206 static void init_window (ModestMsgViewWindow *obj);
208 static gboolean msg_is_visible (TnyHeader *header, gboolean check_outbox);
210 static void check_dimming_rules_after_change (ModestMsgViewWindow *window);
212 static gboolean on_fetch_image (ModestMsgView *msgview,
215 ModestMsgViewWindow *window);
217 static gboolean modest_msg_view_window_scroll_child (ModestMsgViewWindow *self,
218 GtkScrollType scroll_type,
221 static gboolean message_reader (ModestMsgViewWindow *window,
222 ModestMsgViewWindowPrivate *priv,
224 GtkTreeRowReference *row_reference);
226 static void add_to_menu (ModestMsgViewWindow *self,
230 ModestDimmingRulesGroup *group,
231 GCallback dimming_callback);
232 static void setup_menu (ModestMsgViewWindow *self,
233 ModestDimmingRulesGroup *group);
235 /* list my signals */
242 static const GtkToggleActionEntry msg_view_toggle_action_entries [] = {
243 { "FindInMessage", MODEST_TOOLBAR_ICON_FIND, N_("qgn_toolb_gene_find"), NULL, NULL, G_CALLBACK (modest_msg_view_window_toggle_find_toolbar), FALSE },
247 #define MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE((o), \
248 MODEST_TYPE_MSG_VIEW_WINDOW, \
249 ModestMsgViewWindowPrivate))
251 static GtkWindowClass *parent_class = NULL;
253 /* uncomment the following if you have defined any signals */
254 static guint signals[LAST_SIGNAL] = {0};
257 modest_msg_view_window_get_type (void)
259 static GType my_type = 0;
261 static const GTypeInfo my_info = {
262 sizeof(ModestMsgViewWindowClass),
263 NULL, /* base init */
264 NULL, /* base finalize */
265 (GClassInitFunc) modest_msg_view_window_class_init,
266 NULL, /* class finalize */
267 NULL, /* class data */
268 sizeof(ModestMsgViewWindow),
270 (GInstanceInitFunc) modest_msg_view_window_init,
273 my_type = g_type_register_static (MODEST_TYPE_WINDOW,
274 "ModestMsgViewWindow",
277 static const GInterfaceInfo modest_header_view_observer_info =
279 (GInterfaceInitFunc) modest_header_view_observer_init,
280 NULL, /* interface_finalize */
281 NULL /* interface_data */
284 g_type_add_interface_static (my_type,
285 MODEST_TYPE_HEADER_VIEW_OBSERVER,
286 &modest_header_view_observer_info);
292 save_state (ModestWindow *self)
294 modest_widget_memory_save (modest_runtime_get_conf (),
296 MODEST_CONF_MSG_VIEW_WINDOW_KEY);
300 gboolean modest_msg_view_window_scroll_child (ModestMsgViewWindow *self,
301 GtkScrollType scroll_type,
305 ModestMsgViewWindowPrivate *priv;
306 gboolean return_value;
308 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(self);
309 g_signal_emit_by_name (priv->main_scroll, "scroll-child", scroll_type, horizontal, &return_value);
314 add_scroll_binding (GtkBindingSet *binding_set,
316 GtkScrollType scroll)
318 guint keypad_keyval = keyval - GDK_Left + GDK_KP_Left;
320 gtk_binding_entry_add_signal (binding_set, keyval, 0,
322 GTK_TYPE_SCROLL_TYPE, scroll,
323 G_TYPE_BOOLEAN, FALSE);
324 gtk_binding_entry_add_signal (binding_set, keypad_keyval, 0,
326 GTK_TYPE_SCROLL_TYPE, scroll,
327 G_TYPE_BOOLEAN, FALSE);
331 modest_msg_view_window_class_init (ModestMsgViewWindowClass *klass)
333 GObjectClass *gobject_class;
334 HildonWindowClass *hildon_window_class;
335 ModestWindowClass *modest_window_class;
336 GtkBindingSet *binding_set;
338 gobject_class = (GObjectClass*) klass;
339 hildon_window_class = (HildonWindowClass *) klass;
340 modest_window_class = (ModestWindowClass *) klass;
342 parent_class = g_type_class_peek_parent (klass);
343 gobject_class->finalize = modest_msg_view_window_finalize;
345 hildon_window_class->toggle_menu = modest_msg_view_window_toggle_menu;
347 modest_window_class->set_zoom_func = modest_msg_view_window_set_zoom;
348 modest_window_class->get_zoom_func = modest_msg_view_window_get_zoom;
349 modest_window_class->zoom_plus_func = modest_msg_view_window_zoom_plus;
350 modest_window_class->zoom_minus_func = modest_msg_view_window_zoom_minus;
351 modest_window_class->show_toolbar_func = modest_msg_view_window_show_toolbar;
352 modest_window_class->disconnect_signals_func = modest_msg_view_window_disconnect_signals;
354 modest_window_class->save_state_func = save_state;
356 klass->scroll_child = modest_msg_view_window_scroll_child;
358 signals[MSG_CHANGED_SIGNAL] =
359 g_signal_new ("msg-changed",
360 G_TYPE_FROM_CLASS (gobject_class),
362 G_STRUCT_OFFSET (ModestMsgViewWindowClass, msg_changed),
364 modest_marshal_VOID__POINTER_POINTER,
365 G_TYPE_NONE, 2, G_TYPE_POINTER, G_TYPE_POINTER);
367 signals[SCROLL_CHILD_SIGNAL] =
368 g_signal_new ("scroll-child",
369 G_TYPE_FROM_CLASS (gobject_class),
370 G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
371 G_STRUCT_OFFSET (ModestMsgViewWindowClass, scroll_child),
373 modest_marshal_BOOLEAN__ENUM_BOOLEAN,
374 G_TYPE_BOOLEAN, 2, GTK_TYPE_SCROLL_TYPE, G_TYPE_BOOLEAN);
376 binding_set = gtk_binding_set_by_class (klass);
377 add_scroll_binding (binding_set, GDK_Up, GTK_SCROLL_STEP_UP);
378 add_scroll_binding (binding_set, GDK_Down, GTK_SCROLL_STEP_DOWN);
379 add_scroll_binding (binding_set, GDK_Page_Up, GTK_SCROLL_PAGE_UP);
380 add_scroll_binding (binding_set, GDK_Page_Down, GTK_SCROLL_PAGE_DOWN);
381 add_scroll_binding (binding_set, GDK_Home, GTK_SCROLL_START);
382 add_scroll_binding (binding_set, GDK_End, GTK_SCROLL_END);
384 g_type_class_add_private (gobject_class, sizeof(ModestMsgViewWindowPrivate));
388 static void modest_header_view_observer_init(
389 ModestHeaderViewObserverIface *iface_class)
391 iface_class->update_func = modest_msg_view_window_update_model_replaced;
395 modest_msg_view_window_init (ModestMsgViewWindow *obj)
397 ModestMsgViewWindowPrivate *priv;
398 ModestWindowPrivate *parent_priv = NULL;
399 GtkActionGroup *action_group = NULL;
400 GError *error = NULL;
401 GdkPixbuf *window_icon;
403 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(obj);
404 parent_priv = MODEST_WINDOW_GET_PRIVATE(obj);
405 parent_priv->ui_manager = gtk_ui_manager_new();
407 action_group = gtk_action_group_new ("ModestMsgViewWindowActions");
408 gtk_action_group_set_translation_domain (action_group, GETTEXT_PACKAGE);
410 /* Add common actions */
411 gtk_action_group_add_actions (action_group,
412 modest_action_entries,
413 G_N_ELEMENTS (modest_action_entries),
415 gtk_action_group_add_toggle_actions (action_group,
416 msg_view_toggle_action_entries,
417 G_N_ELEMENTS (msg_view_toggle_action_entries),
420 gtk_ui_manager_insert_action_group (parent_priv->ui_manager, action_group, 0);
421 g_object_unref (action_group);
423 /* Load the UI definition */
424 gtk_ui_manager_add_ui_from_file (parent_priv->ui_manager, MODEST_UIDIR "modest-msg-view-window-ui.xml",
427 g_printerr ("modest: could not merge modest-msg-view-window-ui.xml: %s\n", error->message);
428 g_error_free (error);
433 /* Add accelerators */
434 gtk_window_add_accel_group (GTK_WINDOW (obj),
435 gtk_ui_manager_get_accel_group (parent_priv->ui_manager));
437 priv->is_search_result = FALSE;
438 priv->is_outbox = FALSE;
440 priv->msg_view = NULL;
441 priv->header_model = NULL;
442 priv->header_folder_id = NULL;
443 priv->clipboard_change_handler = 0;
444 priv->queue_change_handler = 0;
445 priv->account_removed_handler = 0;
446 priv->row_changed_handler = 0;
447 priv->row_deleted_handler = 0;
448 priv->row_inserted_handler = 0;
449 priv->rows_reordered_handler = 0;
450 priv->progress_hint = FALSE;
452 priv->optimized_view = FALSE;
453 priv->purge_timeout = 0;
454 priv->remove_attachment_banner = NULL;
455 priv->msg_uid = NULL;
457 priv->sighandlers = NULL;
460 init_window (MODEST_MSG_VIEW_WINDOW(obj));
462 /* Set window icon */
463 window_icon = modest_platform_get_icon (MODEST_APP_MSG_VIEW_ICON, MODEST_ICON_SIZE_BIG);
465 gtk_window_set_icon (GTK_WINDOW (obj), window_icon);
466 g_object_unref (window_icon);
469 hildon_program_add_window (hildon_program_get_instance(),
476 set_toolbar_transfer_mode (ModestMsgViewWindow *self)
478 ModestMsgViewWindowPrivate *priv = NULL;
480 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self), FALSE);
482 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(self);
484 set_progress_hint (self, TRUE);
490 set_progress_hint (ModestMsgViewWindow *self,
493 ModestWindowPrivate *parent_priv;
494 ModestMsgViewWindowPrivate *priv;
496 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self));
498 parent_priv = MODEST_WINDOW_GET_PRIVATE(self);
499 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(self);
501 /* Sets current progress hint */
502 priv->progress_hint = enabled;
504 if (GTK_WIDGET_VISIBLE (self)) {
505 hildon_gtk_window_set_progress_indicator (GTK_WINDOW (self), enabled?1:0);
512 init_window (ModestMsgViewWindow *obj)
514 GtkWidget *main_vbox;
515 ModestMsgViewWindowPrivate *priv;
517 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(obj);
519 priv->msg_view = GTK_WIDGET (tny_platform_factory_new_msg_view (modest_tny_platform_factory_get_instance ()));
520 modest_msg_view_set_shadow_type (MODEST_MSG_VIEW (priv->msg_view), GTK_SHADOW_NONE);
521 main_vbox = gtk_vbox_new (FALSE, 6);
522 #ifdef MODEST_TOOLKIT_HILDON2
523 priv->main_scroll = hildon_pannable_area_new ();
524 gtk_container_add (GTK_CONTAINER (priv->main_scroll), priv->msg_view);
526 #ifdef MODEST_USE_MOZEMBED
527 priv->main_scroll = priv->msg_view;
528 gtk_widget_set_size_request (priv->msg_view, -1, 1600);
530 priv->main_scroll = gtk_scrolled_window_new (NULL, NULL);
531 gtk_container_add (GTK_CONTAINER (priv->main_scroll), priv->msg_view);
533 gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (priv->main_scroll), GTK_POLICY_NEVER, GTK_POLICY_AUTOMATIC);
534 gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW (priv->main_scroll), GTK_SHADOW_NONE);
535 modest_maemo_set_thumbable_scrollbar (GTK_SCROLLED_WINDOW(priv->main_scroll), TRUE);
538 gtk_box_pack_start (GTK_BOX(main_vbox), priv->main_scroll, TRUE, TRUE, 0);
539 gtk_container_add (GTK_CONTAINER(obj), main_vbox);
541 priv->find_toolbar = hildon_find_toolbar_new (NULL);
542 hildon_window_add_toolbar (HILDON_WINDOW (obj), GTK_TOOLBAR (priv->find_toolbar));
543 gtk_widget_set_no_show_all (priv->find_toolbar, TRUE);
545 gtk_widget_show_all (GTK_WIDGET(main_vbox));
549 modest_msg_view_window_disconnect_signals (ModestWindow *self)
551 ModestMsgViewWindowPrivate *priv;
552 GtkWidget *header_view = NULL;
553 GtkWindow *parent_window = NULL;
555 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
557 if (gtk_clipboard_get (GDK_SELECTION_PRIMARY) &&
558 g_signal_handler_is_connected (gtk_clipboard_get (GDK_SELECTION_PRIMARY),
559 priv->clipboard_change_handler))
560 g_signal_handler_disconnect (gtk_clipboard_get (GDK_SELECTION_PRIMARY),
561 priv->clipboard_change_handler);
563 if (g_signal_handler_is_connected (G_OBJECT (modest_runtime_get_mail_operation_queue ()),
564 priv->queue_change_handler))
565 g_signal_handler_disconnect (G_OBJECT (modest_runtime_get_mail_operation_queue ()),
566 priv->queue_change_handler);
568 if (g_signal_handler_is_connected (G_OBJECT (modest_runtime_get_account_store ()),
569 priv->account_removed_handler))
570 g_signal_handler_disconnect (G_OBJECT (modest_runtime_get_account_store ()),
571 priv->account_removed_handler);
573 if (priv->header_model) {
574 if (g_signal_handler_is_connected(G_OBJECT (priv->header_model),
575 priv->row_changed_handler))
576 g_signal_handler_disconnect(G_OBJECT (priv->header_model),
577 priv->row_changed_handler);
579 if (g_signal_handler_is_connected(G_OBJECT (priv->header_model),
580 priv->row_deleted_handler))
581 g_signal_handler_disconnect(G_OBJECT (priv->header_model),
582 priv->row_deleted_handler);
584 if (g_signal_handler_is_connected(G_OBJECT (priv->header_model),
585 priv->row_inserted_handler))
586 g_signal_handler_disconnect(G_OBJECT (priv->header_model),
587 priv->row_inserted_handler);
589 if (g_signal_handler_is_connected(G_OBJECT (priv->header_model),
590 priv->rows_reordered_handler))
591 g_signal_handler_disconnect(G_OBJECT (priv->header_model),
592 priv->rows_reordered_handler);
595 modest_signal_mgr_disconnect_all_and_destroy (priv->sighandlers);
596 priv->sighandlers = NULL;
598 parent_window = gtk_window_get_transient_for (GTK_WINDOW (self));
599 if (parent_window && MODEST_IS_HEADER_WINDOW (parent_window)) {
600 header_view = GTK_WIDGET (modest_header_window_get_header_view (MODEST_HEADER_WINDOW (parent_window)));
602 modest_header_view_remove_observer(MODEST_HEADER_VIEW (header_view),
603 MODEST_HEADER_VIEW_OBSERVER(self));
609 modest_msg_view_window_finalize (GObject *obj)
611 ModestMsgViewWindowPrivate *priv;
613 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (obj);
615 /* Sanity check: shouldn't be needed, the window mgr should
616 call this function before */
617 modest_msg_view_window_disconnect_signals (MODEST_WINDOW (obj));
619 if (priv->header_model != NULL) {
620 g_object_unref (priv->header_model);
621 priv->header_model = NULL;
624 if (priv->remove_attachment_banner) {
625 gtk_widget_destroy (priv->remove_attachment_banner);
626 g_object_unref (priv->remove_attachment_banner);
627 priv->remove_attachment_banner = NULL;
630 if (priv->purge_timeout > 0) {
631 g_source_remove (priv->purge_timeout);
632 priv->purge_timeout = 0;
635 if (priv->row_reference) {
636 gtk_tree_row_reference_free (priv->row_reference);
637 priv->row_reference = NULL;
640 if (priv->next_row_reference) {
641 gtk_tree_row_reference_free (priv->next_row_reference);
642 priv->next_row_reference = NULL;
646 g_free (priv->msg_uid);
647 priv->msg_uid = NULL;
650 G_OBJECT_CLASS(parent_class)->finalize (obj);
654 select_next_valid_row (GtkTreeModel *model,
655 GtkTreeRowReference **row_reference,
659 GtkTreeIter tmp_iter;
661 GtkTreePath *next = NULL;
662 gboolean retval = FALSE, finished;
664 g_return_val_if_fail (gtk_tree_row_reference_valid (*row_reference), FALSE);
666 path = gtk_tree_row_reference_get_path (*row_reference);
667 gtk_tree_model_get_iter (model, &tmp_iter, path);
668 gtk_tree_row_reference_free (*row_reference);
669 *row_reference = NULL;
673 TnyHeader *header = NULL;
675 if (gtk_tree_model_iter_next (model, &tmp_iter)) {
676 gtk_tree_model_get (model, &tmp_iter,
677 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
681 if (msg_is_visible (header, is_outbox)) {
682 next = gtk_tree_model_get_path (model, &tmp_iter);
683 *row_reference = gtk_tree_row_reference_new (model, next);
684 gtk_tree_path_free (next);
688 g_object_unref (header);
691 } else if (cycle && gtk_tree_model_get_iter_first (model, &tmp_iter)) {
692 next = gtk_tree_model_get_path (model, &tmp_iter);
694 /* Ensure that we are not selecting the same */
695 if (gtk_tree_path_compare (path, next) != 0) {
696 gtk_tree_model_get (model, &tmp_iter,
697 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
700 if (msg_is_visible (header, is_outbox)) {
701 *row_reference = gtk_tree_row_reference_new (model, next);
705 g_object_unref (header);
709 /* If we ended up in the same message
710 then there is no valid next
714 gtk_tree_path_free (next);
716 /* If there are no more messages and we don't
717 want to start again in the first one then
718 there is no valid next message */
724 gtk_tree_path_free (path);
729 /* TODO: This should be in _init(), with the parameters as properties. */
731 modest_msg_view_window_construct (ModestMsgViewWindow *self,
732 const gchar *modest_account_name,
733 const gchar *msg_uid)
736 ModestMsgViewWindowPrivate *priv = NULL;
737 ModestWindowPrivate *parent_priv = NULL;
738 ModestDimmingRulesGroup *menu_rules_group = NULL;
739 ModestDimmingRulesGroup *toolbar_rules_group = NULL;
740 ModestDimmingRulesGroup *clipboard_rules_group = NULL;
742 obj = G_OBJECT (self);
743 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(obj);
744 parent_priv = MODEST_WINDOW_GET_PRIVATE(obj);
746 priv->msg_uid = g_strdup (msg_uid);
749 parent_priv->menubar = NULL;
750 parent_priv->ui_dimming_manager = modest_ui_dimming_manager_new();
752 menu_rules_group = modest_dimming_rules_group_new (MODEST_DIMMING_RULES_MENU, FALSE);
753 toolbar_rules_group = modest_dimming_rules_group_new (MODEST_DIMMING_RULES_TOOLBAR, TRUE);
754 clipboard_rules_group = modest_dimming_rules_group_new (MODEST_DIMMING_RULES_CLIPBOARD, FALSE);
756 setup_menu (self, menu_rules_group);
757 /* Add common dimming rules */
758 modest_dimming_rules_group_add_rules (menu_rules_group,
759 modest_msg_view_menu_dimming_entries,
760 G_N_ELEMENTS (modest_msg_view_menu_dimming_entries),
761 MODEST_WINDOW (self));
762 modest_dimming_rules_group_add_rules (toolbar_rules_group,
763 modest_msg_view_toolbar_dimming_entries,
764 G_N_ELEMENTS (modest_msg_view_toolbar_dimming_entries),
765 MODEST_WINDOW (self));
766 modest_dimming_rules_group_add_rules (clipboard_rules_group,
767 modest_msg_view_clipboard_dimming_entries,
768 G_N_ELEMENTS (modest_msg_view_clipboard_dimming_entries),
769 MODEST_WINDOW (self));
771 /* Insert dimming rules group for this window */
772 modest_ui_dimming_manager_insert_rules_group (parent_priv->ui_dimming_manager, menu_rules_group);
773 modest_ui_dimming_manager_insert_rules_group (parent_priv->ui_dimming_manager, toolbar_rules_group);
774 modest_ui_dimming_manager_insert_rules_group (parent_priv->ui_dimming_manager, clipboard_rules_group);
775 g_object_unref (menu_rules_group);
776 g_object_unref (toolbar_rules_group);
777 g_object_unref (clipboard_rules_group);
779 /* g_signal_connect (G_OBJECT(obj), "delete-event", G_CALLBACK(on_delete_event), obj); */
781 priv->clipboard_change_handler = g_signal_connect (G_OBJECT (gtk_clipboard_get (GDK_SELECTION_PRIMARY)), "owner-change", G_CALLBACK (modest_msg_view_window_clipboard_owner_change), obj);
782 g_signal_connect (G_OBJECT(priv->msg_view), "activate_link",
783 G_CALLBACK (modest_ui_actions_on_msg_link_clicked), obj);
784 g_signal_connect (G_OBJECT(priv->msg_view), "link_hover",
785 G_CALLBACK (modest_ui_actions_on_msg_link_hover), obj);
786 g_signal_connect (G_OBJECT(priv->msg_view), "attachment_clicked",
787 G_CALLBACK (modest_ui_actions_on_msg_attachment_clicked), obj);
788 g_signal_connect (G_OBJECT(priv->msg_view), "recpt_activated",
789 G_CALLBACK (modest_ui_actions_on_msg_recpt_activated), obj);
790 g_signal_connect (G_OBJECT(priv->msg_view), "show_details",
791 G_CALLBACK (modest_ui_actions_on_details), obj);
792 g_signal_connect (G_OBJECT(priv->msg_view), "link_contextual",
793 G_CALLBACK (modest_ui_actions_on_msg_link_contextual), obj);
794 g_signal_connect (G_OBJECT (priv->msg_view), "fetch_image",
795 G_CALLBACK (on_fetch_image), obj);
797 g_signal_connect (G_OBJECT (obj), "key-release-event",
798 G_CALLBACK (modest_msg_view_window_key_event),
801 g_signal_connect (G_OBJECT (obj), "key-press-event",
802 G_CALLBACK (modest_msg_view_window_key_event),
805 g_signal_connect (G_OBJECT (obj), "move-focus",
806 G_CALLBACK (on_move_focus), obj);
808 /* Mail Operation Queue */
809 priv->queue_change_handler = g_signal_connect (G_OBJECT (modest_runtime_get_mail_operation_queue ()),
811 G_CALLBACK (on_queue_changed),
814 /* Account manager */
815 priv->account_removed_handler = g_signal_connect (G_OBJECT (modest_runtime_get_account_store ()),
817 G_CALLBACK(on_account_removed),
820 modest_window_set_active_account (MODEST_WINDOW(obj), modest_account_name);
822 g_signal_connect (G_OBJECT (priv->find_toolbar), "close", G_CALLBACK (modest_msg_view_window_find_toolbar_close), obj);
823 g_signal_connect (G_OBJECT (priv->find_toolbar), "search", G_CALLBACK (modest_msg_view_window_find_toolbar_search), obj);
824 priv->last_search = NULL;
826 modest_msg_view_window_show_toolbar (MODEST_WINDOW (obj), TRUE);
828 /* Init the clipboard actions dim status */
829 modest_msg_view_grab_focus(MODEST_MSG_VIEW (priv->msg_view));
831 update_window_title (MODEST_MSG_VIEW_WINDOW (obj));
836 /* FIXME: parameter checks */
838 modest_msg_view_window_new_with_header_model (TnyMsg *msg,
839 const gchar *modest_account_name,
840 const gchar *msg_uid,
842 GtkTreeRowReference *row_reference)
844 ModestMsgViewWindow *window = NULL;
845 ModestMsgViewWindowPrivate *priv = NULL;
846 TnyFolder *header_folder = NULL;
847 ModestHeaderView *header_view = NULL;
848 ModestWindow *main_window = NULL;
849 ModestWindowMgr *mgr = NULL;
852 modest_tny_mime_part_to_string (TNY_MIME_PART (msg), 0);
855 mgr = modest_runtime_get_window_mgr ();
856 window = MODEST_MSG_VIEW_WINDOW (modest_window_mgr_get_msg_view_window (mgr));
857 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), NULL);
859 modest_msg_view_window_construct (window, modest_account_name, msg_uid);
861 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
863 /* Remember the message list's TreeModel so we can detect changes
864 * and change the list selection when necessary: */
866 main_window = modest_window_mgr_get_main_window(mgr, FALSE); /* don't create */
868 header_view = MODEST_HEADER_VIEW(modest_main_window_get_child_widget(
869 MODEST_MAIN_WINDOW(main_window),
870 MODEST_MAIN_WINDOW_WIDGET_TYPE_HEADER_VIEW));
873 if (header_view != NULL){
874 header_folder = modest_header_view_get_folder(header_view);
875 /* This could happen if the header folder was
876 unseleted before opening this msg window (for
877 example if the user selects an account in the
878 folder view of the main window */
880 priv->is_outbox = (modest_tny_folder_guess_folder_type (header_folder) == TNY_FOLDER_TYPE_OUTBOX);
881 priv->header_folder_id = tny_folder_get_id(header_folder);
882 g_assert(priv->header_folder_id != NULL);
883 g_object_unref(header_folder);
887 /* Setup row references and connect signals */
888 priv->header_model = g_object_ref (model);
891 priv->row_reference = gtk_tree_row_reference_copy (row_reference);
892 priv->next_row_reference = gtk_tree_row_reference_copy (row_reference);
893 select_next_valid_row (model, &(priv->next_row_reference), TRUE, priv->is_outbox);
895 priv->row_reference = NULL;
896 priv->next_row_reference = NULL;
899 /* Connect signals */
900 priv->row_changed_handler =
901 g_signal_connect (GTK_TREE_MODEL(model), "row-changed",
902 G_CALLBACK(modest_msg_view_window_on_row_changed),
904 priv->row_deleted_handler =
905 g_signal_connect (GTK_TREE_MODEL(model), "row-deleted",
906 G_CALLBACK(modest_msg_view_window_on_row_deleted),
908 priv->row_inserted_handler =
909 g_signal_connect (GTK_TREE_MODEL(model), "row-inserted",
910 G_CALLBACK(modest_msg_view_window_on_row_inserted),
912 priv->rows_reordered_handler =
913 g_signal_connect(GTK_TREE_MODEL(model), "rows-reordered",
914 G_CALLBACK(modest_msg_view_window_on_row_reordered),
917 if (header_view != NULL){
918 modest_header_view_add_observer(header_view,
919 MODEST_HEADER_VIEW_OBSERVER(window));
922 tny_msg_view_set_msg (TNY_MSG_VIEW (priv->msg_view), msg);
923 update_window_title (MODEST_MSG_VIEW_WINDOW (window));
925 /* gtk_widget_show_all (GTK_WIDGET (window)); */
926 modest_msg_view_window_update_priority (window);
927 /* Check dimming rules */
928 modest_ui_actions_check_toolbar_dimming_rules (MODEST_WINDOW (window));
929 modest_ui_actions_check_menu_dimming_rules (MODEST_WINDOW (window));
930 modest_window_check_dimming_rules_group (MODEST_WINDOW (window), MODEST_DIMMING_RULES_CLIPBOARD);
932 return MODEST_WINDOW(window);
936 modest_msg_view_window_new_from_header_view (ModestHeaderView *header_view,
937 const gchar *modest_account_name,
938 const gchar *msg_uid,
939 GtkTreeRowReference *row_reference)
941 ModestMsgViewWindow *window = NULL;
942 ModestMsgViewWindowPrivate *priv = NULL;
943 TnyFolder *header_folder = NULL;
944 ModestWindowMgr *mgr = NULL;
948 mgr = modest_runtime_get_window_mgr ();
949 window = MODEST_MSG_VIEW_WINDOW (modest_window_mgr_get_msg_view_window (mgr));
950 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), NULL);
952 modest_msg_view_window_construct (window, modest_account_name, msg_uid);
954 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
956 /* Remember the message list's TreeModel so we can detect changes
957 * and change the list selection when necessary: */
959 if (header_view != NULL){
960 header_folder = modest_header_view_get_folder(header_view);
961 /* This could happen if the header folder was
962 unseleted before opening this msg window (for
963 example if the user selects an account in the
964 folder view of the main window */
966 priv->is_outbox = (modest_tny_folder_guess_folder_type (header_folder) == TNY_FOLDER_TYPE_OUTBOX);
967 priv->header_folder_id = tny_folder_get_id(header_folder);
968 g_assert(priv->header_folder_id != NULL);
969 g_object_unref(header_folder);
973 /* Setup row references and connect signals */
974 priv->header_model = gtk_tree_view_get_model (GTK_TREE_VIEW (header_view));
977 priv->row_reference = gtk_tree_row_reference_copy (row_reference);
978 priv->next_row_reference = gtk_tree_row_reference_copy (row_reference);
979 select_next_valid_row (priv->header_model, &(priv->next_row_reference), TRUE, priv->is_outbox);
981 priv->row_reference = NULL;
982 priv->next_row_reference = NULL;
985 /* Connect signals */
986 priv->row_changed_handler =
987 g_signal_connect (GTK_TREE_MODEL(priv->header_model), "row-changed",
988 G_CALLBACK(modest_msg_view_window_on_row_changed),
990 priv->row_deleted_handler =
991 g_signal_connect (GTK_TREE_MODEL(priv->header_model), "row-deleted",
992 G_CALLBACK(modest_msg_view_window_on_row_deleted),
994 priv->row_inserted_handler =
995 g_signal_connect (GTK_TREE_MODEL(priv->header_model), "row-inserted",
996 G_CALLBACK(modest_msg_view_window_on_row_inserted),
998 priv->rows_reordered_handler =
999 g_signal_connect(GTK_TREE_MODEL(priv->header_model), "rows-reordered",
1000 G_CALLBACK(modest_msg_view_window_on_row_reordered),
1003 if (header_view != NULL){
1004 modest_header_view_add_observer(header_view,
1005 MODEST_HEADER_VIEW_OBSERVER(window));
1008 tny_msg_view_set_msg (TNY_MSG_VIEW (priv->msg_view), NULL);
1010 path = gtk_tree_row_reference_get_path (row_reference);
1011 if (gtk_tree_model_get_iter (priv->header_model, &iter, path)) {
1013 gtk_tree_model_get (priv->header_model, &iter,
1014 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
1016 message_reader (window, priv, header, row_reference);
1018 gtk_tree_path_free (path);
1020 /* Check dimming rules */
1021 modest_ui_actions_check_toolbar_dimming_rules (MODEST_WINDOW (window));
1022 modest_ui_actions_check_menu_dimming_rules (MODEST_WINDOW (window));
1023 modest_window_check_dimming_rules_group (MODEST_WINDOW (window), MODEST_DIMMING_RULES_CLIPBOARD);
1025 return MODEST_WINDOW(window);
1029 modest_msg_view_window_new_for_search_result (TnyMsg *msg,
1030 const gchar *modest_account_name,
1031 const gchar *msg_uid)
1033 ModestMsgViewWindow *window = NULL;
1034 ModestMsgViewWindowPrivate *priv = NULL;
1035 ModestWindowMgr *mgr = NULL;
1037 mgr = modest_runtime_get_window_mgr ();
1038 window = MODEST_MSG_VIEW_WINDOW (modest_window_mgr_get_msg_view_window (mgr));
1039 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), NULL);
1040 modest_msg_view_window_construct (window, modest_account_name, msg_uid);
1042 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1044 /* Remember that this is a search result,
1045 * so we can disable some UI appropriately: */
1046 priv->is_search_result = TRUE;
1048 tny_msg_view_set_msg (TNY_MSG_VIEW (priv->msg_view), msg);
1050 update_window_title (window);
1051 /* gtk_widget_show_all (GTK_WIDGET (window));*/
1052 modest_msg_view_window_update_priority (window);
1054 /* Check dimming rules */
1055 modest_ui_actions_check_toolbar_dimming_rules (MODEST_WINDOW (window));
1056 modest_ui_actions_check_menu_dimming_rules (MODEST_WINDOW (window));
1057 modest_window_check_dimming_rules_group (MODEST_WINDOW (window), MODEST_DIMMING_RULES_CLIPBOARD);
1059 return MODEST_WINDOW(window);
1063 modest_msg_view_window_new_for_attachment (TnyMsg *msg,
1064 const gchar *modest_account_name,
1065 const gchar *msg_uid)
1067 GObject *obj = NULL;
1068 ModestMsgViewWindowPrivate *priv;
1069 ModestWindowMgr *mgr = NULL;
1071 g_return_val_if_fail (msg, NULL);
1072 mgr = modest_runtime_get_window_mgr ();
1073 obj = G_OBJECT (modest_window_mgr_get_msg_view_window (mgr));
1074 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (obj);
1075 modest_msg_view_window_construct (MODEST_MSG_VIEW_WINDOW (obj),
1076 modest_account_name, msg_uid);
1078 tny_msg_view_set_msg (TNY_MSG_VIEW (priv->msg_view), msg);
1079 update_window_title (MODEST_MSG_VIEW_WINDOW (obj));
1081 /* gtk_widget_show_all (GTK_WIDGET (obj)); */
1083 /* Check dimming rules */
1084 modest_ui_actions_check_toolbar_dimming_rules (MODEST_WINDOW (obj));
1085 modest_ui_actions_check_menu_dimming_rules (MODEST_WINDOW (obj));
1086 modest_window_check_dimming_rules_group (MODEST_WINDOW (obj), MODEST_DIMMING_RULES_CLIPBOARD);
1088 return MODEST_WINDOW(obj);
1092 modest_msg_view_window_on_row_changed (GtkTreeModel *header_model,
1095 ModestMsgViewWindow *window)
1097 check_dimming_rules_after_change (window);
1101 modest_msg_view_window_on_row_deleted(GtkTreeModel *header_model,
1103 ModestMsgViewWindow *window)
1105 check_dimming_rules_after_change (window);
1107 /* The window could have dissapeared */
1110 check_dimming_rules_after_change (ModestMsgViewWindow *window)
1112 modest_ui_actions_check_menu_dimming_rules (MODEST_WINDOW (window));
1113 modest_ui_actions_check_toolbar_dimming_rules (MODEST_WINDOW (window));
1117 /* On insertions we check if the folder still has the message we are
1118 * showing or do not. If do not, we do nothing. Which means we are still
1119 * not attached to any header folder and thus next/prev buttons are
1120 * still dimmed. Once the message that is shown by msg-view is found, the
1121 * new model of header-view will be attached and the references will be set.
1122 * On each further insertions dimming rules will be checked. However
1123 * this requires extra CPU time at least works.
1124 * (An message might be deleted from TnyFolder and thus will not be
1125 * inserted into the model again for example if it is removed by the
1126 * imap server and the header view is refreshed.)
1129 modest_msg_view_window_on_row_inserted (GtkTreeModel *model,
1130 GtkTreePath *tree_path,
1131 GtkTreeIter *tree_iter,
1132 ModestMsgViewWindow *window)
1134 ModestMsgViewWindowPrivate *priv = NULL;
1135 TnyHeader *header = NULL;
1137 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window));
1138 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1140 g_assert (model == priv->header_model);
1142 /* Check if the newly inserted message is the same we are actually
1143 * showing. IF not, we should remain detached from the header model
1144 * and thus prev and next toolbar buttons should remain dimmed. */
1145 gtk_tree_model_get (model, tree_iter,
1146 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
1149 if (TNY_IS_HEADER (header)) {
1152 uid = modest_tny_folder_get_header_unique_id (header);
1153 if (!g_str_equal(priv->msg_uid, uid)) {
1154 check_dimming_rules_after_change (window);
1156 g_object_unref (G_OBJECT(header));
1160 g_object_unref(G_OBJECT(header));
1163 if (priv->row_reference) {
1164 gtk_tree_row_reference_free (priv->row_reference);
1167 /* Setup row_reference for the actual msg. */
1168 priv->row_reference = gtk_tree_row_reference_new (priv->header_model, tree_path);
1169 if (priv->row_reference == NULL) {
1170 g_warning("No reference for msg header item.");
1174 /* Now set up next_row_reference. */
1175 if (priv->next_row_reference) {
1176 gtk_tree_row_reference_free (priv->next_row_reference);
1179 priv->next_row_reference = gtk_tree_row_reference_copy (priv->row_reference);
1180 select_next_valid_row (priv->header_model,
1181 &(priv->next_row_reference), FALSE, priv->is_outbox);
1183 /* Connect the remaining callbacks to become able to detect
1184 * changes in header-view. */
1185 priv->row_changed_handler =
1186 g_signal_connect (priv->header_model, "row-changed",
1187 G_CALLBACK (modest_msg_view_window_on_row_changed),
1189 priv->row_deleted_handler =
1190 g_signal_connect (priv->header_model, "row-deleted",
1191 G_CALLBACK (modest_msg_view_window_on_row_deleted),
1193 priv->rows_reordered_handler =
1194 g_signal_connect (priv->header_model, "rows-reordered",
1195 G_CALLBACK (modest_msg_view_window_on_row_reordered),
1198 check_dimming_rules_after_change (window);
1202 modest_msg_view_window_on_row_reordered (GtkTreeModel *header_model,
1206 ModestMsgViewWindow *window)
1208 ModestMsgViewWindowPrivate *priv = NULL;
1209 gboolean already_changed = FALSE;
1211 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(window);
1213 /* If the current row was reordered select the proper next
1214 valid row. The same if the next row reference changes */
1215 if (priv->row_reference &&
1216 gtk_tree_row_reference_valid (priv->row_reference)) {
1218 path = gtk_tree_row_reference_get_path (priv->row_reference);
1219 if (gtk_tree_path_compare (path, arg1) == 0) {
1220 if (priv->next_row_reference) {
1221 gtk_tree_row_reference_free (priv->next_row_reference);
1223 priv->next_row_reference = gtk_tree_row_reference_copy (priv->row_reference);
1224 select_next_valid_row (header_model, &(priv->next_row_reference), FALSE, priv->is_outbox);
1225 already_changed = TRUE;
1227 gtk_tree_path_free (path);
1229 if (!already_changed &&
1230 priv->next_row_reference &&
1231 gtk_tree_row_reference_valid (priv->next_row_reference)) {
1233 path = gtk_tree_row_reference_get_path (priv->next_row_reference);
1234 if (gtk_tree_path_compare (path, arg1) == 0) {
1235 if (priv->next_row_reference) {
1236 gtk_tree_row_reference_free (priv->next_row_reference);
1238 priv->next_row_reference = gtk_tree_row_reference_copy (priv->row_reference);
1239 select_next_valid_row (header_model, &(priv->next_row_reference), FALSE, priv->is_outbox);
1241 gtk_tree_path_free (path);
1243 check_dimming_rules_after_change (window);
1246 /* The modest_msg_view_window_update_model_replaced implements update
1247 * function for ModestHeaderViewObserver. Checks whether the TnyFolder
1248 * actually belongs to the header-view is the same as the TnyFolder of
1249 * the message of msg-view or not. If they are different, there is
1250 * nothing to do. If they are the same, then the model has replaced and
1251 * the reference in msg-view shall be replaced from the old model to
1252 * the new model. In this case the view will be detached from it's
1253 * header folder. From this point the next/prev buttons are dimmed.
1256 modest_msg_view_window_update_model_replaced (ModestHeaderViewObserver *observer,
1257 GtkTreeModel *model,
1258 const gchar *tny_folder_id)
1260 ModestMsgViewWindowPrivate *priv = NULL;
1261 ModestMsgViewWindow *window = NULL;
1263 g_assert(MODEST_IS_HEADER_VIEW_OBSERVER(observer));
1264 g_assert(MODEST_IS_MSG_VIEW_WINDOW(observer));
1266 window = MODEST_MSG_VIEW_WINDOW(observer);
1267 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(window);
1269 /* If there is an other folder in the header-view then we do
1270 * not care about it's model (msg list). Else if the
1271 * header-view shows the folder the msg shown by us is in, we
1272 * shall replace our model reference and make some check. */
1273 if(model == NULL || tny_folder_id == NULL ||
1274 (priv->header_folder_id && !g_str_equal(tny_folder_id, priv->header_folder_id)))
1277 /* Model is changed(replaced), so we should forget the old
1278 * one. Because there might be other references and there
1279 * might be some change on the model even if we unreferenced
1280 * it, we need to disconnect our signals here. */
1281 if (priv->header_model) {
1282 if (g_signal_handler_is_connected(G_OBJECT (priv->header_model),
1283 priv->row_changed_handler))
1284 g_signal_handler_disconnect(G_OBJECT (priv->header_model),
1285 priv->row_changed_handler);
1286 if (g_signal_handler_is_connected(G_OBJECT (priv->header_model),
1287 priv->row_deleted_handler))
1288 g_signal_handler_disconnect(G_OBJECT (priv->header_model),
1289 priv->row_deleted_handler);
1290 if (g_signal_handler_is_connected(G_OBJECT (priv->header_model),
1291 priv->row_inserted_handler))
1292 g_signal_handler_disconnect(G_OBJECT (priv->header_model),
1293 priv->row_inserted_handler);
1294 if (g_signal_handler_is_connected(G_OBJECT (priv->header_model),
1295 priv->rows_reordered_handler))
1296 g_signal_handler_disconnect(G_OBJECT (priv->header_model),
1297 priv->rows_reordered_handler);
1300 if (priv->row_reference)
1301 gtk_tree_row_reference_free (priv->row_reference);
1302 if (priv->next_row_reference)
1303 gtk_tree_row_reference_free (priv->next_row_reference);
1304 g_object_unref(priv->header_model);
1307 priv->row_changed_handler = 0;
1308 priv->row_deleted_handler = 0;
1309 priv->row_inserted_handler = 0;
1310 priv->rows_reordered_handler = 0;
1311 priv->next_row_reference = NULL;
1312 priv->row_reference = NULL;
1313 priv->header_model = NULL;
1316 priv->header_model = g_object_ref (model);
1318 /* Also we must connect to the new model for row insertions.
1319 * Only for insertions now. We will need other ones only after
1320 * the msg is show by msg-view is added to the new model. */
1321 priv->row_inserted_handler =
1322 g_signal_connect (priv->header_model, "row-inserted",
1323 G_CALLBACK(modest_msg_view_window_on_row_inserted),
1326 modest_ui_actions_check_menu_dimming_rules(MODEST_WINDOW(window));
1327 modest_ui_actions_check_toolbar_dimming_rules(MODEST_WINDOW(window));
1331 modest_msg_view_window_toolbar_on_transfer_mode (ModestMsgViewWindow *self)
1333 ModestMsgViewWindowPrivate *priv= NULL;
1335 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self), FALSE);
1336 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
1338 return priv->progress_hint;
1342 modest_msg_view_window_get_header (ModestMsgViewWindow *self)
1344 ModestMsgViewWindowPrivate *priv= NULL;
1346 TnyHeader *header = NULL;
1347 GtkTreePath *path = NULL;
1350 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self), NULL);
1351 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
1353 /* If the message was not obtained from a treemodel,
1354 * for instance if it was opened directly by the search UI:
1356 if (priv->header_model == NULL ||
1357 priv->row_reference == NULL ||
1358 !gtk_tree_row_reference_valid (priv->row_reference)) {
1359 msg = modest_msg_view_window_get_message (self);
1361 header = tny_msg_get_header (msg);
1362 g_object_unref (msg);
1367 /* Get iter of the currently selected message in the header view: */
1368 path = gtk_tree_row_reference_get_path (priv->row_reference);
1369 g_return_val_if_fail (path != NULL, NULL);
1370 gtk_tree_model_get_iter (priv->header_model,
1374 /* Get current message header */
1375 gtk_tree_model_get (priv->header_model, &iter,
1376 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
1379 gtk_tree_path_free (path);
1384 modest_msg_view_window_get_message (ModestMsgViewWindow *self)
1386 ModestMsgViewWindowPrivate *priv;
1388 g_return_val_if_fail (self, NULL);
1390 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(self);
1392 return tny_msg_view_get_msg (TNY_MSG_VIEW (priv->msg_view));
1396 modest_msg_view_window_get_message_uid (ModestMsgViewWindow *self)
1398 ModestMsgViewWindowPrivate *priv;
1400 g_return_val_if_fail (self, NULL);
1402 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
1404 return (const gchar*) priv->msg_uid;
1408 modest_msg_view_window_toggle_find_toolbar (GtkToggleAction *toggle,
1411 ModestMsgViewWindow *window = MODEST_MSG_VIEW_WINDOW (data);
1412 ModestMsgViewWindowPrivate *priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1413 ModestWindowPrivate *parent_priv = MODEST_WINDOW_GET_PRIVATE (window);
1417 is_active = gtk_toggle_action_get_active (toggle);
1420 gtk_widget_show (priv->find_toolbar);
1421 hildon_find_toolbar_highlight_entry (HILDON_FIND_TOOLBAR (priv->find_toolbar), TRUE);
1423 gtk_widget_hide (priv->find_toolbar);
1424 modest_msg_view_grab_focus (MODEST_MSG_VIEW (priv->msg_view));
1427 /* update the toggle buttons status */
1428 action = gtk_ui_manager_get_action (parent_priv->ui_manager, "/ToolBar/FindInMessage");
1430 modest_utils_toggle_action_set_active_block_notify (GTK_TOGGLE_ACTION (action), is_active);
1435 modest_msg_view_window_find_toolbar_close (GtkWidget *widget,
1436 ModestMsgViewWindow *obj)
1438 GtkToggleAction *toggle;
1439 ModestWindowPrivate *parent_priv;
1440 ModestMsgViewWindowPrivate *priv;
1442 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (obj);
1443 parent_priv = MODEST_WINDOW_GET_PRIVATE (obj);
1445 toggle = GTK_TOGGLE_ACTION (gtk_ui_manager_get_action (parent_priv->ui_manager, "/ToolBar/FindInMessage"));
1446 gtk_toggle_action_set_active (toggle, FALSE);
1447 modest_msg_view_grab_focus (MODEST_MSG_VIEW (priv->msg_view));
1451 modest_msg_view_window_find_toolbar_search (GtkWidget *widget,
1452 ModestMsgViewWindow *obj)
1454 gchar *current_search;
1455 ModestMsgViewWindowPrivate *priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (obj);
1457 if (modest_mime_part_view_is_empty (MODEST_MIME_PART_VIEW (priv->msg_view))) {
1458 hildon_banner_show_information (NULL, NULL, _("mail_ib_nothing_to_find"));
1462 g_object_get (G_OBJECT (widget), "prefix", ¤t_search, NULL);
1464 if ((current_search == NULL) || (strcmp (current_search, "") == 0)) {
1465 g_free (current_search);
1466 hildon_banner_show_information (NULL, NULL, dgettext("hildon-common-strings", "ecdg_ib_find_rep_enter_text"));
1470 if ((priv->last_search == NULL) || (strcmp (priv->last_search, current_search) != 0)) {
1472 g_free (priv->last_search);
1473 priv->last_search = g_strdup (current_search);
1474 result = modest_isearch_view_search (MODEST_ISEARCH_VIEW (priv->msg_view),
1477 hildon_banner_show_information (NULL, NULL, dgettext("hildon-libs", "ckct_ib_find_no_matches"));
1478 g_free (priv->last_search);
1479 priv->last_search = NULL;
1481 modest_msg_view_grab_focus (MODEST_MSG_VIEW (priv->msg_view));
1482 hildon_find_toolbar_highlight_entry (HILDON_FIND_TOOLBAR (priv->find_toolbar), TRUE);
1485 if (!modest_isearch_view_search_next (MODEST_ISEARCH_VIEW (priv->msg_view))) {
1486 hildon_banner_show_information (NULL, NULL, dgettext("hildon-libs", "ckct_ib_find_search_complete"));
1487 g_free (priv->last_search);
1488 priv->last_search = NULL;
1490 modest_msg_view_grab_focus (MODEST_MSG_VIEW (priv->msg_view));
1491 hildon_find_toolbar_highlight_entry (HILDON_FIND_TOOLBAR (priv->find_toolbar), TRUE);
1495 g_free (current_search);
1500 modest_msg_view_window_set_zoom (ModestWindow *window,
1503 ModestMsgViewWindowPrivate *priv;
1504 ModestWindowPrivate *parent_priv;
1506 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window));
1508 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1509 parent_priv = MODEST_WINDOW_GET_PRIVATE (window);
1510 modest_zoomable_set_zoom (MODEST_ZOOMABLE (priv->msg_view), zoom);
1515 modest_msg_view_window_get_zoom (ModestWindow *window)
1517 ModestMsgViewWindowPrivate *priv;
1519 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), 1.0);
1521 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1522 return modest_zoomable_get_zoom (MODEST_ZOOMABLE (priv->msg_view));
1526 modest_msg_view_window_zoom_plus (ModestWindow *window)
1529 ModestMsgViewWindowPrivate *priv;
1531 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), 1.0);
1532 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1534 zoom_level = modest_zoomable_get_zoom (MODEST_ZOOMABLE (priv->msg_view));
1536 if (zoom_level >= 2.0) {
1537 hildon_banner_show_information (NULL, NULL, dgettext("hildon-common-strings", "ckct_ib_max_zoom_level_reached"));
1539 } else if (zoom_level >= 1.5) {
1541 } else if (zoom_level >= 1.2) {
1543 } else if (zoom_level >= 1.0) {
1545 } else if (zoom_level >= 0.8) {
1547 } else if (zoom_level >= 0.5) {
1553 /* set zoom level */
1554 modest_zoomable_set_zoom (MODEST_ZOOMABLE (priv->msg_view), zoom_level);
1561 modest_msg_view_window_zoom_minus (ModestWindow *window)
1564 ModestMsgViewWindowPrivate *priv;
1566 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), 1.0);
1567 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1569 zoom_level = modest_zoomable_get_zoom (MODEST_ZOOMABLE (priv->msg_view));
1571 if (zoom_level <= 0.5) {
1572 hildon_banner_show_information (NULL, NULL, dgettext("hildon-common-strings", "ckct_ib_min_zoom_level_reached"));
1574 } else if (zoom_level <= 0.8) {
1576 } else if (zoom_level <= 1.0) {
1578 } else if (zoom_level <= 1.2) {
1580 } else if (zoom_level <= 1.5) {
1582 } else if (zoom_level <= 2.0) {
1588 /* set zoom level */
1589 modest_zoomable_set_zoom (MODEST_ZOOMABLE (priv->msg_view), zoom_level);
1596 modest_msg_view_window_key_event (GtkWidget *window,
1602 focus = gtk_window_get_focus (GTK_WINDOW (window));
1604 /* for the find toolbar case */
1605 if (focus && GTK_IS_ENTRY (focus)) {
1606 if (event->keyval == GDK_BackSpace) {
1608 copy = gdk_event_copy ((GdkEvent *) event);
1609 gtk_widget_event (focus, copy);
1610 gdk_event_free (copy);
1615 if (event->keyval == GDK_Up || event->keyval == GDK_KP_Up ||
1616 event->keyval == GDK_Down || event->keyval == GDK_KP_Down ||
1617 event->keyval == GDK_Page_Up || event->keyval == GDK_KP_Page_Up ||
1618 event->keyval == GDK_Page_Down || event->keyval == GDK_KP_Page_Down ||
1619 event->keyval == GDK_Home || event->keyval == GDK_KP_Home ||
1620 event->keyval == GDK_End || event->keyval == GDK_KP_End) {
1621 /* ModestMsgViewWindowPrivate *priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window); */
1622 /* gboolean return_value; */
1624 if (event->type == GDK_KEY_PRESS) {
1625 GtkScrollType scroll_type;
1627 switch (event->keyval) {
1630 scroll_type = GTK_SCROLL_STEP_UP; break;
1633 scroll_type = GTK_SCROLL_STEP_DOWN; break;
1635 case GDK_KP_Page_Up:
1636 scroll_type = GTK_SCROLL_PAGE_UP; break;
1638 case GDK_KP_Page_Down:
1639 scroll_type = GTK_SCROLL_PAGE_DOWN; break;
1642 scroll_type = GTK_SCROLL_START; break;
1645 scroll_type = GTK_SCROLL_END; break;
1646 default: scroll_type = GTK_SCROLL_NONE;
1649 /* g_signal_emit_by_name (G_OBJECT (priv->main_scroll), "scroll-child", */
1650 /* scroll_type, FALSE, &return_value); */
1661 modest_msg_view_window_last_message_selected (ModestMsgViewWindow *window)
1664 ModestMsgViewWindowPrivate *priv;
1665 GtkTreeIter tmp_iter;
1666 gboolean is_last_selected;
1668 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), TRUE);
1669 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1671 /*if no model (so no rows at all), then virtually we are the last*/
1672 if (!priv->header_model || !priv->row_reference)
1675 if (!gtk_tree_row_reference_valid (priv->row_reference))
1678 path = gtk_tree_row_reference_get_path (priv->row_reference);
1682 is_last_selected = TRUE;
1683 while (is_last_selected) {
1685 gtk_tree_path_next (path);
1686 if (!gtk_tree_model_get_iter (priv->header_model, &tmp_iter, path))
1688 gtk_tree_model_get (priv->header_model, &tmp_iter,
1689 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
1692 if (msg_is_visible (header, priv->is_outbox))
1693 is_last_selected = FALSE;
1694 g_object_unref(G_OBJECT(header));
1697 gtk_tree_path_free (path);
1698 return is_last_selected;
1702 modest_msg_view_window_has_headers_model (ModestMsgViewWindow *window)
1704 ModestMsgViewWindowPrivate *priv;
1706 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), TRUE);
1707 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1709 return priv->header_model != NULL;
1713 modest_msg_view_window_is_search_result (ModestMsgViewWindow *window)
1715 ModestMsgViewWindowPrivate *priv;
1717 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), TRUE);
1718 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1720 return priv->is_search_result;
1724 msg_is_visible (TnyHeader *header, gboolean check_outbox)
1726 if ((tny_header_get_flags(header) & TNY_HEADER_FLAG_DELETED))
1728 if (!check_outbox) {
1731 ModestTnySendQueueStatus status;
1732 status = modest_tny_all_send_queues_get_msg_status (header);
1733 return ((status != MODEST_TNY_SEND_QUEUE_FAILED) &&
1734 (status != MODEST_TNY_SEND_QUEUE_SENDING));
1739 modest_msg_view_window_first_message_selected (ModestMsgViewWindow *window)
1742 ModestMsgViewWindowPrivate *priv;
1743 gboolean is_first_selected;
1744 GtkTreeIter tmp_iter;
1746 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), TRUE);
1747 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1749 /*if no model (so no rows at all), then virtually we are the first*/
1750 if (!priv->header_model || !priv->row_reference)
1753 if (!gtk_tree_row_reference_valid (priv->row_reference))
1756 path = gtk_tree_row_reference_get_path (priv->row_reference);
1760 is_first_selected = TRUE;
1761 while (is_first_selected) {
1763 if(!gtk_tree_path_prev (path))
1765 /* Here the 'if' is needless for logic, but let make sure
1766 * iter is valid for gtk_tree_model_get. */
1767 if (!gtk_tree_model_get_iter (priv->header_model, &tmp_iter, path))
1769 gtk_tree_model_get (priv->header_model, &tmp_iter,
1770 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
1773 if (msg_is_visible (header, priv->is_outbox))
1774 is_first_selected = FALSE;
1775 g_object_unref(G_OBJECT(header));
1778 gtk_tree_path_free (path);
1779 return is_first_selected;
1784 GtkTreeRowReference *row_reference;
1788 message_reader_performer (gboolean canceled,
1790 GtkWindow *parent_window,
1791 TnyAccount *account,
1794 ModestMailOperation *mail_op = NULL;
1795 MsgReaderInfo *info;
1797 info = (MsgReaderInfo *) user_data;
1798 if (canceled || err) {
1802 /* Register the header - it'll be unregistered in the callback */
1803 modest_window_mgr_register_header (modest_runtime_get_window_mgr (), info->header, NULL);
1805 /* New mail operation */
1806 mail_op = modest_mail_operation_new_with_error_handling (G_OBJECT(parent_window),
1807 modest_ui_actions_disk_operations_error_handler,
1810 modest_mail_operation_queue_add (modest_runtime_get_mail_operation_queue (), mail_op);
1811 modest_mail_operation_get_msg (mail_op, info->header, TRUE, view_msg_cb, info->row_reference);
1812 g_object_unref (mail_op);
1814 /* Update dimming rules */
1815 modest_ui_actions_check_toolbar_dimming_rules (MODEST_WINDOW (parent_window));
1816 modest_ui_actions_check_menu_dimming_rules (MODEST_WINDOW (parent_window));
1819 /* Frees. The row_reference will be freed by the view_msg_cb callback */
1820 g_object_unref (info->header);
1821 g_slice_free (MsgReaderInfo, info);
1826 * Reads the message whose summary item is @header. It takes care of
1827 * several things, among others:
1829 * If the message was not previously downloaded then ask the user
1830 * before downloading. If there is no connection launch the connection
1831 * dialog. Update toolbar dimming rules.
1833 * Returns: TRUE if the mail operation was started, otherwise if the
1834 * user do not want to download the message, or if the user do not
1835 * want to connect, then the operation is not issued
1838 message_reader (ModestMsgViewWindow *window,
1839 ModestMsgViewWindowPrivate *priv,
1841 GtkTreeRowReference *row_reference)
1843 ModestWindowMgr *mgr;
1844 TnyAccount *account;
1846 MsgReaderInfo *info;
1848 g_return_val_if_fail (row_reference != NULL, FALSE);
1850 mgr = modest_runtime_get_window_mgr ();
1851 /* Msg download completed */
1852 if (!(tny_header_get_flags (header) & TNY_HEADER_FLAG_CACHED)) {
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)
1863 folder = tny_header_get_folder (header);
1864 info = g_slice_new (MsgReaderInfo);
1865 info->header = g_object_ref (header);
1866 info->row_reference = gtk_tree_row_reference_copy (row_reference);
1868 /* Offer the connection dialog if necessary */
1869 modest_platform_connect_if_remote_and_perform ((GtkWindow *) window,
1871 TNY_FOLDER_STORE (folder),
1872 message_reader_performer,
1874 g_object_unref (folder);
1879 folder = tny_header_get_folder (header);
1880 account = tny_folder_get_account (folder);
1881 info = g_slice_new (MsgReaderInfo);
1882 info->header = g_object_ref (header);
1883 info->row_reference = gtk_tree_row_reference_copy (row_reference);
1885 message_reader_performer (FALSE, NULL, (GtkWindow *) window, account, info);
1886 g_object_unref (account);
1887 g_object_unref (folder);
1893 modest_msg_view_window_select_next_message (ModestMsgViewWindow *window)
1895 ModestMsgViewWindowPrivate *priv;
1896 GtkTreePath *path= NULL;
1897 GtkTreeIter tmp_iter;
1899 gboolean retval = TRUE;
1900 GtkTreeRowReference *row_reference = NULL;
1902 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), FALSE);
1903 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1905 if (!priv->row_reference)
1908 /* Update the next row reference if it's not valid. This could
1909 happen if for example the header which it was pointing to,
1910 was deleted. The best place to do it is in the row-deleted
1911 handler but the tinymail model do not work like the glib
1912 tree models and reports the deletion when the row is still
1914 if (!gtk_tree_row_reference_valid (priv->next_row_reference)) {
1915 if (gtk_tree_row_reference_valid (priv->row_reference)) {
1916 priv->next_row_reference = gtk_tree_row_reference_copy (priv->row_reference);
1917 select_next_valid_row (priv->header_model, &(priv->next_row_reference), FALSE, priv->is_outbox);
1920 if (priv->next_row_reference)
1921 path = gtk_tree_row_reference_get_path (priv->next_row_reference);
1925 row_reference = gtk_tree_row_reference_copy (priv->next_row_reference);
1927 gtk_tree_model_get_iter (priv->header_model,
1930 gtk_tree_path_free (path);
1932 gtk_tree_model_get (priv->header_model, &tmp_iter,
1933 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
1936 /* Read the message & show it */
1937 if (!message_reader (window, priv, header, row_reference)) {
1940 gtk_tree_row_reference_free (row_reference);
1943 g_object_unref (header);
1949 modest_msg_view_window_select_previous_message (ModestMsgViewWindow *window)
1951 ModestMsgViewWindowPrivate *priv = NULL;
1953 gboolean finished = FALSE;
1954 gboolean retval = FALSE;
1956 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), FALSE);
1957 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1959 /* Return inmediatly if there is no header model */
1960 if (!priv->header_model || !priv->row_reference)
1963 path = gtk_tree_row_reference_get_path (priv->row_reference);
1964 while (!finished && gtk_tree_path_prev (path)) {
1968 gtk_tree_model_get_iter (priv->header_model, &iter, path);
1969 gtk_tree_model_get (priv->header_model, &iter,
1970 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
1974 if (msg_is_visible (header, priv->is_outbox)) {
1975 GtkTreeRowReference *row_reference;
1976 row_reference = gtk_tree_row_reference_new (priv->header_model, path);
1977 /* Read the message & show it */
1978 retval = message_reader (window, priv, header, row_reference);
1979 gtk_tree_row_reference_free (row_reference);
1983 g_object_unref (header);
1987 gtk_tree_path_free (path);
1992 view_msg_cb (ModestMailOperation *mail_op,
1999 ModestMsgViewWindow *self = NULL;
2000 ModestMsgViewWindowPrivate *priv = NULL;
2001 GtkTreeRowReference *row_reference = NULL;
2003 /* Unregister the header (it was registered before creating the mail operation) */
2004 modest_window_mgr_unregister_header (modest_runtime_get_window_mgr (), header);
2006 row_reference = (GtkTreeRowReference *) user_data;
2008 gtk_tree_row_reference_free (row_reference);
2012 /* If there was any error */
2013 if (!modest_ui_actions_msg_retrieval_check (mail_op, header, msg)) {
2014 gtk_tree_row_reference_free (row_reference);
2018 /* Get the window */
2019 self = (ModestMsgViewWindow *) modest_mail_operation_get_source (mail_op);
2020 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self));
2021 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
2023 /* Update the row reference */
2024 if (priv->row_reference != NULL) {
2025 gtk_tree_row_reference_free (priv->row_reference);
2026 priv->row_reference = gtk_tree_row_reference_copy (row_reference);
2027 if (priv->next_row_reference != NULL) {
2028 gtk_tree_row_reference_free (priv->next_row_reference);
2030 priv->next_row_reference = gtk_tree_row_reference_copy (priv->row_reference);
2031 select_next_valid_row (priv->header_model, &(priv->next_row_reference), TRUE, priv->is_outbox);
2034 /* Mark header as read */
2035 if (!(tny_header_get_flags (header) & TNY_HEADER_FLAG_SEEN))
2036 tny_header_set_flag (header, TNY_HEADER_FLAG_SEEN);
2038 /* Set new message */
2039 if (priv->msg_view != NULL && TNY_IS_MSG_VIEW (priv->msg_view)) {
2040 tny_msg_view_set_msg (TNY_MSG_VIEW (priv->msg_view), msg);
2041 modest_msg_view_window_update_priority (self);
2042 update_window_title (MODEST_MSG_VIEW_WINDOW (self));
2043 modest_msg_view_grab_focus (MODEST_MSG_VIEW (priv->msg_view));
2046 /* Set the new message uid of the window */
2047 if (priv->msg_uid) {
2048 g_free (priv->msg_uid);
2049 priv->msg_uid = modest_tny_folder_get_header_unique_id (header);
2052 /* Notify the observers */
2053 g_signal_emit (G_OBJECT (self), signals[MSG_CHANGED_SIGNAL],
2054 0, priv->header_model, priv->row_reference);
2057 g_object_unref (self);
2058 gtk_tree_row_reference_free (row_reference);
2062 modest_msg_view_window_get_folder_type (ModestMsgViewWindow *window)
2064 ModestMsgViewWindowPrivate *priv;
2066 TnyFolderType folder_type;
2068 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
2070 folder_type = TNY_FOLDER_TYPE_UNKNOWN;
2072 msg = tny_msg_view_get_msg (TNY_MSG_VIEW (priv->msg_view));
2076 folder = tny_msg_get_folder (msg);
2078 folder_type = modest_tny_folder_guess_folder_type (folder);
2079 g_object_unref (folder);
2081 g_object_unref (msg);
2089 modest_msg_view_window_update_priority (ModestMsgViewWindow *window)
2091 ModestMsgViewWindowPrivate *priv;
2092 TnyHeader *header = NULL;
2093 TnyHeaderFlags flags = 0;
2095 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
2097 if (priv->header_model && priv->row_reference) {
2099 GtkTreePath *path = NULL;
2101 path = gtk_tree_row_reference_get_path (priv->row_reference);
2102 g_return_if_fail (path != NULL);
2103 gtk_tree_model_get_iter (priv->header_model,
2105 gtk_tree_row_reference_get_path (priv->row_reference));
2107 gtk_tree_model_get (priv->header_model, &iter, TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
2109 gtk_tree_path_free (path);
2112 msg = tny_msg_view_get_msg (TNY_MSG_VIEW (priv->msg_view));
2114 header = tny_msg_get_header (msg);
2115 g_object_unref (msg);
2120 flags = tny_header_get_flags (header);
2121 g_object_unref(G_OBJECT(header));
2124 modest_msg_view_set_priority (MODEST_MSG_VIEW(priv->msg_view), flags);
2129 toolbar_resize (ModestMsgViewWindow *self)
2131 ModestMsgViewWindowPrivate *priv = NULL;
2132 ModestWindowPrivate *parent_priv = NULL;
2134 gint static_button_size;
2135 ModestWindowMgr *mgr;
2137 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self));
2138 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
2139 parent_priv = MODEST_WINDOW_GET_PRIVATE(self);
2141 mgr = modest_runtime_get_window_mgr ();
2142 static_button_size = modest_window_mgr_get_fullscreen_mode (mgr)?118:108;
2144 if (parent_priv->toolbar) {
2145 /* left size buttons */
2146 widget = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/ToolbarMessageReply");
2147 gtk_tool_item_set_expand (GTK_TOOL_ITEM (widget), FALSE);
2148 gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (widget), FALSE);
2149 gtk_widget_set_size_request (GTK_WIDGET (widget), static_button_size, -1);
2150 widget = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/ToolbarMessageMoveTo");
2151 gtk_tool_item_set_expand (GTK_TOOL_ITEM (widget), FALSE);
2152 gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (widget), FALSE);
2153 gtk_widget_set_size_request (GTK_WIDGET (widget), static_button_size, -1);
2154 widget = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/ToolbarDeleteMessage");
2155 gtk_tool_item_set_expand (GTK_TOOL_ITEM (widget), FALSE);
2156 gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (widget), FALSE);
2157 gtk_widget_set_size_request (GTK_WIDGET (widget), static_button_size, -1);
2158 widget = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/FindInMessage");
2159 gtk_tool_item_set_expand (GTK_TOOL_ITEM (widget), FALSE);
2160 gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (widget), FALSE);
2161 gtk_widget_set_size_request (GTK_WIDGET (widget), static_button_size, -1);
2163 gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (priv->next_toolitem), TRUE);
2164 gtk_tool_item_set_expand (GTK_TOOL_ITEM (priv->next_toolitem), TRUE);
2165 gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (priv->prev_toolitem), TRUE);
2166 gtk_tool_item_set_expand (GTK_TOOL_ITEM (priv->prev_toolitem), TRUE);
2172 modest_msg_view_window_show_toolbar (ModestWindow *self,
2173 gboolean show_toolbar)
2175 ModestMsgViewWindowPrivate *priv = NULL;
2176 ModestWindowPrivate *parent_priv;
2177 GtkWidget *reply_button = NULL, *menu = NULL;
2179 parent_priv = MODEST_WINDOW_GET_PRIVATE(self);
2180 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(self);
2182 /* Set optimized view status */
2183 priv->optimized_view = !show_toolbar;
2185 if (!parent_priv->toolbar) {
2186 parent_priv->toolbar = gtk_ui_manager_get_widget (parent_priv->ui_manager,
2188 gtk_widget_set_no_show_all (parent_priv->toolbar, TRUE);
2190 priv->next_toolitem = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/ToolbarMessageNext");
2191 priv->prev_toolitem = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/ToolbarMessageBack");
2192 toolbar_resize (MODEST_MSG_VIEW_WINDOW (self));
2195 hildon_window_add_toolbar (HILDON_WINDOW (self),
2196 GTK_TOOLBAR (parent_priv->toolbar));
2198 /* Set reply button tap and hold menu */
2199 reply_button = gtk_ui_manager_get_widget (parent_priv->ui_manager,
2200 "/ToolBar/ToolbarMessageReply");
2201 menu = gtk_ui_manager_get_widget (parent_priv->ui_manager,
2202 "/ToolbarReplyCSM");
2203 if (menu && reply_button)
2204 gtk_widget_tap_and_hold_setup (GTK_WIDGET (reply_button), menu, NULL, 0);
2208 /* Quick hack: this prevents toolbar icons "dance" when progress bar show status is changed */
2209 /* TODO: resize mode migth be GTK_RESIZE_QUEUE, in order to avoid unneccesary shows */
2210 gtk_container_set_resize_mode (GTK_CONTAINER(parent_priv->toolbar), GTK_RESIZE_IMMEDIATE);
2212 gtk_widget_show (GTK_WIDGET (parent_priv->toolbar));
2213 if (modest_msg_view_window_transfer_mode_enabled (MODEST_MSG_VIEW_WINDOW (self)))
2214 set_progress_hint (MODEST_MSG_VIEW_WINDOW (self), TRUE);
2216 set_progress_hint (MODEST_MSG_VIEW_WINDOW (self), FALSE);
2219 gtk_widget_set_no_show_all (parent_priv->toolbar, TRUE);
2220 gtk_widget_hide (GTK_WIDGET (parent_priv->toolbar));
2225 modest_msg_view_window_clipboard_owner_change (GtkClipboard *clipboard,
2227 ModestMsgViewWindow *window)
2229 if (!GTK_WIDGET_VISIBLE (window))
2232 modest_window_check_dimming_rules_group (MODEST_WINDOW (window), MODEST_DIMMING_RULES_CLIPBOARD);
2236 modest_msg_view_window_transfer_mode_enabled (ModestMsgViewWindow *self)
2238 ModestMsgViewWindowPrivate *priv;
2240 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self), FALSE);
2241 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(self);
2243 return priv->progress_hint;
2247 observers_empty (ModestMsgViewWindow *self)
2250 ModestMsgViewWindowPrivate *priv;
2251 gboolean is_empty = TRUE;
2252 guint pending_ops = 0;
2254 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(self);
2255 tmp = priv->progress_widgets;
2257 /* Check all observers */
2258 while (tmp && is_empty) {
2259 pending_ops = modest_progress_object_num_pending_operations (MODEST_PROGRESS_OBJECT(tmp->data));
2260 is_empty = pending_ops == 0;
2262 tmp = g_slist_next(tmp);
2269 on_account_removed (TnyAccountStore *account_store,
2270 TnyAccount *account,
2273 /* Do nothing if it's a transport account, because we only
2274 show the messages of a store account */
2275 if (tny_account_get_account_type(account) == TNY_ACCOUNT_TYPE_STORE) {
2276 const gchar *parent_acc = NULL;
2277 const gchar *our_acc = NULL;
2279 our_acc = modest_window_get_active_account (MODEST_WINDOW (user_data));
2280 parent_acc = modest_tny_account_get_parent_modest_account_name_for_server_account (account);
2282 /* Close this window if I'm showing a message of the removed account */
2283 if (strcmp (parent_acc, our_acc) == 0)
2284 modest_ui_actions_on_close_window (NULL, MODEST_WINDOW (user_data));
2289 on_mail_operation_started (ModestMailOperation *mail_op,
2292 ModestMsgViewWindow *self;
2293 ModestMailOperationTypeOperation op_type;
2295 ModestMsgViewWindowPrivate *priv;
2296 GObject *source = NULL;
2298 self = MODEST_MSG_VIEW_WINDOW (user_data);
2299 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
2300 op_type = modest_mail_operation_get_type_operation (mail_op);
2301 tmp = priv->progress_widgets;
2302 source = modest_mail_operation_get_source(mail_op);
2303 if (G_OBJECT (self) == source) {
2304 if (op_type == MODEST_MAIL_OPERATION_TYPE_RECEIVE ) {
2305 set_toolbar_transfer_mode(self);
2307 modest_progress_object_add_operation (
2308 MODEST_PROGRESS_OBJECT (tmp->data),
2310 tmp = g_slist_next (tmp);
2314 g_object_unref (source);
2318 on_mail_operation_finished (ModestMailOperation *mail_op,
2321 ModestMsgViewWindow *self;
2322 ModestMailOperationTypeOperation op_type;
2324 ModestMsgViewWindowPrivate *priv;
2326 self = MODEST_MSG_VIEW_WINDOW (user_data);
2327 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
2328 op_type = modest_mail_operation_get_type_operation (mail_op);
2329 tmp = priv->progress_widgets;
2331 if (op_type == MODEST_MAIL_OPERATION_TYPE_RECEIVE ) {
2333 modest_progress_object_remove_operation (MODEST_PROGRESS_OBJECT (tmp->data),
2335 tmp = g_slist_next (tmp);
2338 /* If no more operations are being observed, NORMAL mode is enabled again */
2339 if (observers_empty (self)) {
2340 set_progress_hint (self, FALSE);
2343 /* Update dimming rules. We have to do this right here
2344 and not in view_msg_cb because at that point the
2345 transfer mode is still enabled so the dimming rule
2346 won't let the user delete the message that has been
2347 readed for example */
2348 modest_ui_actions_check_toolbar_dimming_rules (MODEST_WINDOW (self));
2349 modest_ui_actions_check_menu_dimming_rules (MODEST_WINDOW (self));
2354 on_queue_changed (ModestMailOperationQueue *queue,
2355 ModestMailOperation *mail_op,
2356 ModestMailOperationQueueNotification type,
2357 ModestMsgViewWindow *self)
2359 ModestMsgViewWindowPrivate *priv;
2361 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
2363 /* If this operations was created by another window, do nothing */
2364 if (!modest_mail_operation_is_mine (mail_op, G_OBJECT(self)))
2367 if (type == MODEST_MAIL_OPERATION_QUEUE_OPERATION_ADDED) {
2368 priv->sighandlers = modest_signal_mgr_connect (priv->sighandlers,
2370 "operation-started",
2371 G_CALLBACK (on_mail_operation_started),
2373 priv->sighandlers = modest_signal_mgr_connect (priv->sighandlers,
2375 "operation-finished",
2376 G_CALLBACK (on_mail_operation_finished),
2378 } else if (type == MODEST_MAIL_OPERATION_QUEUE_OPERATION_REMOVED) {
2379 priv->sighandlers = modest_signal_mgr_disconnect (priv->sighandlers,
2381 "operation-started");
2382 priv->sighandlers = modest_signal_mgr_disconnect (priv->sighandlers,
2384 "operation-finished");
2389 modest_msg_view_window_get_attachments (ModestMsgViewWindow *win)
2391 ModestMsgViewWindowPrivate *priv;
2392 TnyList *selected_attachments = NULL;
2394 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (win), NULL);
2395 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (win);
2397 selected_attachments = modest_msg_view_get_selected_attachments (MODEST_MSG_VIEW (priv->msg_view));
2399 return selected_attachments;
2405 guint banner_idle_id;
2406 } DecodeAsyncHelper;
2409 decode_async_banner_idle (gpointer user_data)
2411 DecodeAsyncHelper *helper = (DecodeAsyncHelper *) user_data;
2413 helper->banner_idle_id = 0;
2414 helper->banner = hildon_banner_show_animation (NULL, NULL, _("mail_me_opening"));
2420 on_decode_to_stream_async_handler (TnyMimePart *mime_part,
2426 DecodeAsyncHelper *helper = (DecodeAsyncHelper *) user_data;
2428 if (helper->banner_idle_id > 0) {
2429 g_source_remove (helper->banner_idle_id);
2430 helper->banner_idle_id = 0;
2432 if (helper->banner) {
2433 gtk_widget_destroy (helper->banner);
2434 helper->banner = NULL;
2436 if (cancelled || err) {
2437 modest_platform_information_banner (NULL, NULL,
2438 _("mail_ib_file_operation_failed"));
2442 /* make the file read-only */
2443 g_chmod(helper->filepath, 0444);
2445 /* Activate the file */
2446 modest_platform_activate_file (helper->filepath, tny_mime_part_get_content_type (mime_part));
2450 g_free (helper->filepath);
2451 g_slice_free (DecodeAsyncHelper, helper);
2455 modest_msg_view_window_view_attachment (ModestMsgViewWindow *window,
2456 TnyMimePart *mime_part)
2458 ModestMsgViewWindowPrivate *priv;
2459 const gchar *msg_uid;
2460 gchar *attachment_uid = NULL;
2461 gint attachment_index = 0;
2462 TnyList *attachments;
2464 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window));
2465 g_return_if_fail (TNY_IS_MIME_PART (mime_part) || (mime_part == NULL));
2466 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
2468 msg_uid = modest_msg_view_window_get_message_uid (MODEST_MSG_VIEW_WINDOW (window));
2469 attachments = modest_msg_view_get_attachments (MODEST_MSG_VIEW (priv->msg_view));
2470 attachment_index = modest_list_index (attachments, (GObject *) mime_part);
2471 g_object_unref (attachments);
2473 if (msg_uid && attachment_index >= 0) {
2474 attachment_uid = g_strdup_printf ("%s/%d", msg_uid, attachment_index);
2477 if (mime_part == NULL) {
2478 gboolean error = FALSE;
2479 TnyList *selected_attachments = modest_msg_view_get_selected_attachments (MODEST_MSG_VIEW (priv->msg_view));
2480 if (selected_attachments == NULL || tny_list_get_length (selected_attachments) == 0) {
2482 } else if (tny_list_get_length (selected_attachments) > 1) {
2483 hildon_banner_show_information (NULL, NULL, _("mcen_ib_unable_to_display_more"));
2487 iter = tny_list_create_iterator (selected_attachments);
2488 mime_part = (TnyMimePart *) tny_iterator_get_current (iter);
2489 g_object_unref (iter);
2491 g_object_unref (selected_attachments);
2496 g_object_ref (mime_part);
2499 if (tny_mime_part_is_purged (mime_part)) {
2500 g_object_unref (mime_part);
2504 if (!modest_tny_mime_part_is_msg (mime_part)) {
2505 gchar *filepath = NULL;
2506 const gchar *att_filename = tny_mime_part_get_filename (mime_part);
2507 gboolean show_error_banner = FALSE;
2508 TnyFsStream *temp_stream = NULL;
2509 temp_stream = modest_utils_create_temp_stream (att_filename, attachment_uid,
2512 if (temp_stream != NULL) {
2513 DecodeAsyncHelper *helper = g_slice_new (DecodeAsyncHelper);
2514 helper->filepath = g_strdup (filepath);
2515 helper->banner = NULL;
2516 helper->banner_idle_id = g_timeout_add (1000, decode_async_banner_idle, helper);
2517 tny_mime_part_decode_to_stream_async (mime_part, TNY_STREAM (temp_stream),
2518 on_decode_to_stream_async_handler,
2521 g_object_unref (temp_stream);
2522 /* NOTE: files in the temporary area will be automatically
2523 * cleaned after some time if they are no longer in use */
2526 const gchar *content_type;
2527 /* the file may already exist but it isn't writable,
2528 * let's try to open it anyway */
2529 content_type = tny_mime_part_get_content_type (mime_part);
2530 modest_platform_activate_file (filepath, content_type);
2532 g_warning ("%s: modest_utils_create_temp_stream failed", __FUNCTION__);
2533 show_error_banner = TRUE;
2538 if (show_error_banner)
2539 modest_platform_information_banner (NULL, NULL, _("mail_ib_file_operation_failed"));
2541 /* message attachment */
2542 TnyHeader *header = NULL;
2543 ModestWindowMgr *mgr;
2544 ModestWindow *msg_win = NULL;
2547 header = tny_msg_get_header (TNY_MSG (mime_part));
2548 mgr = modest_runtime_get_window_mgr ();
2549 found = modest_window_mgr_find_registered_header (mgr, header, &msg_win);
2552 /* if it's found, but there is no msg_win, it's probably in the process of being created;
2553 * thus, we don't do anything */
2554 g_warning ("window for is already being created");
2556 /* it's not found, so create a new window for it */
2557 modest_window_mgr_register_header (mgr, header, attachment_uid); /* register the uid before building the window */
2558 gchar *account = g_strdup (modest_window_get_active_account (MODEST_WINDOW (window)));
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, attachment_uid);
2562 modest_window_set_zoom (MODEST_WINDOW (msg_win),
2563 modest_window_get_zoom (MODEST_WINDOW (window)));
2564 modest_window_mgr_register_window (mgr, msg_win, MODEST_WINDOW (window));
2565 gtk_widget_show_all (GTK_WIDGET (msg_win));
2568 g_object_unref (mime_part);
2581 GnomeVFSResult result;
2584 static void save_mime_part_info_free (SaveMimePartInfo *info, gboolean with_struct);
2585 static gboolean idle_save_mime_part_show_result (SaveMimePartInfo *info);
2586 static gpointer save_mime_part_to_file (SaveMimePartInfo *info);
2587 static void save_mime_parts_to_file_with_checks (SaveMimePartInfo *info);
2590 save_mime_part_info_free (SaveMimePartInfo *info, gboolean with_struct)
2594 for (node = info->pairs; node != NULL; node = g_list_next (node)) {
2595 SaveMimePartPair *pair = (SaveMimePartPair *) node->data;
2596 g_free (pair->filename);
2597 g_object_unref (pair->part);
2598 g_slice_free (SaveMimePartPair, pair);
2600 g_list_free (info->pairs);
2603 gtk_widget_destroy (info->banner);
2604 g_slice_free (SaveMimePartInfo, info);
2609 idle_save_mime_part_show_result (SaveMimePartInfo *info)
2611 if (info->pairs != NULL) {
2612 save_mime_part_to_file (info);
2614 /* This is a GDK lock because we are an idle callback and
2615 * hildon_banner_show_information is or does Gtk+ code */
2617 gdk_threads_enter (); /* CHECKED */
2618 save_mime_part_info_free (info, TRUE);
2619 if (info->result == GNOME_VFS_OK) {
2620 hildon_banner_show_information (NULL, NULL, _CS("sfil_ib_saved"));
2621 } else if (info->result == GNOME_VFS_ERROR_NO_SPACE) {
2622 hildon_banner_show_information (NULL, NULL, dgettext("ke-recv",
2623 "cerm_device_memory_full"));
2625 hildon_banner_show_information (NULL, NULL, _("mail_ib_file_operation_failed"));
2627 gdk_threads_leave (); /* CHECKED */
2634 save_mime_part_to_file (SaveMimePartInfo *info)
2636 GnomeVFSHandle *handle;
2638 SaveMimePartPair *pair = (SaveMimePartPair *) info->pairs->data;
2640 info->result = gnome_vfs_create (&handle, pair->filename, GNOME_VFS_OPEN_WRITE, FALSE, 0644);
2641 if (info->result == GNOME_VFS_OK) {
2642 GError *error = NULL;
2643 stream = tny_vfs_stream_new (handle);
2644 if (tny_mime_part_decode_to_stream (pair->part, stream, &error) < 0) {
2645 g_warning ("modest: could not save attachment %s: %d (%s)\n", pair->filename, error?error->code:-1, error?error->message:"Unknown error");
2647 info->result = GNOME_VFS_ERROR_IO;
2649 g_object_unref (G_OBJECT (stream));
2650 g_object_unref (pair->part);
2651 g_slice_free (SaveMimePartPair, pair);
2652 info->pairs = g_list_delete_link (info->pairs, info->pairs);
2654 g_warning ("modest: could not create save attachment %s: %s\n", pair->filename, gnome_vfs_result_to_string (info->result));
2655 save_mime_part_info_free (info, FALSE);
2658 g_idle_add ((GSourceFunc) idle_save_mime_part_show_result, info);
2663 save_mime_parts_to_file_with_checks (SaveMimePartInfo *info)
2665 gboolean is_ok = TRUE;
2666 gint replaced_files = 0;
2667 const GList *files = info->pairs;
2670 for (iter = files; (iter != NULL) && (replaced_files < 2); iter = g_list_next(iter)) {
2671 SaveMimePartPair *pair = iter->data;
2672 if (modest_utils_file_exists (pair->filename)) {
2676 if (replaced_files) {
2677 GtkWidget *confirm_overwrite_dialog;
2678 const gchar *message = (replaced_files == 1) ?
2679 _FM("docm_nc_replace_file") : _FM("docm_nc_replace_multiple");
2680 confirm_overwrite_dialog = hildon_note_new_confirmation (NULL, message);
2681 if (gtk_dialog_run (GTK_DIALOG (confirm_overwrite_dialog)) != GTK_RESPONSE_OK) {
2684 gtk_widget_destroy (confirm_overwrite_dialog);
2688 save_mime_part_info_free (info, TRUE);
2690 GtkWidget *banner = hildon_banner_show_animation (NULL, NULL,
2691 _CS("sfil_ib_saving"));
2692 info->banner = banner;
2693 g_thread_create ((GThreadFunc)save_mime_part_to_file, info, FALSE, NULL);
2699 save_attachments_response (GtkDialog *dialog,
2703 TnyList *mime_parts;
2705 GList *files_to_save = NULL;
2707 mime_parts = TNY_LIST (user_data);
2709 if (arg1 != GTK_RESPONSE_OK)
2712 chooser_uri = gtk_file_chooser_get_uri (GTK_FILE_CHOOSER (dialog));
2714 if (!modest_utils_folder_writable (chooser_uri)) {
2715 hildon_banner_show_information
2716 (NULL, NULL, dgettext("hildon-fm", "sfil_ib_readonly_location"));
2720 iter = tny_list_create_iterator (mime_parts);
2721 while (!tny_iterator_is_done (iter)) {
2722 TnyMimePart *mime_part = (TnyMimePart *) tny_iterator_get_current (iter);
2724 if ((modest_tny_mime_part_is_attachment_for_modest (mime_part)) &&
2725 !tny_mime_part_is_purged (mime_part) &&
2726 (tny_mime_part_get_filename (mime_part) != NULL)) {
2727 SaveMimePartPair *pair;
2729 pair = g_slice_new0 (SaveMimePartPair);
2731 if (tny_list_get_length (mime_parts) > 1) {
2733 gnome_vfs_escape_slashes (tny_mime_part_get_filename (mime_part));
2734 pair->filename = g_build_filename (chooser_uri, escaped, NULL);
2737 pair->filename = g_strdup (chooser_uri);
2739 pair->part = mime_part;
2740 files_to_save = g_list_prepend (files_to_save, pair);
2742 tny_iterator_next (iter);
2744 g_object_unref (iter);
2746 g_free (chooser_uri);
2748 if (files_to_save != NULL) {
2749 SaveMimePartInfo *info = g_slice_new0 (SaveMimePartInfo);
2750 info->pairs = files_to_save;
2751 info->result = TRUE;
2752 save_mime_parts_to_file_with_checks (info);
2756 /* Free and close the dialog */
2757 g_object_unref (mime_parts);
2758 gtk_widget_destroy (GTK_WIDGET (dialog));
2762 modest_msg_view_window_save_attachments (ModestMsgViewWindow *window, TnyList *mime_parts)
2764 ModestMsgViewWindowPrivate *priv;
2765 GtkWidget *save_dialog = NULL;
2766 gchar *folder = NULL;
2767 gchar *filename = NULL;
2768 gchar *save_multiple_str = NULL;
2770 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window));
2771 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
2773 if (mime_parts == NULL) {
2774 mime_parts = modest_msg_view_get_selected_attachments (MODEST_MSG_VIEW (priv->msg_view));
2775 if (mime_parts == NULL || tny_list_get_length (mime_parts) == 0)
2778 g_object_ref (mime_parts);
2781 /* prepare dialog */
2782 if (tny_list_get_length (mime_parts) == 1) {
2784 /* only one attachment selected */
2785 iter = tny_list_create_iterator (mime_parts);
2786 TnyMimePart *mime_part = (TnyMimePart *) tny_iterator_get_current (iter);
2787 g_object_unref (iter);
2788 if (!modest_tny_mime_part_is_msg (mime_part) &&
2789 modest_tny_mime_part_is_attachment_for_modest (mime_part) &&
2790 !tny_mime_part_is_purged (mime_part)) {
2791 filename = g_strdup (tny_mime_part_get_filename (mime_part));
2793 /* TODO: show any error? */
2794 g_warning ("Tried to save a non-file attachment");
2795 g_object_unref (mime_parts);
2798 g_object_unref (mime_part);
2800 save_multiple_str = g_strdup_printf (_FM("sfil_va_number_of_objects_attachments"),
2801 tny_list_get_length (mime_parts));
2804 save_dialog = hildon_file_chooser_dialog_new (GTK_WINDOW (window),
2805 GTK_FILE_CHOOSER_ACTION_SAVE);
2808 folder = g_build_filename (g_get_home_dir (), g_getenv (MYDOCS_ENV), DOCS_FOLDER, NULL);
2809 gtk_file_chooser_set_current_folder (GTK_FILE_CHOOSER (save_dialog), folder);
2814 gtk_file_chooser_set_current_name (GTK_FILE_CHOOSER (save_dialog),
2819 /* if multiple, set multiple string */
2820 if (save_multiple_str) {
2821 g_object_set (G_OBJECT (save_dialog), "save-multiple", save_multiple_str, NULL);
2822 gtk_window_set_title (GTK_WINDOW (save_dialog), _FM("sfil_ti_save_objects_files"));
2825 /* We must run this asynchronously, because the hildon dialog
2826 performs a gtk_dialog_run by itself which leads to gdk
2828 g_signal_connect (save_dialog, "response",
2829 G_CALLBACK (save_attachments_response), mime_parts);
2831 gtk_widget_show_all (save_dialog);
2835 show_remove_attachment_information (gpointer userdata)
2837 ModestMsgViewWindow *window = (ModestMsgViewWindow *) userdata;
2838 ModestMsgViewWindowPrivate *priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
2840 /* We're outside the main lock */
2841 gdk_threads_enter ();
2843 if (priv->remove_attachment_banner != NULL) {
2844 gtk_widget_destroy (priv->remove_attachment_banner);
2845 g_object_unref (priv->remove_attachment_banner);
2848 priv->remove_attachment_banner = g_object_ref (
2849 hildon_banner_show_animation (NULL, NULL, _("mcen_me_inbox_remove_attachments")));
2851 gdk_threads_leave ();
2857 modest_msg_view_window_remove_attachments (ModestMsgViewWindow *window, gboolean get_all)
2859 ModestMsgViewWindowPrivate *priv;
2860 TnyList *mime_parts = NULL;
2861 gchar *confirmation_message;
2867 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window));
2868 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
2871 mime_parts = modest_msg_view_get_attachments (MODEST_MSG_VIEW (priv->msg_view));
2873 mime_parts = modest_msg_view_get_selected_attachments (MODEST_MSG_VIEW (priv->msg_view));
2875 /* Remove already purged messages from mime parts list */
2876 iter = tny_list_create_iterator (mime_parts);
2877 while (!tny_iterator_is_done (iter)) {
2878 TnyMimePart *part = TNY_MIME_PART (tny_iterator_get_current (iter));
2879 tny_iterator_next (iter);
2880 if (tny_mime_part_is_purged (part)) {
2881 tny_list_remove (mime_parts, (GObject *) part);
2883 g_object_unref (part);
2885 g_object_unref (iter);
2887 if (tny_list_get_length (mime_parts) == 0) {
2888 g_object_unref (mime_parts);
2892 n_attachments = tny_list_get_length (mime_parts);
2893 if (n_attachments == 1) {
2897 iter = tny_list_create_iterator (mime_parts);
2898 part = (TnyMimePart *) tny_iterator_get_current (iter);
2899 g_object_unref (iter);
2900 if (modest_tny_mime_part_is_msg (part)) {
2902 header = tny_msg_get_header (TNY_MSG (part));
2903 filename = tny_header_dup_subject (header);
2904 g_object_unref (header);
2905 if (filename == NULL)
2906 filename = g_strdup (_("mail_va_no_subject"));
2908 filename = g_strdup (tny_mime_part_get_filename (TNY_MIME_PART (part)));
2910 confirmation_message = g_strdup_printf (_("mcen_nc_purge_file_text"), filename);
2912 g_object_unref (part);
2914 confirmation_message = g_strdup_printf (ngettext("mcen_nc_purge_file_text",
2915 "mcen_nc_purge_files_text",
2916 n_attachments), n_attachments);
2918 response = modest_platform_run_confirmation_dialog (GTK_WINDOW (window),
2919 confirmation_message);
2920 g_free (confirmation_message);
2922 if (response != GTK_RESPONSE_OK) {
2923 g_object_unref (mime_parts);
2927 priv->purge_timeout = g_timeout_add (2000, show_remove_attachment_information, window);
2929 iter = tny_list_create_iterator (mime_parts);
2930 while (!tny_iterator_is_done (iter)) {
2933 part = (TnyMimePart *) tny_iterator_get_current (iter);
2934 tny_mime_part_set_purged (TNY_MIME_PART (part));
2935 g_object_unref (part);
2936 tny_iterator_next (iter);
2938 g_object_unref (iter);
2940 msg = tny_msg_view_get_msg (TNY_MSG_VIEW (priv->msg_view));
2941 tny_msg_view_clear (TNY_MSG_VIEW (priv->msg_view));
2942 tny_msg_rewrite_cache (msg);
2943 tny_msg_view_set_msg (TNY_MSG_VIEW (priv->msg_view), msg);
2944 g_object_unref (msg);
2946 g_object_unref (mime_parts);
2948 if (priv->purge_timeout > 0) {
2949 g_source_remove (priv->purge_timeout);
2950 priv->purge_timeout = 0;
2953 if (priv->remove_attachment_banner) {
2954 gtk_widget_destroy (priv->remove_attachment_banner);
2955 g_object_unref (priv->remove_attachment_banner);
2956 priv->remove_attachment_banner = NULL;
2964 update_window_title (ModestMsgViewWindow *window)
2966 ModestMsgViewWindowPrivate *priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
2968 TnyHeader *header = NULL;
2969 gchar *subject = NULL;
2971 msg = tny_msg_view_get_msg (TNY_MSG_VIEW (priv->msg_view));
2974 header = tny_msg_get_header (msg);
2975 subject = tny_header_dup_subject (header);
2976 g_object_unref (header);
2977 g_object_unref (msg);
2980 if ((subject == NULL)||(subject[0] == '\0')) {
2982 subject = g_strdup (_("mail_va_no_subject"));
2985 gtk_window_set_title (GTK_WINDOW (window), subject);
2989 static void on_move_focus (GtkWidget *widget,
2990 GtkDirectionType direction,
2993 g_signal_stop_emission_by_name (G_OBJECT (widget), "move-focus");
2997 fetch_image_open_stream (TnyStreamCache *self, gint64 *expected_size, gchar *uri)
2999 GnomeVFSResult result;
3000 GnomeVFSHandle *handle = NULL;
3001 GnomeVFSFileInfo *info = NULL;
3004 result = gnome_vfs_open (&handle, uri, GNOME_VFS_OPEN_READ);
3005 if (result != GNOME_VFS_OK) {
3010 info = gnome_vfs_file_info_new ();
3011 result = gnome_vfs_get_file_info_from_handle (handle, info, GNOME_VFS_FILE_INFO_DEFAULT);
3012 if (result != GNOME_VFS_OK || ! (info->valid_fields & GNOME_VFS_FILE_INFO_FIELDS_SIZE)) {
3013 /* We put a "safe" default size for going to cache */
3014 *expected_size = (300*1024);
3016 *expected_size = info->size;
3018 gnome_vfs_file_info_unref (info);
3020 stream = tny_vfs_stream_new (handle);
3029 TnyStream *output_stream;
3030 GtkWidget *msg_view;
3034 on_fetch_image_idle_refresh_view (gpointer userdata)
3037 FetchImageData *fidata = (FetchImageData *) userdata;
3038 g_message ("REFRESH VIEW");
3039 if (GTK_WIDGET_DRAWABLE (fidata->msg_view)) {
3040 g_message ("QUEUING DRAW");
3041 gtk_widget_queue_draw (fidata->msg_view);
3043 g_object_unref (fidata->msg_view);
3044 g_slice_free (FetchImageData, fidata);
3049 on_fetch_image_thread (gpointer userdata)
3051 FetchImageData *fidata = (FetchImageData *) userdata;
3052 TnyStreamCache *cache;
3053 TnyStream *cache_stream;
3055 cache = modest_runtime_get_images_cache ();
3056 cache_stream = tny_stream_cache_get_stream (cache, fidata->cache_id, (TnyStreamCacheOpenStreamFetcher) fetch_image_open_stream, (gpointer) fidata->uri);
3057 g_free (fidata->cache_id);
3058 g_free (fidata->uri);
3060 if (cache_stream != NULL) {
3061 tny_stream_write_to_stream (cache_stream, fidata->output_stream);
3062 tny_stream_close (cache_stream);
3063 g_object_unref (cache_stream);
3066 tny_stream_close (fidata->output_stream);
3067 g_object_unref (fidata->output_stream);
3070 gdk_threads_enter ();
3071 g_idle_add (on_fetch_image_idle_refresh_view, fidata);
3072 gdk_threads_leave ();
3078 on_fetch_image (ModestMsgView *msgview,
3081 ModestMsgViewWindow *window)
3083 const gchar *current_account;
3084 ModestMsgViewWindowPrivate *priv;
3085 FetchImageData *fidata;
3087 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
3089 current_account = modest_window_get_active_account (MODEST_WINDOW (window));
3091 fidata = g_slice_new0 (FetchImageData);
3092 fidata->msg_view = g_object_ref (msgview);
3093 fidata->uri = g_strdup (uri);
3094 fidata->cache_id = modest_images_cache_get_id (current_account, uri);
3095 fidata->output_stream = g_object_ref (stream);
3097 if (g_thread_create (on_fetch_image_thread, fidata, FALSE, NULL) == NULL) {
3098 g_object_unref (fidata->output_stream);
3099 g_free (fidata->cache_id);
3100 g_free (fidata->uri);
3101 g_object_unref (fidata->msg_view);
3102 g_slice_free (FetchImageData, fidata);
3103 tny_stream_close (stream);
3111 add_to_menu (ModestMsgViewWindow *self,
3112 HildonAppMenu *menu,
3115 ModestDimmingRulesGroup *dimming_group,
3116 GCallback dimming_callback)
3120 button = gtk_button_new_with_label (label);
3121 g_signal_connect_after (G_OBJECT (button), "clicked",
3122 callback, (gpointer) self);
3123 modest_dimming_rules_group_add_widget_rule (dimming_group,
3126 MODEST_WINDOW (self));
3127 hildon_app_menu_append (menu, GTK_BUTTON (button));
3131 setup_menu (ModestMsgViewWindow *self, ModestDimmingRulesGroup *group)
3133 ModestMsgViewWindowPrivate *priv = NULL;
3134 GtkWidget *app_menu;
3136 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW(self));
3138 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
3140 app_menu = hildon_app_menu_new ();
3142 /* Settings menu buttons */
3143 add_to_menu (self, HILDON_APP_MENU (app_menu), _("mcen_me_inbox_replytoall"),
3144 G_CALLBACK (modest_ui_actions_on_reply_all),
3145 group, G_CALLBACK (modest_ui_dimming_rules_on_reply_msg));
3146 add_to_menu (self, HILDON_APP_MENU (app_menu), _("mcen_me_inbox_forward"),
3147 G_CALLBACK (modest_ui_actions_on_forward),
3148 group, G_CALLBACK (modest_ui_dimming_rules_on_reply_msg));
3150 add_to_menu (self, HILDON_APP_MENU (app_menu), _("mcen_me_inbox_mark_as_read"),
3151 G_CALLBACK (modest_ui_actions_on_mark_as_read),
3152 group, G_CALLBACK (modest_ui_dimming_rules_on_mark_as_read_msg_in_view));
3153 add_to_menu (self, HILDON_APP_MENU (app_menu), _("mcen_me_inbox_mark_as_unread"),
3154 G_CALLBACK (modest_ui_actions_on_mark_as_unread),
3155 group, G_CALLBACK (modest_ui_dimming_rules_on_mark_as_unread_msg_in_view));
3157 add_to_menu (self, HILDON_APP_MENU (app_menu), _("mcen_me_viewer_save_attachments"),
3158 G_CALLBACK (modest_ui_actions_save_attachments),
3159 group, G_CALLBACK (modest_ui_dimming_rules_on_save_attachments));
3160 add_to_menu (self, HILDON_APP_MENU (app_menu), _("mcen_me_inbox_remove_attachments"),
3161 G_CALLBACK (modest_ui_actions_remove_attachments),
3162 group, G_CALLBACK (modest_ui_dimming_rules_on_remove_attachments));
3164 add_to_menu (self, HILDON_APP_MENU (app_menu), _("mcen_me_viewer_newemail"),
3165 G_CALLBACK (modest_ui_actions_on_new_msg),
3166 group, G_CALLBACK (modest_ui_dimming_rules_on_new_msg));
3167 add_to_menu (self, HILDON_APP_MENU (app_menu), _("mcen_me_viewer_addtocontacts"),
3168 G_CALLBACK (modest_ui_actions_add_to_contacts),
3169 group, G_CALLBACK (modest_ui_dimming_rules_on_add_to_contacts));
3171 hildon_stackable_window_set_main_menu (HILDON_STACKABLE_WINDOW (self),
3172 HILDON_APP_MENU (app_menu));
3176 modest_msg_view_window_toggle_menu (HildonWindow *window,
3180 modest_ui_actions_check_menu_dimming_rules (MODEST_WINDOW (window));
3182 return HILDON_WINDOW_CLASS (parent_class)->toggle_menu (window, button, time);
3187 modest_msg_view_window_add_to_contacts (ModestMsgViewWindow *self)
3189 ModestMsgViewWindowPrivate *priv;
3190 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
3191 GSList *recipients = NULL;
3193 gboolean contacts_to_add = FALSE;
3195 msg = tny_msg_view_get_msg (TNY_MSG_VIEW (priv->msg_view));
3196 if (msg == NULL) return;
3197 recipients = modest_tny_msg_get_all_recipients_list (msg);
3199 if (recipients != NULL) {
3200 GtkWidget *picker_dialog;
3201 GtkWidget *selector;
3203 gchar *selected = NULL;
3205 selector = hildon_touch_selector_new_text ();
3206 g_object_ref (selector);
3208 for (node = recipients; node != NULL; node = g_slist_next (node)) {
3209 if (!modest_address_book_has_address ((const gchar *) node->data)) {
3210 hildon_touch_selector_append_text (HILDON_TOUCH_SELECTOR (selector),
3211 (const gchar *) node->data);
3212 contacts_to_add = TRUE;
3216 if (contacts_to_add) {
3219 picker_dialog = hildon_picker_dialog_new (GTK_WINDOW (self));
3220 gtk_window_set_title (GTK_WINDOW (picker_dialog), _("mcen_me_viewer_addtocontacts"));
3222 hildon_picker_dialog_set_selector (HILDON_PICKER_DIALOG (picker_dialog),
3223 HILDON_TOUCH_SELECTOR (selector));
3225 picker_result = gtk_dialog_run (GTK_DIALOG (picker_dialog));
3227 if (picker_result == GTK_RESPONSE_OK) {
3228 selected = hildon_touch_selector_get_current_text (HILDON_TOUCH_SELECTOR (selector));
3230 gtk_widget_destroy (picker_dialog);
3233 modest_address_book_add_address (selected);
3238 g_object_unref (selector);
3243 if (recipients) {g_slist_foreach (recipients, (GFunc) g_free, NULL); g_slist_free (recipients);}
3244 g_object_unref (msg);