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));
975 g_object_ref (priv->header_model);
978 priv->row_reference = gtk_tree_row_reference_copy (row_reference);
979 priv->next_row_reference = gtk_tree_row_reference_copy (row_reference);
980 select_next_valid_row (priv->header_model, &(priv->next_row_reference), TRUE, priv->is_outbox);
982 priv->row_reference = NULL;
983 priv->next_row_reference = NULL;
986 /* Connect signals */
987 priv->row_changed_handler =
988 g_signal_connect (GTK_TREE_MODEL(priv->header_model), "row-changed",
989 G_CALLBACK(modest_msg_view_window_on_row_changed),
991 priv->row_deleted_handler =
992 g_signal_connect (GTK_TREE_MODEL(priv->header_model), "row-deleted",
993 G_CALLBACK(modest_msg_view_window_on_row_deleted),
995 priv->row_inserted_handler =
996 g_signal_connect (GTK_TREE_MODEL(priv->header_model), "row-inserted",
997 G_CALLBACK(modest_msg_view_window_on_row_inserted),
999 priv->rows_reordered_handler =
1000 g_signal_connect(GTK_TREE_MODEL(priv->header_model), "rows-reordered",
1001 G_CALLBACK(modest_msg_view_window_on_row_reordered),
1004 if (header_view != NULL){
1005 modest_header_view_add_observer(header_view,
1006 MODEST_HEADER_VIEW_OBSERVER(window));
1009 tny_msg_view_set_msg (TNY_MSG_VIEW (priv->msg_view), NULL);
1011 path = gtk_tree_row_reference_get_path (row_reference);
1012 if (gtk_tree_model_get_iter (priv->header_model, &iter, path)) {
1014 gtk_tree_model_get (priv->header_model, &iter,
1015 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
1017 message_reader (window, priv, header, row_reference);
1019 gtk_tree_path_free (path);
1021 /* Check dimming rules */
1022 modest_ui_actions_check_toolbar_dimming_rules (MODEST_WINDOW (window));
1023 modest_ui_actions_check_menu_dimming_rules (MODEST_WINDOW (window));
1024 modest_window_check_dimming_rules_group (MODEST_WINDOW (window), MODEST_DIMMING_RULES_CLIPBOARD);
1026 return MODEST_WINDOW(window);
1030 modest_msg_view_window_new_for_search_result (TnyMsg *msg,
1031 const gchar *modest_account_name,
1032 const gchar *msg_uid)
1034 ModestMsgViewWindow *window = NULL;
1035 ModestMsgViewWindowPrivate *priv = NULL;
1036 ModestWindowMgr *mgr = NULL;
1038 mgr = modest_runtime_get_window_mgr ();
1039 window = MODEST_MSG_VIEW_WINDOW (modest_window_mgr_get_msg_view_window (mgr));
1040 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), NULL);
1041 modest_msg_view_window_construct (window, modest_account_name, msg_uid);
1043 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1045 /* Remember that this is a search result,
1046 * so we can disable some UI appropriately: */
1047 priv->is_search_result = TRUE;
1049 tny_msg_view_set_msg (TNY_MSG_VIEW (priv->msg_view), msg);
1051 update_window_title (window);
1052 /* gtk_widget_show_all (GTK_WIDGET (window));*/
1053 modest_msg_view_window_update_priority (window);
1055 /* Check dimming rules */
1056 modest_ui_actions_check_toolbar_dimming_rules (MODEST_WINDOW (window));
1057 modest_ui_actions_check_menu_dimming_rules (MODEST_WINDOW (window));
1058 modest_window_check_dimming_rules_group (MODEST_WINDOW (window), MODEST_DIMMING_RULES_CLIPBOARD);
1060 return MODEST_WINDOW(window);
1064 modest_msg_view_window_new_for_attachment (TnyMsg *msg,
1065 const gchar *modest_account_name,
1066 const gchar *msg_uid)
1068 GObject *obj = NULL;
1069 ModestMsgViewWindowPrivate *priv;
1070 ModestWindowMgr *mgr = NULL;
1072 g_return_val_if_fail (msg, NULL);
1073 mgr = modest_runtime_get_window_mgr ();
1074 obj = G_OBJECT (modest_window_mgr_get_msg_view_window (mgr));
1075 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (obj);
1076 modest_msg_view_window_construct (MODEST_MSG_VIEW_WINDOW (obj),
1077 modest_account_name, msg_uid);
1079 tny_msg_view_set_msg (TNY_MSG_VIEW (priv->msg_view), msg);
1080 update_window_title (MODEST_MSG_VIEW_WINDOW (obj));
1082 /* gtk_widget_show_all (GTK_WIDGET (obj)); */
1084 /* Check dimming rules */
1085 modest_ui_actions_check_toolbar_dimming_rules (MODEST_WINDOW (obj));
1086 modest_ui_actions_check_menu_dimming_rules (MODEST_WINDOW (obj));
1087 modest_window_check_dimming_rules_group (MODEST_WINDOW (obj), MODEST_DIMMING_RULES_CLIPBOARD);
1089 return MODEST_WINDOW(obj);
1093 modest_msg_view_window_on_row_changed (GtkTreeModel *header_model,
1096 ModestMsgViewWindow *window)
1098 check_dimming_rules_after_change (window);
1102 modest_msg_view_window_on_row_deleted(GtkTreeModel *header_model,
1104 ModestMsgViewWindow *window)
1106 check_dimming_rules_after_change (window);
1108 /* The window could have dissapeared */
1111 check_dimming_rules_after_change (ModestMsgViewWindow *window)
1113 modest_ui_actions_check_menu_dimming_rules (MODEST_WINDOW (window));
1114 modest_ui_actions_check_toolbar_dimming_rules (MODEST_WINDOW (window));
1118 /* On insertions we check if the folder still has the message we are
1119 * showing or do not. If do not, we do nothing. Which means we are still
1120 * not attached to any header folder and thus next/prev buttons are
1121 * still dimmed. Once the message that is shown by msg-view is found, the
1122 * new model of header-view will be attached and the references will be set.
1123 * On each further insertions dimming rules will be checked. However
1124 * this requires extra CPU time at least works.
1125 * (An message might be deleted from TnyFolder and thus will not be
1126 * inserted into the model again for example if it is removed by the
1127 * imap server and the header view is refreshed.)
1130 modest_msg_view_window_on_row_inserted (GtkTreeModel *model,
1131 GtkTreePath *tree_path,
1132 GtkTreeIter *tree_iter,
1133 ModestMsgViewWindow *window)
1135 ModestMsgViewWindowPrivate *priv = NULL;
1136 TnyHeader *header = NULL;
1138 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window));
1139 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1141 g_assert (model == priv->header_model);
1143 /* Check if the newly inserted message is the same we are actually
1144 * showing. IF not, we should remain detached from the header model
1145 * and thus prev and next toolbar buttons should remain dimmed. */
1146 gtk_tree_model_get (model, tree_iter,
1147 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
1150 if (TNY_IS_HEADER (header)) {
1153 uid = modest_tny_folder_get_header_unique_id (header);
1154 if (!g_str_equal(priv->msg_uid, uid)) {
1155 check_dimming_rules_after_change (window);
1157 g_object_unref (G_OBJECT(header));
1161 g_object_unref(G_OBJECT(header));
1164 if (priv->row_reference) {
1165 gtk_tree_row_reference_free (priv->row_reference);
1168 /* Setup row_reference for the actual msg. */
1169 priv->row_reference = gtk_tree_row_reference_new (priv->header_model, tree_path);
1170 if (priv->row_reference == NULL) {
1171 g_warning("No reference for msg header item.");
1175 /* Now set up next_row_reference. */
1176 if (priv->next_row_reference) {
1177 gtk_tree_row_reference_free (priv->next_row_reference);
1180 priv->next_row_reference = gtk_tree_row_reference_copy (priv->row_reference);
1181 select_next_valid_row (priv->header_model,
1182 &(priv->next_row_reference), FALSE, priv->is_outbox);
1184 /* Connect the remaining callbacks to become able to detect
1185 * changes in header-view. */
1186 priv->row_changed_handler =
1187 g_signal_connect (priv->header_model, "row-changed",
1188 G_CALLBACK (modest_msg_view_window_on_row_changed),
1190 priv->row_deleted_handler =
1191 g_signal_connect (priv->header_model, "row-deleted",
1192 G_CALLBACK (modest_msg_view_window_on_row_deleted),
1194 priv->rows_reordered_handler =
1195 g_signal_connect (priv->header_model, "rows-reordered",
1196 G_CALLBACK (modest_msg_view_window_on_row_reordered),
1199 check_dimming_rules_after_change (window);
1203 modest_msg_view_window_on_row_reordered (GtkTreeModel *header_model,
1207 ModestMsgViewWindow *window)
1209 ModestMsgViewWindowPrivate *priv = NULL;
1210 gboolean already_changed = FALSE;
1212 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(window);
1214 /* If the current row was reordered select the proper next
1215 valid row. The same if the next row reference changes */
1216 if (priv->row_reference &&
1217 gtk_tree_row_reference_valid (priv->row_reference)) {
1219 path = gtk_tree_row_reference_get_path (priv->row_reference);
1220 if (gtk_tree_path_compare (path, arg1) == 0) {
1221 if (priv->next_row_reference) {
1222 gtk_tree_row_reference_free (priv->next_row_reference);
1224 priv->next_row_reference = gtk_tree_row_reference_copy (priv->row_reference);
1225 select_next_valid_row (header_model, &(priv->next_row_reference), FALSE, priv->is_outbox);
1226 already_changed = TRUE;
1228 gtk_tree_path_free (path);
1230 if (!already_changed &&
1231 priv->next_row_reference &&
1232 gtk_tree_row_reference_valid (priv->next_row_reference)) {
1234 path = gtk_tree_row_reference_get_path (priv->next_row_reference);
1235 if (gtk_tree_path_compare (path, arg1) == 0) {
1236 if (priv->next_row_reference) {
1237 gtk_tree_row_reference_free (priv->next_row_reference);
1239 priv->next_row_reference = gtk_tree_row_reference_copy (priv->row_reference);
1240 select_next_valid_row (header_model, &(priv->next_row_reference), FALSE, priv->is_outbox);
1242 gtk_tree_path_free (path);
1244 check_dimming_rules_after_change (window);
1247 /* The modest_msg_view_window_update_model_replaced implements update
1248 * function for ModestHeaderViewObserver. Checks whether the TnyFolder
1249 * actually belongs to the header-view is the same as the TnyFolder of
1250 * the message of msg-view or not. If they are different, there is
1251 * nothing to do. If they are the same, then the model has replaced and
1252 * the reference in msg-view shall be replaced from the old model to
1253 * the new model. In this case the view will be detached from it's
1254 * header folder. From this point the next/prev buttons are dimmed.
1257 modest_msg_view_window_update_model_replaced (ModestHeaderViewObserver *observer,
1258 GtkTreeModel *model,
1259 const gchar *tny_folder_id)
1261 ModestMsgViewWindowPrivate *priv = NULL;
1262 ModestMsgViewWindow *window = NULL;
1264 g_assert(MODEST_IS_HEADER_VIEW_OBSERVER(observer));
1265 g_assert(MODEST_IS_MSG_VIEW_WINDOW(observer));
1267 window = MODEST_MSG_VIEW_WINDOW(observer);
1268 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(window);
1270 /* If there is an other folder in the header-view then we do
1271 * not care about it's model (msg list). Else if the
1272 * header-view shows the folder the msg shown by us is in, we
1273 * shall replace our model reference and make some check. */
1274 if(model == NULL || tny_folder_id == NULL ||
1275 (priv->header_folder_id && !g_str_equal(tny_folder_id, priv->header_folder_id)))
1278 /* Model is changed(replaced), so we should forget the old
1279 * one. Because there might be other references and there
1280 * might be some change on the model even if we unreferenced
1281 * it, we need to disconnect our signals here. */
1282 if (priv->header_model) {
1283 if (g_signal_handler_is_connected(G_OBJECT (priv->header_model),
1284 priv->row_changed_handler))
1285 g_signal_handler_disconnect(G_OBJECT (priv->header_model),
1286 priv->row_changed_handler);
1287 if (g_signal_handler_is_connected(G_OBJECT (priv->header_model),
1288 priv->row_deleted_handler))
1289 g_signal_handler_disconnect(G_OBJECT (priv->header_model),
1290 priv->row_deleted_handler);
1291 if (g_signal_handler_is_connected(G_OBJECT (priv->header_model),
1292 priv->row_inserted_handler))
1293 g_signal_handler_disconnect(G_OBJECT (priv->header_model),
1294 priv->row_inserted_handler);
1295 if (g_signal_handler_is_connected(G_OBJECT (priv->header_model),
1296 priv->rows_reordered_handler))
1297 g_signal_handler_disconnect(G_OBJECT (priv->header_model),
1298 priv->rows_reordered_handler);
1301 if (priv->row_reference)
1302 gtk_tree_row_reference_free (priv->row_reference);
1303 if (priv->next_row_reference)
1304 gtk_tree_row_reference_free (priv->next_row_reference);
1305 g_object_unref(priv->header_model);
1308 priv->row_changed_handler = 0;
1309 priv->row_deleted_handler = 0;
1310 priv->row_inserted_handler = 0;
1311 priv->rows_reordered_handler = 0;
1312 priv->next_row_reference = NULL;
1313 priv->row_reference = NULL;
1314 priv->header_model = NULL;
1317 priv->header_model = g_object_ref (model);
1319 /* Also we must connect to the new model for row insertions.
1320 * Only for insertions now. We will need other ones only after
1321 * the msg is show by msg-view is added to the new model. */
1322 priv->row_inserted_handler =
1323 g_signal_connect (priv->header_model, "row-inserted",
1324 G_CALLBACK(modest_msg_view_window_on_row_inserted),
1327 modest_ui_actions_check_menu_dimming_rules(MODEST_WINDOW(window));
1328 modest_ui_actions_check_toolbar_dimming_rules(MODEST_WINDOW(window));
1332 modest_msg_view_window_toolbar_on_transfer_mode (ModestMsgViewWindow *self)
1334 ModestMsgViewWindowPrivate *priv= NULL;
1336 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self), FALSE);
1337 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
1339 return priv->progress_hint;
1343 modest_msg_view_window_get_header (ModestMsgViewWindow *self)
1345 ModestMsgViewWindowPrivate *priv= NULL;
1347 TnyHeader *header = NULL;
1348 GtkTreePath *path = NULL;
1351 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self), NULL);
1352 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
1354 /* If the message was not obtained from a treemodel,
1355 * for instance if it was opened directly by the search UI:
1357 if (priv->header_model == NULL ||
1358 priv->row_reference == NULL ||
1359 !gtk_tree_row_reference_valid (priv->row_reference)) {
1360 msg = modest_msg_view_window_get_message (self);
1362 header = tny_msg_get_header (msg);
1363 g_object_unref (msg);
1368 /* Get iter of the currently selected message in the header view: */
1369 path = gtk_tree_row_reference_get_path (priv->row_reference);
1370 g_return_val_if_fail (path != NULL, NULL);
1371 gtk_tree_model_get_iter (priv->header_model,
1375 /* Get current message header */
1376 gtk_tree_model_get (priv->header_model, &iter,
1377 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
1380 gtk_tree_path_free (path);
1385 modest_msg_view_window_get_message (ModestMsgViewWindow *self)
1387 ModestMsgViewWindowPrivate *priv;
1389 g_return_val_if_fail (self, NULL);
1391 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(self);
1393 return tny_msg_view_get_msg (TNY_MSG_VIEW (priv->msg_view));
1397 modest_msg_view_window_get_message_uid (ModestMsgViewWindow *self)
1399 ModestMsgViewWindowPrivate *priv;
1401 g_return_val_if_fail (self, NULL);
1403 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
1405 return (const gchar*) priv->msg_uid;
1409 modest_msg_view_window_toggle_find_toolbar (GtkToggleAction *toggle,
1412 ModestMsgViewWindow *window = MODEST_MSG_VIEW_WINDOW (data);
1413 ModestMsgViewWindowPrivate *priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1414 ModestWindowPrivate *parent_priv = MODEST_WINDOW_GET_PRIVATE (window);
1418 is_active = gtk_toggle_action_get_active (toggle);
1421 gtk_widget_show (priv->find_toolbar);
1422 hildon_find_toolbar_highlight_entry (HILDON_FIND_TOOLBAR (priv->find_toolbar), TRUE);
1424 gtk_widget_hide (priv->find_toolbar);
1425 modest_msg_view_grab_focus (MODEST_MSG_VIEW (priv->msg_view));
1428 /* update the toggle buttons status */
1429 action = gtk_ui_manager_get_action (parent_priv->ui_manager, "/ToolBar/FindInMessage");
1431 modest_utils_toggle_action_set_active_block_notify (GTK_TOGGLE_ACTION (action), is_active);
1436 modest_msg_view_window_find_toolbar_close (GtkWidget *widget,
1437 ModestMsgViewWindow *obj)
1439 GtkToggleAction *toggle;
1440 ModestWindowPrivate *parent_priv;
1441 ModestMsgViewWindowPrivate *priv;
1443 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (obj);
1444 parent_priv = MODEST_WINDOW_GET_PRIVATE (obj);
1446 toggle = GTK_TOGGLE_ACTION (gtk_ui_manager_get_action (parent_priv->ui_manager, "/ToolBar/FindInMessage"));
1447 gtk_toggle_action_set_active (toggle, FALSE);
1448 modest_msg_view_grab_focus (MODEST_MSG_VIEW (priv->msg_view));
1452 modest_msg_view_window_find_toolbar_search (GtkWidget *widget,
1453 ModestMsgViewWindow *obj)
1455 gchar *current_search;
1456 ModestMsgViewWindowPrivate *priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (obj);
1458 if (modest_mime_part_view_is_empty (MODEST_MIME_PART_VIEW (priv->msg_view))) {
1459 hildon_banner_show_information (NULL, NULL, _("mail_ib_nothing_to_find"));
1463 g_object_get (G_OBJECT (widget), "prefix", ¤t_search, NULL);
1465 if ((current_search == NULL) || (strcmp (current_search, "") == 0)) {
1466 g_free (current_search);
1467 hildon_banner_show_information (NULL, NULL, dgettext("hildon-common-strings", "ecdg_ib_find_rep_enter_text"));
1471 if ((priv->last_search == NULL) || (strcmp (priv->last_search, current_search) != 0)) {
1473 g_free (priv->last_search);
1474 priv->last_search = g_strdup (current_search);
1475 result = modest_isearch_view_search (MODEST_ISEARCH_VIEW (priv->msg_view),
1478 hildon_banner_show_information (NULL, NULL, dgettext("hildon-libs", "ckct_ib_find_no_matches"));
1479 g_free (priv->last_search);
1480 priv->last_search = NULL;
1482 modest_msg_view_grab_focus (MODEST_MSG_VIEW (priv->msg_view));
1483 hildon_find_toolbar_highlight_entry (HILDON_FIND_TOOLBAR (priv->find_toolbar), TRUE);
1486 if (!modest_isearch_view_search_next (MODEST_ISEARCH_VIEW (priv->msg_view))) {
1487 hildon_banner_show_information (NULL, NULL, dgettext("hildon-libs", "ckct_ib_find_search_complete"));
1488 g_free (priv->last_search);
1489 priv->last_search = NULL;
1491 modest_msg_view_grab_focus (MODEST_MSG_VIEW (priv->msg_view));
1492 hildon_find_toolbar_highlight_entry (HILDON_FIND_TOOLBAR (priv->find_toolbar), TRUE);
1496 g_free (current_search);
1501 modest_msg_view_window_set_zoom (ModestWindow *window,
1504 ModestMsgViewWindowPrivate *priv;
1505 ModestWindowPrivate *parent_priv;
1507 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window));
1509 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1510 parent_priv = MODEST_WINDOW_GET_PRIVATE (window);
1511 modest_zoomable_set_zoom (MODEST_ZOOMABLE (priv->msg_view), zoom);
1516 modest_msg_view_window_get_zoom (ModestWindow *window)
1518 ModestMsgViewWindowPrivate *priv;
1520 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), 1.0);
1522 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1523 return modest_zoomable_get_zoom (MODEST_ZOOMABLE (priv->msg_view));
1527 modest_msg_view_window_zoom_plus (ModestWindow *window)
1530 ModestMsgViewWindowPrivate *priv;
1532 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), 1.0);
1533 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1535 zoom_level = modest_zoomable_get_zoom (MODEST_ZOOMABLE (priv->msg_view));
1537 if (zoom_level >= 2.0) {
1538 hildon_banner_show_information (NULL, NULL, dgettext("hildon-common-strings", "ckct_ib_max_zoom_level_reached"));
1540 } else if (zoom_level >= 1.5) {
1542 } else if (zoom_level >= 1.2) {
1544 } else if (zoom_level >= 1.0) {
1546 } else if (zoom_level >= 0.8) {
1548 } else if (zoom_level >= 0.5) {
1554 /* set zoom level */
1555 modest_zoomable_set_zoom (MODEST_ZOOMABLE (priv->msg_view), zoom_level);
1562 modest_msg_view_window_zoom_minus (ModestWindow *window)
1565 ModestMsgViewWindowPrivate *priv;
1567 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), 1.0);
1568 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1570 zoom_level = modest_zoomable_get_zoom (MODEST_ZOOMABLE (priv->msg_view));
1572 if (zoom_level <= 0.5) {
1573 hildon_banner_show_information (NULL, NULL, dgettext("hildon-common-strings", "ckct_ib_min_zoom_level_reached"));
1575 } else if (zoom_level <= 0.8) {
1577 } else if (zoom_level <= 1.0) {
1579 } else if (zoom_level <= 1.2) {
1581 } else if (zoom_level <= 1.5) {
1583 } else if (zoom_level <= 2.0) {
1589 /* set zoom level */
1590 modest_zoomable_set_zoom (MODEST_ZOOMABLE (priv->msg_view), zoom_level);
1597 modest_msg_view_window_key_event (GtkWidget *window,
1603 focus = gtk_window_get_focus (GTK_WINDOW (window));
1605 /* for the find toolbar case */
1606 if (focus && GTK_IS_ENTRY (focus)) {
1607 if (event->keyval == GDK_BackSpace) {
1609 copy = gdk_event_copy ((GdkEvent *) event);
1610 gtk_widget_event (focus, copy);
1611 gdk_event_free (copy);
1616 if (event->keyval == GDK_Up || event->keyval == GDK_KP_Up ||
1617 event->keyval == GDK_Down || event->keyval == GDK_KP_Down ||
1618 event->keyval == GDK_Page_Up || event->keyval == GDK_KP_Page_Up ||
1619 event->keyval == GDK_Page_Down || event->keyval == GDK_KP_Page_Down ||
1620 event->keyval == GDK_Home || event->keyval == GDK_KP_Home ||
1621 event->keyval == GDK_End || event->keyval == GDK_KP_End) {
1622 /* ModestMsgViewWindowPrivate *priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window); */
1623 /* gboolean return_value; */
1625 if (event->type == GDK_KEY_PRESS) {
1626 GtkScrollType scroll_type;
1628 switch (event->keyval) {
1631 scroll_type = GTK_SCROLL_STEP_UP; break;
1634 scroll_type = GTK_SCROLL_STEP_DOWN; break;
1636 case GDK_KP_Page_Up:
1637 scroll_type = GTK_SCROLL_PAGE_UP; break;
1639 case GDK_KP_Page_Down:
1640 scroll_type = GTK_SCROLL_PAGE_DOWN; break;
1643 scroll_type = GTK_SCROLL_START; break;
1646 scroll_type = GTK_SCROLL_END; break;
1647 default: scroll_type = GTK_SCROLL_NONE;
1650 /* g_signal_emit_by_name (G_OBJECT (priv->main_scroll), "scroll-child", */
1651 /* scroll_type, FALSE, &return_value); */
1662 modest_msg_view_window_last_message_selected (ModestMsgViewWindow *window)
1665 ModestMsgViewWindowPrivate *priv;
1666 GtkTreeIter tmp_iter;
1667 gboolean is_last_selected;
1669 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), TRUE);
1670 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1672 /*if no model (so no rows at all), then virtually we are the last*/
1673 if (!priv->header_model || !priv->row_reference)
1676 if (!gtk_tree_row_reference_valid (priv->row_reference))
1679 path = gtk_tree_row_reference_get_path (priv->row_reference);
1683 is_last_selected = TRUE;
1684 while (is_last_selected) {
1686 gtk_tree_path_next (path);
1687 if (!gtk_tree_model_get_iter (priv->header_model, &tmp_iter, path))
1689 gtk_tree_model_get (priv->header_model, &tmp_iter,
1690 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
1693 if (msg_is_visible (header, priv->is_outbox))
1694 is_last_selected = FALSE;
1695 g_object_unref(G_OBJECT(header));
1698 gtk_tree_path_free (path);
1699 return is_last_selected;
1703 modest_msg_view_window_has_headers_model (ModestMsgViewWindow *window)
1705 ModestMsgViewWindowPrivate *priv;
1707 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), TRUE);
1708 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1710 return priv->header_model != NULL;
1714 modest_msg_view_window_is_search_result (ModestMsgViewWindow *window)
1716 ModestMsgViewWindowPrivate *priv;
1718 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), TRUE);
1719 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1721 return priv->is_search_result;
1725 msg_is_visible (TnyHeader *header, gboolean check_outbox)
1727 if ((tny_header_get_flags(header) & TNY_HEADER_FLAG_DELETED))
1729 if (!check_outbox) {
1732 ModestTnySendQueueStatus status;
1733 status = modest_tny_all_send_queues_get_msg_status (header);
1734 return ((status != MODEST_TNY_SEND_QUEUE_FAILED) &&
1735 (status != MODEST_TNY_SEND_QUEUE_SENDING));
1740 modest_msg_view_window_first_message_selected (ModestMsgViewWindow *window)
1743 ModestMsgViewWindowPrivate *priv;
1744 gboolean is_first_selected;
1745 GtkTreeIter tmp_iter;
1747 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), TRUE);
1748 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1750 /*if no model (so no rows at all), then virtually we are the first*/
1751 if (!priv->header_model || !priv->row_reference)
1754 if (!gtk_tree_row_reference_valid (priv->row_reference))
1757 path = gtk_tree_row_reference_get_path (priv->row_reference);
1761 is_first_selected = TRUE;
1762 while (is_first_selected) {
1764 if(!gtk_tree_path_prev (path))
1766 /* Here the 'if' is needless for logic, but let make sure
1767 * iter is valid for gtk_tree_model_get. */
1768 if (!gtk_tree_model_get_iter (priv->header_model, &tmp_iter, path))
1770 gtk_tree_model_get (priv->header_model, &tmp_iter,
1771 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
1774 if (msg_is_visible (header, priv->is_outbox))
1775 is_first_selected = FALSE;
1776 g_object_unref(G_OBJECT(header));
1779 gtk_tree_path_free (path);
1780 return is_first_selected;
1785 GtkTreeRowReference *row_reference;
1789 message_reader_performer (gboolean canceled,
1791 GtkWindow *parent_window,
1792 TnyAccount *account,
1795 ModestMailOperation *mail_op = NULL;
1796 MsgReaderInfo *info;
1798 info = (MsgReaderInfo *) user_data;
1799 if (canceled || err) {
1803 /* Register the header - it'll be unregistered in the callback */
1804 modest_window_mgr_register_header (modest_runtime_get_window_mgr (), info->header, NULL);
1806 /* New mail operation */
1807 mail_op = modest_mail_operation_new_with_error_handling (G_OBJECT(parent_window),
1808 modest_ui_actions_disk_operations_error_handler,
1811 modest_mail_operation_queue_add (modest_runtime_get_mail_operation_queue (), mail_op);
1812 modest_mail_operation_get_msg (mail_op, info->header, TRUE, view_msg_cb, info->row_reference);
1813 g_object_unref (mail_op);
1815 /* Update dimming rules */
1816 modest_ui_actions_check_toolbar_dimming_rules (MODEST_WINDOW (parent_window));
1817 modest_ui_actions_check_menu_dimming_rules (MODEST_WINDOW (parent_window));
1820 /* Frees. The row_reference will be freed by the view_msg_cb callback */
1821 g_object_unref (info->header);
1822 g_slice_free (MsgReaderInfo, info);
1827 * Reads the message whose summary item is @header. It takes care of
1828 * several things, among others:
1830 * If the message was not previously downloaded then ask the user
1831 * before downloading. If there is no connection launch the connection
1832 * dialog. Update toolbar dimming rules.
1834 * Returns: TRUE if the mail operation was started, otherwise if the
1835 * user do not want to download the message, or if the user do not
1836 * want to connect, then the operation is not issued
1839 message_reader (ModestMsgViewWindow *window,
1840 ModestMsgViewWindowPrivate *priv,
1842 GtkTreeRowReference *row_reference)
1844 ModestWindowMgr *mgr;
1845 TnyAccount *account;
1847 MsgReaderInfo *info;
1849 g_return_val_if_fail (row_reference != NULL, FALSE);
1851 mgr = modest_runtime_get_window_mgr ();
1852 /* Msg download completed */
1853 if (!(tny_header_get_flags (header) & TNY_HEADER_FLAG_CACHED)) {
1854 /* Ask the user if he wants to download the message if
1856 if (!tny_device_is_online (modest_runtime_get_device())) {
1857 GtkResponseType response;
1859 response = modest_platform_run_confirmation_dialog (GTK_WINDOW (window),
1860 _("mcen_nc_get_msg"));
1861 if (response == GTK_RESPONSE_CANCEL)
1864 folder = tny_header_get_folder (header);
1865 info = g_slice_new (MsgReaderInfo);
1866 info->header = g_object_ref (header);
1867 info->row_reference = gtk_tree_row_reference_copy (row_reference);
1869 /* Offer the connection dialog if necessary */
1870 modest_platform_connect_if_remote_and_perform ((GtkWindow *) window,
1872 TNY_FOLDER_STORE (folder),
1873 message_reader_performer,
1875 g_object_unref (folder);
1880 folder = tny_header_get_folder (header);
1881 account = tny_folder_get_account (folder);
1882 info = g_slice_new (MsgReaderInfo);
1883 info->header = g_object_ref (header);
1884 info->row_reference = gtk_tree_row_reference_copy (row_reference);
1886 message_reader_performer (FALSE, NULL, (GtkWindow *) window, account, info);
1887 g_object_unref (account);
1888 g_object_unref (folder);
1894 modest_msg_view_window_select_next_message (ModestMsgViewWindow *window)
1896 ModestMsgViewWindowPrivate *priv;
1897 GtkTreePath *path= NULL;
1898 GtkTreeIter tmp_iter;
1900 gboolean retval = TRUE;
1901 GtkTreeRowReference *row_reference = NULL;
1903 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), FALSE);
1904 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1906 if (!priv->row_reference)
1909 /* Update the next row reference if it's not valid. This could
1910 happen if for example the header which it was pointing to,
1911 was deleted. The best place to do it is in the row-deleted
1912 handler but the tinymail model do not work like the glib
1913 tree models and reports the deletion when the row is still
1915 if (!gtk_tree_row_reference_valid (priv->next_row_reference)) {
1916 if (gtk_tree_row_reference_valid (priv->row_reference)) {
1917 priv->next_row_reference = gtk_tree_row_reference_copy (priv->row_reference);
1918 select_next_valid_row (priv->header_model, &(priv->next_row_reference), FALSE, priv->is_outbox);
1921 if (priv->next_row_reference)
1922 path = gtk_tree_row_reference_get_path (priv->next_row_reference);
1926 row_reference = gtk_tree_row_reference_copy (priv->next_row_reference);
1928 gtk_tree_model_get_iter (priv->header_model,
1931 gtk_tree_path_free (path);
1933 gtk_tree_model_get (priv->header_model, &tmp_iter,
1934 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
1937 /* Read the message & show it */
1938 if (!message_reader (window, priv, header, row_reference)) {
1941 gtk_tree_row_reference_free (row_reference);
1944 g_object_unref (header);
1950 modest_msg_view_window_select_previous_message (ModestMsgViewWindow *window)
1952 ModestMsgViewWindowPrivate *priv = NULL;
1954 gboolean finished = FALSE;
1955 gboolean retval = FALSE;
1957 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), FALSE);
1958 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1960 /* Return inmediatly if there is no header model */
1961 if (!priv->header_model || !priv->row_reference)
1964 path = gtk_tree_row_reference_get_path (priv->row_reference);
1965 while (!finished && gtk_tree_path_prev (path)) {
1969 gtk_tree_model_get_iter (priv->header_model, &iter, path);
1970 gtk_tree_model_get (priv->header_model, &iter,
1971 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
1975 if (msg_is_visible (header, priv->is_outbox)) {
1976 GtkTreeRowReference *row_reference;
1977 row_reference = gtk_tree_row_reference_new (priv->header_model, path);
1978 /* Read the message & show it */
1979 retval = message_reader (window, priv, header, row_reference);
1980 gtk_tree_row_reference_free (row_reference);
1984 g_object_unref (header);
1988 gtk_tree_path_free (path);
1993 view_msg_cb (ModestMailOperation *mail_op,
2000 ModestMsgViewWindow *self = NULL;
2001 ModestMsgViewWindowPrivate *priv = NULL;
2002 GtkTreeRowReference *row_reference = NULL;
2004 /* Unregister the header (it was registered before creating the mail operation) */
2005 modest_window_mgr_unregister_header (modest_runtime_get_window_mgr (), header);
2007 row_reference = (GtkTreeRowReference *) user_data;
2009 gtk_tree_row_reference_free (row_reference);
2013 /* If there was any error */
2014 if (!modest_ui_actions_msg_retrieval_check (mail_op, header, msg)) {
2015 gtk_tree_row_reference_free (row_reference);
2019 /* Get the window */
2020 self = (ModestMsgViewWindow *) modest_mail_operation_get_source (mail_op);
2021 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self));
2022 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
2024 /* Update the row reference */
2025 if (priv->row_reference != NULL) {
2026 gtk_tree_row_reference_free (priv->row_reference);
2027 priv->row_reference = gtk_tree_row_reference_copy (row_reference);
2028 if (priv->next_row_reference != NULL) {
2029 gtk_tree_row_reference_free (priv->next_row_reference);
2031 priv->next_row_reference = gtk_tree_row_reference_copy (priv->row_reference);
2032 select_next_valid_row (priv->header_model, &(priv->next_row_reference), TRUE, priv->is_outbox);
2035 /* Mark header as read */
2036 if (!(tny_header_get_flags (header) & TNY_HEADER_FLAG_SEEN))
2037 tny_header_set_flag (header, TNY_HEADER_FLAG_SEEN);
2039 /* Set new message */
2040 if (priv->msg_view != NULL && TNY_IS_MSG_VIEW (priv->msg_view)) {
2041 tny_msg_view_set_msg (TNY_MSG_VIEW (priv->msg_view), msg);
2042 modest_msg_view_window_update_priority (self);
2043 update_window_title (MODEST_MSG_VIEW_WINDOW (self));
2044 modest_msg_view_grab_focus (MODEST_MSG_VIEW (priv->msg_view));
2047 /* Set the new message uid of the window */
2048 if (priv->msg_uid) {
2049 g_free (priv->msg_uid);
2050 priv->msg_uid = modest_tny_folder_get_header_unique_id (header);
2053 /* Notify the observers */
2054 g_signal_emit (G_OBJECT (self), signals[MSG_CHANGED_SIGNAL],
2055 0, priv->header_model, priv->row_reference);
2058 g_object_unref (self);
2059 gtk_tree_row_reference_free (row_reference);
2063 modest_msg_view_window_get_folder_type (ModestMsgViewWindow *window)
2065 ModestMsgViewWindowPrivate *priv;
2067 TnyFolderType folder_type;
2069 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
2071 folder_type = TNY_FOLDER_TYPE_UNKNOWN;
2073 msg = tny_msg_view_get_msg (TNY_MSG_VIEW (priv->msg_view));
2077 folder = tny_msg_get_folder (msg);
2079 folder_type = modest_tny_folder_guess_folder_type (folder);
2080 g_object_unref (folder);
2082 g_object_unref (msg);
2090 modest_msg_view_window_update_priority (ModestMsgViewWindow *window)
2092 ModestMsgViewWindowPrivate *priv;
2093 TnyHeader *header = NULL;
2094 TnyHeaderFlags flags = 0;
2096 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
2098 if (priv->header_model && priv->row_reference) {
2100 GtkTreePath *path = NULL;
2102 path = gtk_tree_row_reference_get_path (priv->row_reference);
2103 g_return_if_fail (path != NULL);
2104 gtk_tree_model_get_iter (priv->header_model,
2106 gtk_tree_row_reference_get_path (priv->row_reference));
2108 gtk_tree_model_get (priv->header_model, &iter, TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
2110 gtk_tree_path_free (path);
2113 msg = tny_msg_view_get_msg (TNY_MSG_VIEW (priv->msg_view));
2115 header = tny_msg_get_header (msg);
2116 g_object_unref (msg);
2121 flags = tny_header_get_flags (header);
2122 g_object_unref(G_OBJECT(header));
2125 modest_msg_view_set_priority (MODEST_MSG_VIEW(priv->msg_view), flags);
2130 toolbar_resize (ModestMsgViewWindow *self)
2132 ModestMsgViewWindowPrivate *priv = NULL;
2133 ModestWindowPrivate *parent_priv = NULL;
2135 gint static_button_size;
2136 ModestWindowMgr *mgr;
2138 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self));
2139 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
2140 parent_priv = MODEST_WINDOW_GET_PRIVATE(self);
2142 mgr = modest_runtime_get_window_mgr ();
2143 static_button_size = modest_window_mgr_get_fullscreen_mode (mgr)?118:108;
2145 if (parent_priv->toolbar) {
2146 /* left size buttons */
2147 widget = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/ToolbarMessageReply");
2148 gtk_tool_item_set_expand (GTK_TOOL_ITEM (widget), FALSE);
2149 gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (widget), FALSE);
2150 gtk_widget_set_size_request (GTK_WIDGET (widget), static_button_size, -1);
2151 widget = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/ToolbarMessageMoveTo");
2152 gtk_tool_item_set_expand (GTK_TOOL_ITEM (widget), FALSE);
2153 gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (widget), FALSE);
2154 gtk_widget_set_size_request (GTK_WIDGET (widget), static_button_size, -1);
2155 widget = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/ToolbarDeleteMessage");
2156 gtk_tool_item_set_expand (GTK_TOOL_ITEM (widget), FALSE);
2157 gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (widget), FALSE);
2158 gtk_widget_set_size_request (GTK_WIDGET (widget), static_button_size, -1);
2159 widget = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/FindInMessage");
2160 gtk_tool_item_set_expand (GTK_TOOL_ITEM (widget), FALSE);
2161 gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (widget), FALSE);
2162 gtk_widget_set_size_request (GTK_WIDGET (widget), static_button_size, -1);
2164 gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (priv->next_toolitem), TRUE);
2165 gtk_tool_item_set_expand (GTK_TOOL_ITEM (priv->next_toolitem), TRUE);
2166 gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (priv->prev_toolitem), TRUE);
2167 gtk_tool_item_set_expand (GTK_TOOL_ITEM (priv->prev_toolitem), TRUE);
2173 modest_msg_view_window_show_toolbar (ModestWindow *self,
2174 gboolean show_toolbar)
2176 ModestMsgViewWindowPrivate *priv = NULL;
2177 ModestWindowPrivate *parent_priv;
2178 GtkWidget *reply_button = NULL, *menu = NULL;
2180 parent_priv = MODEST_WINDOW_GET_PRIVATE(self);
2181 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(self);
2183 /* Set optimized view status */
2184 priv->optimized_view = !show_toolbar;
2186 if (!parent_priv->toolbar) {
2187 parent_priv->toolbar = gtk_ui_manager_get_widget (parent_priv->ui_manager,
2189 gtk_widget_set_no_show_all (parent_priv->toolbar, TRUE);
2191 priv->next_toolitem = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/ToolbarMessageNext");
2192 priv->prev_toolitem = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/ToolbarMessageBack");
2193 toolbar_resize (MODEST_MSG_VIEW_WINDOW (self));
2196 hildon_window_add_toolbar (HILDON_WINDOW (self),
2197 GTK_TOOLBAR (parent_priv->toolbar));
2199 /* Set reply button tap and hold menu */
2200 reply_button = gtk_ui_manager_get_widget (parent_priv->ui_manager,
2201 "/ToolBar/ToolbarMessageReply");
2202 menu = gtk_ui_manager_get_widget (parent_priv->ui_manager,
2203 "/ToolbarReplyCSM");
2204 if (menu && reply_button)
2205 gtk_widget_tap_and_hold_setup (GTK_WIDGET (reply_button), menu, NULL, 0);
2209 /* Quick hack: this prevents toolbar icons "dance" when progress bar show status is changed */
2210 /* TODO: resize mode migth be GTK_RESIZE_QUEUE, in order to avoid unneccesary shows */
2211 gtk_container_set_resize_mode (GTK_CONTAINER(parent_priv->toolbar), GTK_RESIZE_IMMEDIATE);
2213 gtk_widget_show (GTK_WIDGET (parent_priv->toolbar));
2214 if (modest_msg_view_window_transfer_mode_enabled (MODEST_MSG_VIEW_WINDOW (self)))
2215 set_progress_hint (MODEST_MSG_VIEW_WINDOW (self), TRUE);
2217 set_progress_hint (MODEST_MSG_VIEW_WINDOW (self), FALSE);
2220 gtk_widget_set_no_show_all (parent_priv->toolbar, TRUE);
2221 gtk_widget_hide (GTK_WIDGET (parent_priv->toolbar));
2226 modest_msg_view_window_clipboard_owner_change (GtkClipboard *clipboard,
2228 ModestMsgViewWindow *window)
2230 if (!GTK_WIDGET_VISIBLE (window))
2233 modest_window_check_dimming_rules_group (MODEST_WINDOW (window), MODEST_DIMMING_RULES_CLIPBOARD);
2237 modest_msg_view_window_transfer_mode_enabled (ModestMsgViewWindow *self)
2239 ModestMsgViewWindowPrivate *priv;
2241 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self), FALSE);
2242 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(self);
2244 return priv->progress_hint;
2248 observers_empty (ModestMsgViewWindow *self)
2251 ModestMsgViewWindowPrivate *priv;
2252 gboolean is_empty = TRUE;
2253 guint pending_ops = 0;
2255 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(self);
2256 tmp = priv->progress_widgets;
2258 /* Check all observers */
2259 while (tmp && is_empty) {
2260 pending_ops = modest_progress_object_num_pending_operations (MODEST_PROGRESS_OBJECT(tmp->data));
2261 is_empty = pending_ops == 0;
2263 tmp = g_slist_next(tmp);
2270 on_account_removed (TnyAccountStore *account_store,
2271 TnyAccount *account,
2274 /* Do nothing if it's a transport account, because we only
2275 show the messages of a store account */
2276 if (tny_account_get_account_type(account) == TNY_ACCOUNT_TYPE_STORE) {
2277 const gchar *parent_acc = NULL;
2278 const gchar *our_acc = NULL;
2280 our_acc = modest_window_get_active_account (MODEST_WINDOW (user_data));
2281 parent_acc = modest_tny_account_get_parent_modest_account_name_for_server_account (account);
2283 /* Close this window if I'm showing a message of the removed account */
2284 if (strcmp (parent_acc, our_acc) == 0)
2285 modest_ui_actions_on_close_window (NULL, MODEST_WINDOW (user_data));
2290 on_mail_operation_started (ModestMailOperation *mail_op,
2293 ModestMsgViewWindow *self;
2294 ModestMailOperationTypeOperation op_type;
2296 ModestMsgViewWindowPrivate *priv;
2297 GObject *source = NULL;
2299 self = MODEST_MSG_VIEW_WINDOW (user_data);
2300 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
2301 op_type = modest_mail_operation_get_type_operation (mail_op);
2302 tmp = priv->progress_widgets;
2303 source = modest_mail_operation_get_source(mail_op);
2304 if (G_OBJECT (self) == source) {
2305 if (op_type == MODEST_MAIL_OPERATION_TYPE_RECEIVE ) {
2306 set_toolbar_transfer_mode(self);
2308 modest_progress_object_add_operation (
2309 MODEST_PROGRESS_OBJECT (tmp->data),
2311 tmp = g_slist_next (tmp);
2315 g_object_unref (source);
2319 on_mail_operation_finished (ModestMailOperation *mail_op,
2322 ModestMsgViewWindow *self;
2323 ModestMailOperationTypeOperation op_type;
2325 ModestMsgViewWindowPrivate *priv;
2327 self = MODEST_MSG_VIEW_WINDOW (user_data);
2328 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
2329 op_type = modest_mail_operation_get_type_operation (mail_op);
2330 tmp = priv->progress_widgets;
2332 if (op_type == MODEST_MAIL_OPERATION_TYPE_RECEIVE ) {
2334 modest_progress_object_remove_operation (MODEST_PROGRESS_OBJECT (tmp->data),
2336 tmp = g_slist_next (tmp);
2339 /* If no more operations are being observed, NORMAL mode is enabled again */
2340 if (observers_empty (self)) {
2341 set_progress_hint (self, FALSE);
2344 /* Update dimming rules. We have to do this right here
2345 and not in view_msg_cb because at that point the
2346 transfer mode is still enabled so the dimming rule
2347 won't let the user delete the message that has been
2348 readed for example */
2349 modest_ui_actions_check_toolbar_dimming_rules (MODEST_WINDOW (self));
2350 modest_ui_actions_check_menu_dimming_rules (MODEST_WINDOW (self));
2355 on_queue_changed (ModestMailOperationQueue *queue,
2356 ModestMailOperation *mail_op,
2357 ModestMailOperationQueueNotification type,
2358 ModestMsgViewWindow *self)
2360 ModestMsgViewWindowPrivate *priv;
2362 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
2364 /* If this operations was created by another window, do nothing */
2365 if (!modest_mail_operation_is_mine (mail_op, G_OBJECT(self)))
2368 if (type == MODEST_MAIL_OPERATION_QUEUE_OPERATION_ADDED) {
2369 priv->sighandlers = modest_signal_mgr_connect (priv->sighandlers,
2371 "operation-started",
2372 G_CALLBACK (on_mail_operation_started),
2374 priv->sighandlers = modest_signal_mgr_connect (priv->sighandlers,
2376 "operation-finished",
2377 G_CALLBACK (on_mail_operation_finished),
2379 } else if (type == MODEST_MAIL_OPERATION_QUEUE_OPERATION_REMOVED) {
2380 priv->sighandlers = modest_signal_mgr_disconnect (priv->sighandlers,
2382 "operation-started");
2383 priv->sighandlers = modest_signal_mgr_disconnect (priv->sighandlers,
2385 "operation-finished");
2390 modest_msg_view_window_get_attachments (ModestMsgViewWindow *win)
2392 ModestMsgViewWindowPrivate *priv;
2393 TnyList *selected_attachments = NULL;
2395 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (win), NULL);
2396 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (win);
2398 selected_attachments = modest_msg_view_get_selected_attachments (MODEST_MSG_VIEW (priv->msg_view));
2400 return selected_attachments;
2406 guint banner_idle_id;
2407 } DecodeAsyncHelper;
2410 decode_async_banner_idle (gpointer user_data)
2412 DecodeAsyncHelper *helper = (DecodeAsyncHelper *) user_data;
2414 helper->banner_idle_id = 0;
2415 helper->banner = hildon_banner_show_animation (NULL, NULL, _("mail_me_opening"));
2421 on_decode_to_stream_async_handler (TnyMimePart *mime_part,
2427 DecodeAsyncHelper *helper = (DecodeAsyncHelper *) user_data;
2429 if (helper->banner_idle_id > 0) {
2430 g_source_remove (helper->banner_idle_id);
2431 helper->banner_idle_id = 0;
2433 if (helper->banner) {
2434 gtk_widget_destroy (helper->banner);
2435 helper->banner = NULL;
2437 if (cancelled || err) {
2438 modest_platform_information_banner (NULL, NULL,
2439 _("mail_ib_file_operation_failed"));
2443 /* make the file read-only */
2444 g_chmod(helper->filepath, 0444);
2446 /* Activate the file */
2447 modest_platform_activate_file (helper->filepath, tny_mime_part_get_content_type (mime_part));
2451 g_free (helper->filepath);
2452 g_slice_free (DecodeAsyncHelper, helper);
2456 modest_msg_view_window_view_attachment (ModestMsgViewWindow *window,
2457 TnyMimePart *mime_part)
2459 ModestMsgViewWindowPrivate *priv;
2460 const gchar *msg_uid;
2461 gchar *attachment_uid = NULL;
2462 gint attachment_index = 0;
2463 TnyList *attachments;
2465 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window));
2466 g_return_if_fail (TNY_IS_MIME_PART (mime_part) || (mime_part == NULL));
2467 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
2469 msg_uid = modest_msg_view_window_get_message_uid (MODEST_MSG_VIEW_WINDOW (window));
2470 attachments = modest_msg_view_get_attachments (MODEST_MSG_VIEW (priv->msg_view));
2471 attachment_index = modest_list_index (attachments, (GObject *) mime_part);
2472 g_object_unref (attachments);
2474 if (msg_uid && attachment_index >= 0) {
2475 attachment_uid = g_strdup_printf ("%s/%d", msg_uid, attachment_index);
2478 if (mime_part == NULL) {
2479 gboolean error = FALSE;
2480 TnyList *selected_attachments = modest_msg_view_get_selected_attachments (MODEST_MSG_VIEW (priv->msg_view));
2481 if (selected_attachments == NULL || tny_list_get_length (selected_attachments) == 0) {
2483 } else if (tny_list_get_length (selected_attachments) > 1) {
2484 hildon_banner_show_information (NULL, NULL, _("mcen_ib_unable_to_display_more"));
2488 iter = tny_list_create_iterator (selected_attachments);
2489 mime_part = (TnyMimePart *) tny_iterator_get_current (iter);
2490 g_object_unref (iter);
2492 g_object_unref (selected_attachments);
2497 g_object_ref (mime_part);
2500 if (tny_mime_part_is_purged (mime_part)) {
2501 g_object_unref (mime_part);
2505 if (!modest_tny_mime_part_is_msg (mime_part)) {
2506 gchar *filepath = NULL;
2507 const gchar *att_filename = tny_mime_part_get_filename (mime_part);
2508 gboolean show_error_banner = FALSE;
2509 TnyFsStream *temp_stream = NULL;
2510 temp_stream = modest_utils_create_temp_stream (att_filename, attachment_uid,
2513 if (temp_stream != NULL) {
2514 DecodeAsyncHelper *helper = g_slice_new (DecodeAsyncHelper);
2515 helper->filepath = g_strdup (filepath);
2516 helper->banner = NULL;
2517 helper->banner_idle_id = g_timeout_add (1000, decode_async_banner_idle, helper);
2518 tny_mime_part_decode_to_stream_async (mime_part, TNY_STREAM (temp_stream),
2519 on_decode_to_stream_async_handler,
2522 g_object_unref (temp_stream);
2523 /* NOTE: files in the temporary area will be automatically
2524 * cleaned after some time if they are no longer in use */
2527 const gchar *content_type;
2528 /* the file may already exist but it isn't writable,
2529 * let's try to open it anyway */
2530 content_type = tny_mime_part_get_content_type (mime_part);
2531 modest_platform_activate_file (filepath, content_type);
2533 g_warning ("%s: modest_utils_create_temp_stream failed", __FUNCTION__);
2534 show_error_banner = TRUE;
2539 if (show_error_banner)
2540 modest_platform_information_banner (NULL, NULL, _("mail_ib_file_operation_failed"));
2542 /* message attachment */
2543 TnyHeader *header = NULL;
2544 ModestWindowMgr *mgr;
2545 ModestWindow *msg_win = NULL;
2548 header = tny_msg_get_header (TNY_MSG (mime_part));
2549 mgr = modest_runtime_get_window_mgr ();
2550 found = modest_window_mgr_find_registered_header (mgr, header, &msg_win);
2553 /* if it's found, but there is no msg_win, it's probably in the process of being created;
2554 * thus, we don't do anything */
2555 g_warning ("window for is already being created");
2557 /* it's not found, so create a new window for it */
2558 modest_window_mgr_register_header (mgr, header, attachment_uid); /* register the uid before building the window */
2559 gchar *account = g_strdup (modest_window_get_active_account (MODEST_WINDOW (window)));
2561 account = modest_account_mgr_get_default_account (modest_runtime_get_account_mgr ());
2562 msg_win = modest_msg_view_window_new_for_attachment (TNY_MSG (mime_part), account, attachment_uid);
2563 modest_window_set_zoom (MODEST_WINDOW (msg_win),
2564 modest_window_get_zoom (MODEST_WINDOW (window)));
2565 modest_window_mgr_register_window (mgr, msg_win, MODEST_WINDOW (window));
2566 gtk_widget_show_all (GTK_WIDGET (msg_win));
2569 g_object_unref (mime_part);
2582 GnomeVFSResult result;
2585 static void save_mime_part_info_free (SaveMimePartInfo *info, gboolean with_struct);
2586 static gboolean idle_save_mime_part_show_result (SaveMimePartInfo *info);
2587 static gpointer save_mime_part_to_file (SaveMimePartInfo *info);
2588 static void save_mime_parts_to_file_with_checks (SaveMimePartInfo *info);
2591 save_mime_part_info_free (SaveMimePartInfo *info, gboolean with_struct)
2595 for (node = info->pairs; node != NULL; node = g_list_next (node)) {
2596 SaveMimePartPair *pair = (SaveMimePartPair *) node->data;
2597 g_free (pair->filename);
2598 g_object_unref (pair->part);
2599 g_slice_free (SaveMimePartPair, pair);
2601 g_list_free (info->pairs);
2604 gtk_widget_destroy (info->banner);
2605 g_slice_free (SaveMimePartInfo, info);
2610 idle_save_mime_part_show_result (SaveMimePartInfo *info)
2612 if (info->pairs != NULL) {
2613 save_mime_part_to_file (info);
2615 /* This is a GDK lock because we are an idle callback and
2616 * hildon_banner_show_information is or does Gtk+ code */
2618 gdk_threads_enter (); /* CHECKED */
2619 save_mime_part_info_free (info, TRUE);
2620 if (info->result == GNOME_VFS_OK) {
2621 hildon_banner_show_information (NULL, NULL, _CS("sfil_ib_saved"));
2622 } else if (info->result == GNOME_VFS_ERROR_NO_SPACE) {
2623 hildon_banner_show_information (NULL, NULL, dgettext("ke-recv",
2624 "cerm_device_memory_full"));
2626 hildon_banner_show_information (NULL, NULL, _("mail_ib_file_operation_failed"));
2628 gdk_threads_leave (); /* CHECKED */
2635 save_mime_part_to_file (SaveMimePartInfo *info)
2637 GnomeVFSHandle *handle;
2639 SaveMimePartPair *pair = (SaveMimePartPair *) info->pairs->data;
2641 info->result = gnome_vfs_create (&handle, pair->filename, GNOME_VFS_OPEN_WRITE, FALSE, 0644);
2642 if (info->result == GNOME_VFS_OK) {
2643 GError *error = NULL;
2644 stream = tny_vfs_stream_new (handle);
2645 if (tny_mime_part_decode_to_stream (pair->part, stream, &error) < 0) {
2646 g_warning ("modest: could not save attachment %s: %d (%s)\n", pair->filename, error?error->code:-1, error?error->message:"Unknown error");
2648 info->result = GNOME_VFS_ERROR_IO;
2650 g_object_unref (G_OBJECT (stream));
2651 g_object_unref (pair->part);
2652 g_slice_free (SaveMimePartPair, pair);
2653 info->pairs = g_list_delete_link (info->pairs, info->pairs);
2655 g_warning ("modest: could not create save attachment %s: %s\n", pair->filename, gnome_vfs_result_to_string (info->result));
2656 save_mime_part_info_free (info, FALSE);
2659 g_idle_add ((GSourceFunc) idle_save_mime_part_show_result, info);
2664 save_mime_parts_to_file_with_checks (SaveMimePartInfo *info)
2666 gboolean is_ok = TRUE;
2667 gint replaced_files = 0;
2668 const GList *files = info->pairs;
2671 for (iter = files; (iter != NULL) && (replaced_files < 2); iter = g_list_next(iter)) {
2672 SaveMimePartPair *pair = iter->data;
2673 if (modest_utils_file_exists (pair->filename)) {
2677 if (replaced_files) {
2678 GtkWidget *confirm_overwrite_dialog;
2679 const gchar *message = (replaced_files == 1) ?
2680 _FM("docm_nc_replace_file") : _FM("docm_nc_replace_multiple");
2681 confirm_overwrite_dialog = hildon_note_new_confirmation (NULL, message);
2682 if (gtk_dialog_run (GTK_DIALOG (confirm_overwrite_dialog)) != GTK_RESPONSE_OK) {
2685 gtk_widget_destroy (confirm_overwrite_dialog);
2689 save_mime_part_info_free (info, TRUE);
2691 GtkWidget *banner = hildon_banner_show_animation (NULL, NULL,
2692 _CS("sfil_ib_saving"));
2693 info->banner = banner;
2694 g_thread_create ((GThreadFunc)save_mime_part_to_file, info, FALSE, NULL);
2700 save_attachments_response (GtkDialog *dialog,
2704 TnyList *mime_parts;
2706 GList *files_to_save = NULL;
2708 mime_parts = TNY_LIST (user_data);
2710 if (arg1 != GTK_RESPONSE_OK)
2713 chooser_uri = gtk_file_chooser_get_uri (GTK_FILE_CHOOSER (dialog));
2715 if (!modest_utils_folder_writable (chooser_uri)) {
2716 hildon_banner_show_information
2717 (NULL, NULL, dgettext("hildon-fm", "sfil_ib_readonly_location"));
2721 iter = tny_list_create_iterator (mime_parts);
2722 while (!tny_iterator_is_done (iter)) {
2723 TnyMimePart *mime_part = (TnyMimePart *) tny_iterator_get_current (iter);
2725 if ((modest_tny_mime_part_is_attachment_for_modest (mime_part)) &&
2726 !tny_mime_part_is_purged (mime_part) &&
2727 (tny_mime_part_get_filename (mime_part) != NULL)) {
2728 SaveMimePartPair *pair;
2730 pair = g_slice_new0 (SaveMimePartPair);
2732 if (tny_list_get_length (mime_parts) > 1) {
2734 gnome_vfs_escape_slashes (tny_mime_part_get_filename (mime_part));
2735 pair->filename = g_build_filename (chooser_uri, escaped, NULL);
2738 pair->filename = g_strdup (chooser_uri);
2740 pair->part = mime_part;
2741 files_to_save = g_list_prepend (files_to_save, pair);
2743 tny_iterator_next (iter);
2745 g_object_unref (iter);
2747 g_free (chooser_uri);
2749 if (files_to_save != NULL) {
2750 SaveMimePartInfo *info = g_slice_new0 (SaveMimePartInfo);
2751 info->pairs = files_to_save;
2752 info->result = TRUE;
2753 save_mime_parts_to_file_with_checks (info);
2757 /* Free and close the dialog */
2758 g_object_unref (mime_parts);
2759 gtk_widget_destroy (GTK_WIDGET (dialog));
2763 modest_msg_view_window_save_attachments (ModestMsgViewWindow *window, TnyList *mime_parts)
2765 ModestMsgViewWindowPrivate *priv;
2766 GtkWidget *save_dialog = NULL;
2767 gchar *folder = NULL;
2768 gchar *filename = NULL;
2769 gchar *save_multiple_str = NULL;
2771 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window));
2772 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
2774 if (mime_parts == NULL) {
2775 mime_parts = modest_msg_view_get_selected_attachments (MODEST_MSG_VIEW (priv->msg_view));
2776 if (mime_parts == NULL || tny_list_get_length (mime_parts) == 0)
2779 g_object_ref (mime_parts);
2782 /* prepare dialog */
2783 if (tny_list_get_length (mime_parts) == 1) {
2785 /* only one attachment selected */
2786 iter = tny_list_create_iterator (mime_parts);
2787 TnyMimePart *mime_part = (TnyMimePart *) tny_iterator_get_current (iter);
2788 g_object_unref (iter);
2789 if (!modest_tny_mime_part_is_msg (mime_part) &&
2790 modest_tny_mime_part_is_attachment_for_modest (mime_part) &&
2791 !tny_mime_part_is_purged (mime_part)) {
2792 filename = g_strdup (tny_mime_part_get_filename (mime_part));
2794 /* TODO: show any error? */
2795 g_warning ("Tried to save a non-file attachment");
2796 g_object_unref (mime_parts);
2799 g_object_unref (mime_part);
2801 save_multiple_str = g_strdup_printf (_FM("sfil_va_number_of_objects_attachments"),
2802 tny_list_get_length (mime_parts));
2805 save_dialog = hildon_file_chooser_dialog_new (GTK_WINDOW (window),
2806 GTK_FILE_CHOOSER_ACTION_SAVE);
2809 folder = g_build_filename (g_get_home_dir (), g_getenv (MYDOCS_ENV), DOCS_FOLDER, NULL);
2810 gtk_file_chooser_set_current_folder (GTK_FILE_CHOOSER (save_dialog), folder);
2815 gtk_file_chooser_set_current_name (GTK_FILE_CHOOSER (save_dialog),
2820 /* if multiple, set multiple string */
2821 if (save_multiple_str) {
2822 g_object_set (G_OBJECT (save_dialog), "save-multiple", save_multiple_str, NULL);
2823 gtk_window_set_title (GTK_WINDOW (save_dialog), _FM("sfil_ti_save_objects_files"));
2826 /* We must run this asynchronously, because the hildon dialog
2827 performs a gtk_dialog_run by itself which leads to gdk
2829 g_signal_connect (save_dialog, "response",
2830 G_CALLBACK (save_attachments_response), mime_parts);
2832 gtk_widget_show_all (save_dialog);
2836 show_remove_attachment_information (gpointer userdata)
2838 ModestMsgViewWindow *window = (ModestMsgViewWindow *) userdata;
2839 ModestMsgViewWindowPrivate *priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
2841 /* We're outside the main lock */
2842 gdk_threads_enter ();
2844 if (priv->remove_attachment_banner != NULL) {
2845 gtk_widget_destroy (priv->remove_attachment_banner);
2846 g_object_unref (priv->remove_attachment_banner);
2849 priv->remove_attachment_banner = g_object_ref (
2850 hildon_banner_show_animation (NULL, NULL, _("mcen_me_inbox_remove_attachments")));
2852 gdk_threads_leave ();
2858 modest_msg_view_window_remove_attachments (ModestMsgViewWindow *window, gboolean get_all)
2860 ModestMsgViewWindowPrivate *priv;
2861 TnyList *mime_parts = NULL;
2862 gchar *confirmation_message;
2868 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window));
2869 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
2872 mime_parts = modest_msg_view_get_attachments (MODEST_MSG_VIEW (priv->msg_view));
2874 mime_parts = modest_msg_view_get_selected_attachments (MODEST_MSG_VIEW (priv->msg_view));
2876 /* Remove already purged messages from mime parts list */
2877 iter = tny_list_create_iterator (mime_parts);
2878 while (!tny_iterator_is_done (iter)) {
2879 TnyMimePart *part = TNY_MIME_PART (tny_iterator_get_current (iter));
2880 tny_iterator_next (iter);
2881 if (tny_mime_part_is_purged (part)) {
2882 tny_list_remove (mime_parts, (GObject *) part);
2884 g_object_unref (part);
2886 g_object_unref (iter);
2888 if (tny_list_get_length (mime_parts) == 0) {
2889 g_object_unref (mime_parts);
2893 n_attachments = tny_list_get_length (mime_parts);
2894 if (n_attachments == 1) {
2898 iter = tny_list_create_iterator (mime_parts);
2899 part = (TnyMimePart *) tny_iterator_get_current (iter);
2900 g_object_unref (iter);
2901 if (modest_tny_mime_part_is_msg (part)) {
2903 header = tny_msg_get_header (TNY_MSG (part));
2904 filename = tny_header_dup_subject (header);
2905 g_object_unref (header);
2906 if (filename == NULL)
2907 filename = g_strdup (_("mail_va_no_subject"));
2909 filename = g_strdup (tny_mime_part_get_filename (TNY_MIME_PART (part)));
2911 confirmation_message = g_strdup_printf (_("mcen_nc_purge_file_text"), filename);
2913 g_object_unref (part);
2915 confirmation_message = g_strdup_printf (ngettext("mcen_nc_purge_file_text",
2916 "mcen_nc_purge_files_text",
2917 n_attachments), n_attachments);
2919 response = modest_platform_run_confirmation_dialog (GTK_WINDOW (window),
2920 confirmation_message);
2921 g_free (confirmation_message);
2923 if (response != GTK_RESPONSE_OK) {
2924 g_object_unref (mime_parts);
2928 priv->purge_timeout = g_timeout_add (2000, show_remove_attachment_information, window);
2930 iter = tny_list_create_iterator (mime_parts);
2931 while (!tny_iterator_is_done (iter)) {
2934 part = (TnyMimePart *) tny_iterator_get_current (iter);
2935 tny_mime_part_set_purged (TNY_MIME_PART (part));
2936 g_object_unref (part);
2937 tny_iterator_next (iter);
2939 g_object_unref (iter);
2941 msg = tny_msg_view_get_msg (TNY_MSG_VIEW (priv->msg_view));
2942 tny_msg_view_clear (TNY_MSG_VIEW (priv->msg_view));
2943 tny_msg_rewrite_cache (msg);
2944 tny_msg_view_set_msg (TNY_MSG_VIEW (priv->msg_view), msg);
2945 g_object_unref (msg);
2947 g_object_unref (mime_parts);
2949 if (priv->purge_timeout > 0) {
2950 g_source_remove (priv->purge_timeout);
2951 priv->purge_timeout = 0;
2954 if (priv->remove_attachment_banner) {
2955 gtk_widget_destroy (priv->remove_attachment_banner);
2956 g_object_unref (priv->remove_attachment_banner);
2957 priv->remove_attachment_banner = NULL;
2965 update_window_title (ModestMsgViewWindow *window)
2967 ModestMsgViewWindowPrivate *priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
2969 TnyHeader *header = NULL;
2970 gchar *subject = NULL;
2972 msg = tny_msg_view_get_msg (TNY_MSG_VIEW (priv->msg_view));
2975 header = tny_msg_get_header (msg);
2976 subject = tny_header_dup_subject (header);
2977 g_object_unref (header);
2978 g_object_unref (msg);
2981 if ((subject == NULL)||(subject[0] == '\0')) {
2983 subject = g_strdup (_("mail_va_no_subject"));
2986 gtk_window_set_title (GTK_WINDOW (window), subject);
2990 static void on_move_focus (GtkWidget *widget,
2991 GtkDirectionType direction,
2994 g_signal_stop_emission_by_name (G_OBJECT (widget), "move-focus");
2998 fetch_image_open_stream (TnyStreamCache *self, gint64 *expected_size, gchar *uri)
3000 GnomeVFSResult result;
3001 GnomeVFSHandle *handle = NULL;
3002 GnomeVFSFileInfo *info = NULL;
3005 result = gnome_vfs_open (&handle, uri, GNOME_VFS_OPEN_READ);
3006 if (result != GNOME_VFS_OK) {
3011 info = gnome_vfs_file_info_new ();
3012 result = gnome_vfs_get_file_info_from_handle (handle, info, GNOME_VFS_FILE_INFO_DEFAULT);
3013 if (result != GNOME_VFS_OK || ! (info->valid_fields & GNOME_VFS_FILE_INFO_FIELDS_SIZE)) {
3014 /* We put a "safe" default size for going to cache */
3015 *expected_size = (300*1024);
3017 *expected_size = info->size;
3019 gnome_vfs_file_info_unref (info);
3021 stream = tny_vfs_stream_new (handle);
3030 TnyStream *output_stream;
3031 GtkWidget *msg_view;
3035 on_fetch_image_idle_refresh_view (gpointer userdata)
3038 FetchImageData *fidata = (FetchImageData *) userdata;
3039 g_message ("REFRESH VIEW");
3040 if (GTK_WIDGET_DRAWABLE (fidata->msg_view)) {
3041 g_message ("QUEUING DRAW");
3042 gtk_widget_queue_draw (fidata->msg_view);
3044 g_object_unref (fidata->msg_view);
3045 g_slice_free (FetchImageData, fidata);
3050 on_fetch_image_thread (gpointer userdata)
3052 FetchImageData *fidata = (FetchImageData *) userdata;
3053 TnyStreamCache *cache;
3054 TnyStream *cache_stream;
3056 cache = modest_runtime_get_images_cache ();
3057 cache_stream = tny_stream_cache_get_stream (cache, fidata->cache_id, (TnyStreamCacheOpenStreamFetcher) fetch_image_open_stream, (gpointer) fidata->uri);
3058 g_free (fidata->cache_id);
3059 g_free (fidata->uri);
3061 if (cache_stream != NULL) {
3062 tny_stream_write_to_stream (cache_stream, fidata->output_stream);
3063 tny_stream_close (cache_stream);
3064 g_object_unref (cache_stream);
3067 tny_stream_close (fidata->output_stream);
3068 g_object_unref (fidata->output_stream);
3071 gdk_threads_enter ();
3072 g_idle_add (on_fetch_image_idle_refresh_view, fidata);
3073 gdk_threads_leave ();
3079 on_fetch_image (ModestMsgView *msgview,
3082 ModestMsgViewWindow *window)
3084 const gchar *current_account;
3085 ModestMsgViewWindowPrivate *priv;
3086 FetchImageData *fidata;
3088 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
3090 current_account = modest_window_get_active_account (MODEST_WINDOW (window));
3092 fidata = g_slice_new0 (FetchImageData);
3093 fidata->msg_view = g_object_ref (msgview);
3094 fidata->uri = g_strdup (uri);
3095 fidata->cache_id = modest_images_cache_get_id (current_account, uri);
3096 fidata->output_stream = g_object_ref (stream);
3098 if (g_thread_create (on_fetch_image_thread, fidata, FALSE, NULL) == NULL) {
3099 g_object_unref (fidata->output_stream);
3100 g_free (fidata->cache_id);
3101 g_free (fidata->uri);
3102 g_object_unref (fidata->msg_view);
3103 g_slice_free (FetchImageData, fidata);
3104 tny_stream_close (stream);
3112 add_to_menu (ModestMsgViewWindow *self,
3113 HildonAppMenu *menu,
3116 ModestDimmingRulesGroup *dimming_group,
3117 GCallback dimming_callback)
3121 button = gtk_button_new_with_label (label);
3122 g_signal_connect_after (G_OBJECT (button), "clicked",
3123 callback, (gpointer) self);
3124 modest_dimming_rules_group_add_widget_rule (dimming_group,
3127 MODEST_WINDOW (self));
3128 hildon_app_menu_append (menu, GTK_BUTTON (button));
3132 setup_menu (ModestMsgViewWindow *self, ModestDimmingRulesGroup *group)
3134 ModestMsgViewWindowPrivate *priv = NULL;
3135 GtkWidget *app_menu;
3137 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW(self));
3139 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
3141 app_menu = hildon_app_menu_new ();
3143 /* Settings menu buttons */
3144 add_to_menu (self, HILDON_APP_MENU (app_menu), _("mcen_me_inbox_replytoall"),
3145 G_CALLBACK (modest_ui_actions_on_reply_all),
3146 group, G_CALLBACK (modest_ui_dimming_rules_on_reply_msg));
3147 add_to_menu (self, HILDON_APP_MENU (app_menu), _("mcen_me_inbox_forward"),
3148 G_CALLBACK (modest_ui_actions_on_forward),
3149 group, G_CALLBACK (modest_ui_dimming_rules_on_reply_msg));
3151 add_to_menu (self, HILDON_APP_MENU (app_menu), _("mcen_me_inbox_mark_as_read"),
3152 G_CALLBACK (modest_ui_actions_on_mark_as_read),
3153 group, G_CALLBACK (modest_ui_dimming_rules_on_mark_as_read_msg_in_view));
3154 add_to_menu (self, HILDON_APP_MENU (app_menu), _("mcen_me_inbox_mark_as_unread"),
3155 G_CALLBACK (modest_ui_actions_on_mark_as_unread),
3156 group, G_CALLBACK (modest_ui_dimming_rules_on_mark_as_unread_msg_in_view));
3158 add_to_menu (self, HILDON_APP_MENU (app_menu), _("mcen_me_viewer_save_attachments"),
3159 G_CALLBACK (modest_ui_actions_save_attachments),
3160 group, G_CALLBACK (modest_ui_dimming_rules_on_save_attachments));
3161 add_to_menu (self, HILDON_APP_MENU (app_menu), _("mcen_me_inbox_remove_attachments"),
3162 G_CALLBACK (modest_ui_actions_remove_attachments),
3163 group, G_CALLBACK (modest_ui_dimming_rules_on_remove_attachments));
3165 add_to_menu (self, HILDON_APP_MENU (app_menu), _("mcen_me_viewer_newemail"),
3166 G_CALLBACK (modest_ui_actions_on_new_msg),
3167 group, G_CALLBACK (modest_ui_dimming_rules_on_new_msg));
3168 add_to_menu (self, HILDON_APP_MENU (app_menu), _("mcen_me_viewer_addtocontacts"),
3169 G_CALLBACK (modest_ui_actions_add_to_contacts),
3170 group, G_CALLBACK (modest_ui_dimming_rules_on_add_to_contacts));
3172 hildon_stackable_window_set_main_menu (HILDON_STACKABLE_WINDOW (self),
3173 HILDON_APP_MENU (app_menu));
3177 modest_msg_view_window_toggle_menu (HildonWindow *window,
3181 modest_ui_actions_check_menu_dimming_rules (MODEST_WINDOW (window));
3183 return HILDON_WINDOW_CLASS (parent_class)->toggle_menu (window, button, time);
3188 modest_msg_view_window_add_to_contacts (ModestMsgViewWindow *self)
3190 ModestMsgViewWindowPrivate *priv;
3191 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
3192 GSList *recipients = NULL;
3194 gboolean contacts_to_add = FALSE;
3196 msg = tny_msg_view_get_msg (TNY_MSG_VIEW (priv->msg_view));
3197 if (msg == NULL) return;
3198 recipients = modest_tny_msg_get_all_recipients_list (msg);
3200 if (recipients != NULL) {
3201 GtkWidget *picker_dialog;
3202 GtkWidget *selector;
3204 gchar *selected = NULL;
3206 selector = hildon_touch_selector_new_text ();
3207 g_object_ref (selector);
3209 for (node = recipients; node != NULL; node = g_slist_next (node)) {
3210 if (!modest_address_book_has_address ((const gchar *) node->data)) {
3211 hildon_touch_selector_append_text (HILDON_TOUCH_SELECTOR (selector),
3212 (const gchar *) node->data);
3213 contacts_to_add = TRUE;
3217 if (contacts_to_add) {
3220 picker_dialog = hildon_picker_dialog_new (GTK_WINDOW (self));
3221 gtk_window_set_title (GTK_WINDOW (picker_dialog), _("mcen_me_viewer_addtocontacts"));
3223 hildon_picker_dialog_set_selector (HILDON_PICKER_DIALOG (picker_dialog),
3224 HILDON_TOUCH_SELECTOR (selector));
3226 picker_result = gtk_dialog_run (GTK_DIALOG (picker_dialog));
3228 if (picker_result == GTK_RESPONSE_OK) {
3229 selected = hildon_touch_selector_get_current_text (HILDON_TOUCH_SELECTOR (selector));
3231 gtk_widget_destroy (picker_dialog);
3234 modest_address_book_add_address (selected);
3239 g_object_unref (selector);
3244 if (recipients) {g_slist_foreach (recipients, (GFunc) g_free, NULL); g_slist_free (recipients);}
3245 g_object_unref (msg);