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-runtime.h>
46 #include <modest-window-priv.h>
47 #include <modest-tny-folder.h>
48 #include <modest-text-utils.h>
49 #include <modest-account-mgr-helpers.h>
50 #include "modest-progress-bar.h"
51 #include "modest-defs.h"
52 #include "modest-hildon-includes.h"
53 #include "modest-ui-dimming-manager.h"
54 #include <gdk/gdkkeysyms.h>
55 #include <modest-tny-account.h>
56 #include <modest-mime-part-view.h>
57 #include <modest-isearch-view.h>
58 #include <modest-tny-mime-part.h>
61 #include <glib/gstdio.h>
62 #include <modest-debug.h>
64 #define DEFAULT_FOLDER "MyDocs/.documents"
66 static void modest_msg_view_window_class_init (ModestMsgViewWindowClass *klass);
67 static void modest_msg_view_window_init (ModestMsgViewWindow *obj);
68 static void modest_header_view_observer_init(
69 ModestHeaderViewObserverIface *iface_class);
70 static void modest_msg_view_window_finalize (GObject *obj);
71 static void modest_msg_view_window_toggle_find_toolbar (GtkToggleAction *obj,
73 static void modest_msg_view_window_find_toolbar_close (GtkWidget *widget,
74 ModestMsgViewWindow *obj);
75 static void modest_msg_view_window_find_toolbar_search (GtkWidget *widget,
76 ModestMsgViewWindow *obj);
78 static void modest_msg_view_window_disconnect_signals (ModestWindow *self);
79 static void modest_msg_view_window_set_zoom (ModestWindow *window,
81 static gdouble modest_msg_view_window_get_zoom (ModestWindow *window);
82 static gboolean modest_msg_view_window_zoom_minus (ModestWindow *window);
83 static gboolean modest_msg_view_window_zoom_plus (ModestWindow *window);
84 static gboolean modest_msg_view_window_key_event (GtkWidget *window,
87 static gboolean modest_msg_view_window_window_state_event (GtkWidget *widget,
88 GdkEventWindowState *event,
90 static void modest_msg_view_window_update_priority (ModestMsgViewWindow *window);
92 static void modest_msg_view_window_show_toolbar (ModestWindow *window,
93 gboolean show_toolbar);
95 static void modest_msg_view_window_clipboard_owner_change (GtkClipboard *clipboard,
97 ModestMsgViewWindow *window);
99 static void modest_msg_view_window_on_row_changed (GtkTreeModel *header_model,
102 ModestMsgViewWindow *window);
104 static void modest_msg_view_window_on_row_deleted (GtkTreeModel *header_model,
106 ModestMsgViewWindow *window);
108 static void modest_msg_view_window_on_row_inserted (GtkTreeModel *header_model,
109 GtkTreePath *tree_path,
110 GtkTreeIter *tree_iter,
111 ModestMsgViewWindow *window);
113 static void modest_msg_view_window_on_row_reordered (GtkTreeModel *header_model,
117 ModestMsgViewWindow *window);
119 static void modest_msg_view_window_update_model_replaced (ModestHeaderViewObserver *window,
121 const gchar *tny_folder_id);
123 static void cancel_progressbar (GtkToolButton *toolbutton,
124 ModestMsgViewWindow *self);
126 static void on_queue_changed (ModestMailOperationQueue *queue,
127 ModestMailOperation *mail_op,
128 ModestMailOperationQueueNotification type,
129 ModestMsgViewWindow *self);
131 static void on_account_removed (TnyAccountStore *account_store,
135 static void on_move_focus (GtkWidget *widget,
136 GtkDirectionType direction,
139 static void view_msg_cb (ModestMailOperation *mail_op,
146 static void set_toolbar_mode (ModestMsgViewWindow *self,
147 ModestToolBarModes mode);
149 static void update_window_title (ModestMsgViewWindow *window);
151 static gboolean set_toolbar_transfer_mode (ModestMsgViewWindow *self);
152 static void init_window (ModestMsgViewWindow *obj);
154 static gboolean msg_is_visible (TnyHeader *header, gboolean check_outbox);
156 static void check_dimming_rules_after_change (ModestMsgViewWindow *window);
158 static gboolean on_fetch_image (ModestMsgView *msgview,
161 ModestMsgViewWindow *window);
163 static gboolean modest_msg_view_window_scroll_child (ModestMsgViewWindow *self,
164 GtkScrollType scroll_type,
168 /* list my signals */
175 static const GtkToggleActionEntry msg_view_toggle_action_entries [] = {
176 { "FindInMessage", MODEST_TOOLBAR_ICON_FIND, N_("qgn_toolb_gene_find"), NULL, NULL, G_CALLBACK (modest_msg_view_window_toggle_find_toolbar), FALSE },
177 { "ToolsFindInMessage", NULL, N_("mcen_me_viewer_find"), "<CTRL>F", NULL, G_CALLBACK (modest_msg_view_window_toggle_find_toolbar), FALSE },
180 static const GtkRadioActionEntry msg_view_zoom_action_entries [] = {
181 { "Zoom50", NULL, N_("mcen_me_viewer_50"), NULL, NULL, 50 },
182 { "Zoom80", NULL, N_("mcen_me_viewer_80"), NULL, NULL, 80 },
183 { "Zoom100", NULL, N_("mcen_me_viewer_100"), NULL, NULL, 100 },
184 { "Zoom120", NULL, N_("mcen_me_viewer_120"), NULL, NULL, 120 },
185 { "Zoom150", NULL, N_("mcen_me_viewer_150"), NULL, NULL, 150 },
186 { "Zoom200", NULL, N_("mcen_me_viewer_200"), NULL, NULL, 200 }
189 typedef struct _ModestMsgViewWindowPrivate ModestMsgViewWindowPrivate;
190 struct _ModestMsgViewWindowPrivate {
193 GtkWidget *main_scroll;
194 GtkWidget *find_toolbar;
197 /* Progress observers */
198 GtkWidget *progress_bar;
199 GSList *progress_widgets;
202 GtkWidget *progress_toolitem;
203 GtkWidget *cancel_toolitem;
204 GtkWidget *prev_toolitem;
205 GtkWidget *next_toolitem;
206 ModestToolBarModes current_toolbar_mode;
208 /* Optimized view enabled */
209 gboolean optimized_view;
211 /* Whether this was created via the *_new_for_search_result() function. */
212 gboolean is_search_result;
214 /* Whether the message is in outbox */
217 /* A reference to the @model of the header view
218 * to allow selecting previous/next messages,
219 * if the message is currently selected in the header view.
221 const gchar *header_folder_id;
222 GtkTreeModel *header_model;
223 GtkTreeRowReference *row_reference;
224 GtkTreeRowReference *next_row_reference;
226 gulong clipboard_change_handler;
227 gulong queue_change_handler;
228 gulong account_removed_handler;
229 gulong row_changed_handler;
230 gulong row_deleted_handler;
231 gulong row_inserted_handler;
232 gulong rows_reordered_handler;
235 GtkWidget *remove_attachment_banner;
237 guint progress_bar_timeout;
244 #define MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE((o), \
245 MODEST_TYPE_MSG_VIEW_WINDOW, \
246 ModestMsgViewWindowPrivate))
248 static GtkWindowClass *parent_class = NULL;
250 /* uncomment the following if you have defined any signals */
251 static guint signals[LAST_SIGNAL] = {0};
254 modest_msg_view_window_get_type (void)
256 static GType my_type = 0;
258 static const GTypeInfo my_info = {
259 sizeof(ModestMsgViewWindowClass),
260 NULL, /* base init */
261 NULL, /* base finalize */
262 (GClassInitFunc) modest_msg_view_window_class_init,
263 NULL, /* class finalize */
264 NULL, /* class data */
265 sizeof(ModestMsgViewWindow),
267 (GInstanceInitFunc) modest_msg_view_window_init,
270 my_type = g_type_register_static (MODEST_TYPE_WINDOW,
271 "ModestMsgViewWindow",
274 static const GInterfaceInfo modest_header_view_observer_info =
276 (GInterfaceInitFunc) modest_header_view_observer_init,
277 NULL, /* interface_finalize */
278 NULL /* interface_data */
281 g_type_add_interface_static (my_type,
282 MODEST_TYPE_HEADER_VIEW_OBSERVER,
283 &modest_header_view_observer_info);
289 save_state (ModestWindow *self)
291 modest_widget_memory_save (modest_runtime_get_conf (),
293 MODEST_CONF_MSG_VIEW_WINDOW_KEY);
298 restore_settings (ModestMsgViewWindow *self)
301 ModestWindowPrivate *parent_priv = MODEST_WINDOW_GET_PRIVATE (self);
304 conf = modest_runtime_get_conf ();
305 action = gtk_ui_manager_get_action (parent_priv->ui_manager,
306 "/MenuBar/ViewMenu/ViewShowToolbarMenu/ViewShowToolbarNormalScreenMenu");
307 gtk_toggle_action_set_active (GTK_TOGGLE_ACTION (action),
308 modest_conf_get_bool (conf, MODEST_CONF_MSG_VIEW_WINDOW_SHOW_TOOLBAR, NULL));
309 action = gtk_ui_manager_get_action (parent_priv->ui_manager,
310 "/MenuBar/ViewMenu/ViewShowToolbarMenu/ViewShowToolbarFullScreenMenu");
311 gtk_toggle_action_set_active (GTK_TOGGLE_ACTION (action),
312 modest_conf_get_bool (conf, MODEST_CONF_MSG_VIEW_WINDOW_SHOW_TOOLBAR_FULLSCREEN, NULL));
313 modest_widget_memory_restore (conf,
315 MODEST_CONF_MSG_VIEW_WINDOW_KEY);
318 static gboolean modest_msg_view_window_scroll_child (ModestMsgViewWindow *self,
319 GtkScrollType scroll_type,
323 ModestMsgViewWindowPrivate *priv;
324 gboolean return_value;
326 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(self);
327 g_signal_emit_by_name (priv->main_scroll, "scroll-child", scroll_type, horizontal, &return_value);
332 add_scroll_binding (GtkBindingSet *binding_set,
334 GtkScrollType scroll)
336 guint keypad_keyval = keyval - GDK_Left + GDK_KP_Left;
338 gtk_binding_entry_add_signal (binding_set, keyval, 0,
340 GTK_TYPE_SCROLL_TYPE, scroll,
341 G_TYPE_BOOLEAN, FALSE);
342 gtk_binding_entry_add_signal (binding_set, keypad_keyval, 0,
344 GTK_TYPE_SCROLL_TYPE, scroll,
345 G_TYPE_BOOLEAN, FALSE);
349 modest_msg_view_window_class_init (ModestMsgViewWindowClass *klass)
351 GObjectClass *gobject_class;
352 ModestWindowClass *modest_window_class;
353 GtkBindingSet *binding_set;
355 gobject_class = (GObjectClass*) klass;
356 modest_window_class = (ModestWindowClass *) klass;
358 parent_class = g_type_class_peek_parent (klass);
359 gobject_class->finalize = modest_msg_view_window_finalize;
361 modest_window_class->set_zoom_func = modest_msg_view_window_set_zoom;
362 modest_window_class->get_zoom_func = modest_msg_view_window_get_zoom;
363 modest_window_class->zoom_minus_func = modest_msg_view_window_zoom_minus;
364 modest_window_class->zoom_plus_func = modest_msg_view_window_zoom_plus;
365 modest_window_class->show_toolbar_func = modest_msg_view_window_show_toolbar;
366 modest_window_class->disconnect_signals_func = modest_msg_view_window_disconnect_signals;
368 modest_window_class->save_state_func = save_state;
370 klass->scroll_child = modest_msg_view_window_scroll_child;
372 signals[MSG_CHANGED_SIGNAL] =
373 g_signal_new ("msg-changed",
374 G_TYPE_FROM_CLASS (gobject_class),
376 G_STRUCT_OFFSET (ModestMsgViewWindowClass, msg_changed),
378 modest_marshal_VOID__POINTER_POINTER,
379 G_TYPE_NONE, 2, G_TYPE_POINTER, G_TYPE_POINTER);
381 signals[SCROLL_CHILD_SIGNAL] =
382 g_signal_new ("scroll-child",
383 G_TYPE_FROM_CLASS (gobject_class),
384 G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
385 G_STRUCT_OFFSET (ModestMsgViewWindowClass, scroll_child),
387 modest_marshal_BOOLEAN__ENUM_BOOLEAN,
388 G_TYPE_BOOLEAN, 2, GTK_TYPE_SCROLL_TYPE, G_TYPE_BOOLEAN);
390 binding_set = gtk_binding_set_by_class (klass);
391 add_scroll_binding (binding_set, GDK_Up, GTK_SCROLL_STEP_UP);
392 add_scroll_binding (binding_set, GDK_Down, GTK_SCROLL_STEP_DOWN);
393 add_scroll_binding (binding_set, GDK_Page_Up, GTK_SCROLL_PAGE_UP);
394 add_scroll_binding (binding_set, GDK_Page_Down, GTK_SCROLL_PAGE_DOWN);
395 add_scroll_binding (binding_set, GDK_Home, GTK_SCROLL_START);
396 add_scroll_binding (binding_set, GDK_End, GTK_SCROLL_END);
398 g_type_class_add_private (gobject_class, sizeof(ModestMsgViewWindowPrivate));
402 static void modest_header_view_observer_init(
403 ModestHeaderViewObserverIface *iface_class)
405 iface_class->update_func = modest_msg_view_window_update_model_replaced;
409 modest_msg_view_window_init (ModestMsgViewWindow *obj)
411 ModestMsgViewWindowPrivate *priv;
412 ModestWindowPrivate *parent_priv = NULL;
413 GtkActionGroup *action_group = NULL;
414 GError *error = NULL;
415 GdkPixbuf *window_icon;
417 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(obj);
418 parent_priv = MODEST_WINDOW_GET_PRIVATE(obj);
419 parent_priv->ui_manager = gtk_ui_manager_new();
421 action_group = gtk_action_group_new ("ModestMsgViewWindowActions");
422 gtk_action_group_set_translation_domain (action_group, GETTEXT_PACKAGE);
424 /* Add common actions */
425 gtk_action_group_add_actions (action_group,
426 modest_action_entries,
427 G_N_ELEMENTS (modest_action_entries),
429 gtk_action_group_add_toggle_actions (action_group,
430 modest_toggle_action_entries,
431 G_N_ELEMENTS (modest_toggle_action_entries),
433 gtk_action_group_add_toggle_actions (action_group,
434 msg_view_toggle_action_entries,
435 G_N_ELEMENTS (msg_view_toggle_action_entries),
437 gtk_action_group_add_radio_actions (action_group,
438 msg_view_zoom_action_entries,
439 G_N_ELEMENTS (msg_view_zoom_action_entries),
441 G_CALLBACK (modest_ui_actions_on_change_zoom),
444 gtk_ui_manager_insert_action_group (parent_priv->ui_manager, action_group, 0);
445 g_object_unref (action_group);
447 /* Load the UI definition */
448 gtk_ui_manager_add_ui_from_file (parent_priv->ui_manager, MODEST_UIDIR "modest-msg-view-window-ui.xml",
451 g_printerr ("modest: could not merge modest-msg-view-window-ui.xml: %s\n", error->message);
452 g_error_free (error);
457 /* Add accelerators */
458 gtk_window_add_accel_group (GTK_WINDOW (obj),
459 gtk_ui_manager_get_accel_group (parent_priv->ui_manager));
461 priv->is_search_result = FALSE;
462 priv->is_outbox = FALSE;
464 priv->msg_view = NULL;
465 priv->header_model = NULL;
466 priv->header_folder_id = NULL;
467 priv->clipboard_change_handler = 0;
468 priv->queue_change_handler = 0;
469 priv->account_removed_handler = 0;
470 priv->row_changed_handler = 0;
471 priv->row_deleted_handler = 0;
472 priv->row_inserted_handler = 0;
473 priv->rows_reordered_handler = 0;
474 priv->current_toolbar_mode = TOOLBAR_MODE_NORMAL;
476 priv->optimized_view = FALSE;
477 priv->progress_bar_timeout = 0;
478 priv->purge_timeout = 0;
479 priv->remove_attachment_banner = NULL;
480 priv->msg_uid = NULL;
482 priv->sighandlers = NULL;
485 init_window (MODEST_MSG_VIEW_WINDOW(obj));
487 /* Set window icon */
488 window_icon = modest_platform_get_icon (MODEST_APP_MSG_VIEW_ICON, MODEST_ICON_SIZE_BIG);
490 gtk_window_set_icon (GTK_WINDOW (obj), window_icon);
491 g_object_unref (window_icon);
494 hildon_program_add_window (hildon_program_get_instance(),
497 modest_window_mgr_register_help_id (modest_runtime_get_window_mgr(),
498 GTK_WINDOW(obj),"applications_email_viewer");
503 set_toolbar_transfer_mode (ModestMsgViewWindow *self)
505 ModestMsgViewWindowPrivate *priv = NULL;
507 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self), FALSE);
509 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(self);
511 set_toolbar_mode (self, TOOLBAR_MODE_TRANSFER);
513 if (priv->progress_bar_timeout > 0) {
514 g_source_remove (priv->progress_bar_timeout);
515 priv->progress_bar_timeout = 0;
522 set_toolbar_mode (ModestMsgViewWindow *self,
523 ModestToolBarModes mode)
525 ModestWindowPrivate *parent_priv;
526 ModestMsgViewWindowPrivate *priv;
527 /* GtkWidget *widget = NULL; */
529 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self));
531 parent_priv = MODEST_WINDOW_GET_PRIVATE(self);
532 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(self);
534 /* Sets current toolbar mode */
535 priv->current_toolbar_mode = mode;
537 /* Update toolbar dimming state */
538 modest_ui_actions_check_toolbar_dimming_rules (MODEST_WINDOW (self));
541 case TOOLBAR_MODE_NORMAL:
542 if (priv->progress_toolitem) {
543 gtk_tool_item_set_expand (GTK_TOOL_ITEM (priv->progress_toolitem), FALSE);
544 gtk_widget_hide (priv->progress_toolitem);
547 if (priv->progress_bar)
548 gtk_widget_hide (priv->progress_bar);
550 if (priv->cancel_toolitem)
551 gtk_widget_hide (priv->cancel_toolitem);
553 if (priv->prev_toolitem)
554 gtk_widget_show (priv->prev_toolitem);
556 if (priv->next_toolitem)
557 gtk_widget_show (priv->next_toolitem);
559 /* Hide toolbar if optimized view is enabled */
560 if (priv->optimized_view) {
561 gtk_widget_set_no_show_all (parent_priv->toolbar, TRUE);
562 gtk_widget_hide (GTK_WIDGET(parent_priv->toolbar));
566 case TOOLBAR_MODE_TRANSFER:
567 if (priv->prev_toolitem)
568 gtk_widget_hide (priv->prev_toolitem);
570 if (priv->next_toolitem)
571 gtk_widget_hide (priv->next_toolitem);
573 if (priv->progress_bar)
574 gtk_widget_show (priv->progress_bar);
576 if (priv->progress_toolitem) {
577 gtk_tool_item_set_expand (GTK_TOOL_ITEM (priv->progress_toolitem), TRUE);
578 gtk_widget_show (priv->progress_toolitem);
581 if (priv->cancel_toolitem)
582 gtk_widget_show (priv->cancel_toolitem);
584 /* Show toolbar if it's hiden (optimized view ) */
585 if (priv->optimized_view) {
586 gtk_widget_set_no_show_all (parent_priv->toolbar, FALSE);
587 gtk_widget_show (GTK_WIDGET (parent_priv->toolbar));
592 g_return_if_reached ();
599 init_window (ModestMsgViewWindow *obj)
601 GtkWidget *main_vbox;
602 ModestMsgViewWindowPrivate *priv;
604 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(obj);
606 priv->msg_view = GTK_WIDGET (tny_platform_factory_new_msg_view (modest_tny_platform_factory_get_instance ()));
607 modest_msg_view_set_shadow_type (MODEST_MSG_VIEW (priv->msg_view), GTK_SHADOW_NONE);
608 main_vbox = gtk_vbox_new (FALSE, 6);
610 #ifdef MODEST_USE_MOZEMBED
611 priv->main_scroll = priv->msg_view;
612 gtk_widget_set_size_request (priv->msg_view, -1, 1600);
614 priv->main_scroll = gtk_scrolled_window_new (NULL, NULL);
615 gtk_container_add (GTK_CONTAINER (priv->main_scroll), priv->msg_view);
617 gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (priv->main_scroll), GTK_POLICY_NEVER, GTK_POLICY_AUTOMATIC);
618 gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW (priv->main_scroll), GTK_SHADOW_NONE);
619 modest_maemo_set_thumbable_scrollbar (GTK_SCROLLED_WINDOW(priv->main_scroll), TRUE);
621 gtk_box_pack_start (GTK_BOX(main_vbox), priv->main_scroll, TRUE, TRUE, 0);
622 gtk_container_add (GTK_CONTAINER(obj), main_vbox);
624 priv->find_toolbar = hildon_find_toolbar_new (NULL);
625 hildon_window_add_toolbar (HILDON_WINDOW (obj), GTK_TOOLBAR (priv->find_toolbar));
626 gtk_widget_set_no_show_all (priv->find_toolbar, TRUE);
628 gtk_widget_show_all (GTK_WIDGET(main_vbox));
632 modest_msg_view_window_disconnect_signals (ModestWindow *self)
634 ModestMsgViewWindowPrivate *priv;
635 ModestHeaderView *header_view = NULL;
636 ModestWindow *main_window = NULL;
638 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
640 if (gtk_clipboard_get (GDK_SELECTION_PRIMARY) &&
641 g_signal_handler_is_connected (gtk_clipboard_get (GDK_SELECTION_PRIMARY),
642 priv->clipboard_change_handler))
643 g_signal_handler_disconnect (gtk_clipboard_get (GDK_SELECTION_PRIMARY),
644 priv->clipboard_change_handler);
646 if (g_signal_handler_is_connected (G_OBJECT (modest_runtime_get_mail_operation_queue ()),
647 priv->queue_change_handler))
648 g_signal_handler_disconnect (G_OBJECT (modest_runtime_get_mail_operation_queue ()),
649 priv->queue_change_handler);
651 if (g_signal_handler_is_connected (G_OBJECT (modest_runtime_get_account_store ()),
652 priv->account_removed_handler))
653 g_signal_handler_disconnect (G_OBJECT (modest_runtime_get_account_store ()),
654 priv->account_removed_handler);
656 if (priv->header_model) {
657 if (g_signal_handler_is_connected(G_OBJECT (priv->header_model),
658 priv->row_changed_handler))
659 g_signal_handler_disconnect(G_OBJECT (priv->header_model),
660 priv->row_changed_handler);
662 if (g_signal_handler_is_connected(G_OBJECT (priv->header_model),
663 priv->row_deleted_handler))
664 g_signal_handler_disconnect(G_OBJECT (priv->header_model),
665 priv->row_deleted_handler);
667 if (g_signal_handler_is_connected(G_OBJECT (priv->header_model),
668 priv->row_inserted_handler))
669 g_signal_handler_disconnect(G_OBJECT (priv->header_model),
670 priv->row_inserted_handler);
672 if (g_signal_handler_is_connected(G_OBJECT (priv->header_model),
673 priv->rows_reordered_handler))
674 g_signal_handler_disconnect(G_OBJECT (priv->header_model),
675 priv->rows_reordered_handler);
678 modest_signal_mgr_disconnect_all_and_destroy (priv->sighandlers);
679 priv->sighandlers = NULL;
681 main_window = modest_window_mgr_get_main_window (modest_runtime_get_window_mgr(),
682 FALSE); /* don't create */
686 header_view = MODEST_HEADER_VIEW(
687 modest_main_window_get_child_widget(
688 MODEST_MAIN_WINDOW(main_window),
689 MODEST_MAIN_WINDOW_WIDGET_TYPE_HEADER_VIEW));
690 if (header_view == NULL)
693 modest_header_view_remove_observer(header_view,
694 MODEST_HEADER_VIEW_OBSERVER(self));
698 modest_msg_view_window_finalize (GObject *obj)
700 ModestMsgViewWindowPrivate *priv;
702 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (obj);
704 /* Sanity check: shouldn't be needed, the window mgr should
705 call this function before */
706 modest_msg_view_window_disconnect_signals (MODEST_WINDOW (obj));
708 if (priv->header_model != NULL) {
709 g_object_unref (priv->header_model);
710 priv->header_model = NULL;
713 if (priv->progress_bar_timeout > 0) {
714 g_source_remove (priv->progress_bar_timeout);
715 priv->progress_bar_timeout = 0;
718 if (priv->remove_attachment_banner) {
719 gtk_widget_destroy (priv->remove_attachment_banner);
720 g_object_unref (priv->remove_attachment_banner);
721 priv->remove_attachment_banner = NULL;
724 if (priv->purge_timeout > 0) {
725 g_source_remove (priv->purge_timeout);
726 priv->purge_timeout = 0;
729 if (priv->row_reference) {
730 gtk_tree_row_reference_free (priv->row_reference);
731 priv->row_reference = NULL;
734 if (priv->next_row_reference) {
735 gtk_tree_row_reference_free (priv->next_row_reference);
736 priv->next_row_reference = NULL;
740 g_free (priv->msg_uid);
741 priv->msg_uid = NULL;
744 G_OBJECT_CLASS(parent_class)->finalize (obj);
748 select_next_valid_row (GtkTreeModel *model,
749 GtkTreeRowReference **row_reference,
753 GtkTreeIter tmp_iter;
755 GtkTreePath *next = NULL;
756 gboolean retval = FALSE, finished;
758 g_return_val_if_fail (gtk_tree_row_reference_valid (*row_reference), FALSE);
760 path = gtk_tree_row_reference_get_path (*row_reference);
761 gtk_tree_model_get_iter (model, &tmp_iter, path);
762 gtk_tree_row_reference_free (*row_reference);
763 *row_reference = NULL;
767 TnyHeader *header = NULL;
769 if (gtk_tree_model_iter_next (model, &tmp_iter)) {
770 gtk_tree_model_get (model, &tmp_iter,
771 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
775 if (msg_is_visible (header, is_outbox)) {
776 next = gtk_tree_model_get_path (model, &tmp_iter);
777 *row_reference = gtk_tree_row_reference_new (model, next);
778 gtk_tree_path_free (next);
782 g_object_unref (header);
785 } else if (cycle && gtk_tree_model_get_iter_first (model, &tmp_iter)) {
786 next = gtk_tree_model_get_path (model, &tmp_iter);
788 /* Ensure that we are not selecting the same */
789 if (gtk_tree_path_compare (path, next) != 0) {
790 gtk_tree_model_get (model, &tmp_iter,
791 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
794 if (msg_is_visible (header, is_outbox)) {
795 *row_reference = gtk_tree_row_reference_new (model, next);
799 g_object_unref (header);
803 /* If we ended up in the same message
804 then there is no valid next
808 gtk_tree_path_free (next);
810 /* If there are no more messages and we don't
811 want to start again in the first one then
812 there is no valid next message */
818 gtk_tree_path_free (path);
823 /* TODO: This should be in _init(), with the parameters as properties. */
825 modest_msg_view_window_construct (ModestMsgViewWindow *self,
826 const gchar *modest_account_name,
827 const gchar *msg_uid)
830 ModestMsgViewWindowPrivate *priv = NULL;
831 ModestWindowPrivate *parent_priv = NULL;
832 ModestDimmingRulesGroup *menu_rules_group = NULL;
833 ModestDimmingRulesGroup *toolbar_rules_group = NULL;
834 ModestDimmingRulesGroup *clipboard_rules_group = NULL;
836 obj = G_OBJECT (self);
837 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(obj);
838 parent_priv = MODEST_WINDOW_GET_PRIVATE(obj);
840 priv->msg_uid = g_strdup (msg_uid);
843 parent_priv->menubar = modest_maemo_utils_get_manager_menubar_as_menu (parent_priv->ui_manager, "/MenuBar");
844 hildon_window_set_menu (HILDON_WINDOW(obj), GTK_MENU(parent_priv->menubar));
845 gtk_widget_show (parent_priv->menubar);
846 parent_priv->ui_dimming_manager = modest_ui_dimming_manager_new();
848 menu_rules_group = modest_dimming_rules_group_new (MODEST_DIMMING_RULES_MENU, FALSE);
849 toolbar_rules_group = modest_dimming_rules_group_new (MODEST_DIMMING_RULES_TOOLBAR, TRUE);
850 clipboard_rules_group = modest_dimming_rules_group_new (MODEST_DIMMING_RULES_CLIPBOARD, FALSE);
852 /* Add common dimming rules */
853 modest_dimming_rules_group_add_rules (menu_rules_group,
854 modest_msg_view_menu_dimming_entries,
855 G_N_ELEMENTS (modest_msg_view_menu_dimming_entries),
856 MODEST_WINDOW (self));
857 modest_dimming_rules_group_add_rules (toolbar_rules_group,
858 modest_msg_view_toolbar_dimming_entries,
859 G_N_ELEMENTS (modest_msg_view_toolbar_dimming_entries),
860 MODEST_WINDOW (self));
861 modest_dimming_rules_group_add_rules (clipboard_rules_group,
862 modest_msg_view_clipboard_dimming_entries,
863 G_N_ELEMENTS (modest_msg_view_clipboard_dimming_entries),
864 MODEST_WINDOW (self));
866 /* Insert dimming rules group for this window */
867 modest_ui_dimming_manager_insert_rules_group (parent_priv->ui_dimming_manager, menu_rules_group);
868 modest_ui_dimming_manager_insert_rules_group (parent_priv->ui_dimming_manager, toolbar_rules_group);
869 modest_ui_dimming_manager_insert_rules_group (parent_priv->ui_dimming_manager, clipboard_rules_group);
870 g_object_unref (menu_rules_group);
871 g_object_unref (toolbar_rules_group);
872 g_object_unref (clipboard_rules_group);
874 restore_settings (MODEST_MSG_VIEW_WINDOW(obj));
876 /* g_signal_connect (G_OBJECT(obj), "delete-event", G_CALLBACK(on_delete_event), obj); */
878 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);
879 g_signal_connect (G_OBJECT(priv->msg_view), "activate_link",
880 G_CALLBACK (modest_ui_actions_on_msg_link_clicked), obj);
881 g_signal_connect (G_OBJECT(priv->msg_view), "link_hover",
882 G_CALLBACK (modest_ui_actions_on_msg_link_hover), obj);
883 g_signal_connect (G_OBJECT(priv->msg_view), "attachment_clicked",
884 G_CALLBACK (modest_ui_actions_on_msg_attachment_clicked), obj);
885 g_signal_connect (G_OBJECT(priv->msg_view), "recpt_activated",
886 G_CALLBACK (modest_ui_actions_on_msg_recpt_activated), obj);
887 g_signal_connect (G_OBJECT(priv->msg_view), "link_contextual",
888 G_CALLBACK (modest_ui_actions_on_msg_link_contextual), obj);
889 g_signal_connect (G_OBJECT (priv->msg_view), "fetch_image",
890 G_CALLBACK (on_fetch_image), obj);
892 g_signal_connect (G_OBJECT (obj), "key-release-event",
893 G_CALLBACK (modest_msg_view_window_key_event),
896 g_signal_connect (G_OBJECT (obj), "key-press-event",
897 G_CALLBACK (modest_msg_view_window_key_event),
900 g_signal_connect (G_OBJECT (obj), "window-state-event",
901 G_CALLBACK (modest_msg_view_window_window_state_event),
904 g_signal_connect (G_OBJECT (obj), "move-focus",
905 G_CALLBACK (on_move_focus), obj);
907 /* Mail Operation Queue */
908 priv->queue_change_handler = g_signal_connect (G_OBJECT (modest_runtime_get_mail_operation_queue ()),
910 G_CALLBACK (on_queue_changed),
913 /* Account manager */
914 priv->account_removed_handler = g_signal_connect (G_OBJECT (modest_runtime_get_account_store ()),
916 G_CALLBACK(on_account_removed),
919 modest_window_set_active_account (MODEST_WINDOW(obj), modest_account_name);
921 g_signal_connect (G_OBJECT (priv->find_toolbar), "close", G_CALLBACK (modest_msg_view_window_find_toolbar_close), obj);
922 g_signal_connect (G_OBJECT (priv->find_toolbar), "search", G_CALLBACK (modest_msg_view_window_find_toolbar_search), obj);
923 priv->last_search = NULL;
925 /* Init the clipboard actions dim status */
926 modest_msg_view_grab_focus(MODEST_MSG_VIEW (priv->msg_view));
928 update_window_title (MODEST_MSG_VIEW_WINDOW (obj));
933 /* FIXME: parameter checks */
935 modest_msg_view_window_new_with_header_model (TnyMsg *msg,
936 const gchar *modest_account_name,
937 const gchar *mailbox, /*ignored */
938 const gchar *msg_uid,
940 GtkTreeRowReference *row_reference)
942 ModestMsgViewWindow *window = NULL;
943 ModestMsgViewWindowPrivate *priv = NULL;
944 TnyFolder *header_folder = NULL;
945 ModestHeaderView *header_view = NULL;
946 ModestWindow *main_window = NULL;
947 ModestWindowMgr *mgr = NULL;
950 modest_tny_mime_part_to_string (TNY_MIME_PART (msg), 0);
953 mgr = modest_runtime_get_window_mgr ();
954 window = MODEST_MSG_VIEW_WINDOW (modest_window_mgr_get_msg_view_window (mgr));
955 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), NULL);
957 modest_msg_view_window_construct (window, modest_account_name, msg_uid);
959 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
961 /* Remember the message list's TreeModel so we can detect changes
962 * and change the list selection when necessary: */
964 main_window = modest_window_mgr_get_main_window(mgr, FALSE); /* don't create */
966 header_view = MODEST_HEADER_VIEW(modest_main_window_get_child_widget(
967 MODEST_MAIN_WINDOW(main_window),
968 MODEST_MAIN_WINDOW_WIDGET_TYPE_HEADER_VIEW));
971 if (header_view != NULL){
972 header_folder = modest_header_view_get_folder(header_view);
973 /* This could happen if the header folder was
974 unseleted before opening this msg window (for
975 example if the user selects an account in the
976 folder view of the main window */
978 priv->is_outbox = (modest_tny_folder_guess_folder_type (header_folder) == TNY_FOLDER_TYPE_OUTBOX);
979 priv->header_folder_id = tny_folder_get_id(header_folder);
980 g_assert(priv->header_folder_id != NULL);
981 g_object_unref(header_folder);
985 /* Setup row references and connect signals */
986 priv->header_model = g_object_ref (model);
989 priv->row_reference = gtk_tree_row_reference_copy (row_reference);
990 priv->next_row_reference = gtk_tree_row_reference_copy (row_reference);
991 select_next_valid_row (model, &(priv->next_row_reference), TRUE, priv->is_outbox);
993 priv->row_reference = NULL;
994 priv->next_row_reference = NULL;
997 /* Connect signals */
998 priv->row_changed_handler =
999 g_signal_connect (GTK_TREE_MODEL(model), "row-changed",
1000 G_CALLBACK(modest_msg_view_window_on_row_changed),
1002 priv->row_deleted_handler =
1003 g_signal_connect (GTK_TREE_MODEL(model), "row-deleted",
1004 G_CALLBACK(modest_msg_view_window_on_row_deleted),
1006 priv->row_inserted_handler =
1007 g_signal_connect (GTK_TREE_MODEL(model), "row-inserted",
1008 G_CALLBACK(modest_msg_view_window_on_row_inserted),
1010 priv->rows_reordered_handler =
1011 g_signal_connect(GTK_TREE_MODEL(model), "rows-reordered",
1012 G_CALLBACK(modest_msg_view_window_on_row_reordered),
1015 if (header_view != NULL){
1016 modest_header_view_add_observer(header_view,
1017 MODEST_HEADER_VIEW_OBSERVER(window));
1020 tny_msg_view_set_msg (TNY_MSG_VIEW (priv->msg_view), msg);
1021 update_window_title (MODEST_MSG_VIEW_WINDOW (window));
1022 gtk_widget_show_all (GTK_WIDGET (window));
1023 modest_msg_view_window_update_priority (window);
1025 /* Check dimming rules */
1026 modest_ui_actions_check_toolbar_dimming_rules (MODEST_WINDOW (window));
1027 modest_ui_actions_check_menu_dimming_rules (MODEST_WINDOW (window));
1028 modest_window_check_dimming_rules_group (MODEST_WINDOW (window), MODEST_DIMMING_RULES_CLIPBOARD);
1030 return MODEST_WINDOW(window);
1034 modest_msg_view_window_new_for_search_result (TnyMsg *msg,
1035 const gchar *modest_account_name,
1036 const gchar *mailbox, /*ignored*/
1037 const gchar *msg_uid)
1039 ModestMsgViewWindow *window = NULL;
1040 ModestMsgViewWindowPrivate *priv = NULL;
1041 ModestWindowMgr *mgr = NULL;
1043 mgr = modest_runtime_get_window_mgr ();
1044 window = MODEST_MSG_VIEW_WINDOW (modest_window_mgr_get_msg_view_window (mgr));
1045 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), NULL);
1046 modest_msg_view_window_construct (window, modest_account_name, msg_uid);
1048 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1050 /* Remember that this is a search result,
1051 * so we can disable some UI appropriately: */
1052 priv->is_search_result = TRUE;
1054 tny_msg_view_set_msg (TNY_MSG_VIEW (priv->msg_view), msg);
1056 update_window_title (window);
1057 gtk_widget_show_all (GTK_WIDGET (window));
1058 modest_msg_view_window_update_priority (window);
1060 /* Check dimming rules */
1061 modest_ui_actions_check_toolbar_dimming_rules (MODEST_WINDOW (window));
1062 modest_ui_actions_check_menu_dimming_rules (MODEST_WINDOW (window));
1063 modest_window_check_dimming_rules_group (MODEST_WINDOW (window), MODEST_DIMMING_RULES_CLIPBOARD);
1065 return MODEST_WINDOW(window);
1069 modest_msg_view_window_new_with_other_body (TnyMsg *msg,
1071 const gchar *modest_account_name,
1072 const gchar *mailbox, /* ignored */
1073 const gchar *msg_uid)
1075 GObject *obj = NULL;
1076 ModestMsgViewWindowPrivate *priv;
1077 ModestWindowMgr *mgr = NULL;
1079 g_return_val_if_fail (msg, NULL);
1080 mgr = modest_runtime_get_window_mgr ();
1081 obj = G_OBJECT (modest_window_mgr_get_msg_view_window (mgr));
1082 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (obj);
1083 modest_msg_view_window_construct (MODEST_MSG_VIEW_WINDOW (obj),
1084 modest_account_name, msg_uid);
1087 modest_msg_view_set_msg_with_other_body (TNY_MSG_VIEW (priv->msg_view), msg, other_body);
1089 tny_msg_view_set_msg (TNY_MSG_VIEW (priv->msg_view), msg);
1091 update_window_title (MODEST_MSG_VIEW_WINDOW (obj));
1093 gtk_widget_show_all (GTK_WIDGET (obj));
1095 /* Check dimming rules */
1096 modest_ui_actions_check_toolbar_dimming_rules (MODEST_WINDOW (obj));
1097 modest_ui_actions_check_menu_dimming_rules (MODEST_WINDOW (obj));
1098 modest_window_check_dimming_rules_group (MODEST_WINDOW (obj), MODEST_DIMMING_RULES_CLIPBOARD);
1100 return MODEST_WINDOW(obj);
1104 modest_msg_view_window_new_for_attachment (TnyMsg *msg,
1105 const gchar *modest_account_name,
1106 const gchar *mailbox, /* ignored */
1107 const gchar *msg_uid)
1109 return modest_msg_view_window_new_with_other_body (msg, NULL, modest_account_name, mailbox, msg_uid);
1114 modest_msg_view_window_on_row_changed (GtkTreeModel *header_model,
1117 ModestMsgViewWindow *window)
1119 check_dimming_rules_after_change (window);
1123 modest_msg_view_window_on_row_deleted(GtkTreeModel *header_model,
1125 ModestMsgViewWindow *window)
1127 check_dimming_rules_after_change (window);
1129 /* The window could have dissapeared */
1132 check_dimming_rules_after_change (ModestMsgViewWindow *window)
1134 modest_ui_actions_check_menu_dimming_rules (MODEST_WINDOW (window));
1135 modest_ui_actions_check_toolbar_dimming_rules (MODEST_WINDOW (window));
1139 /* On insertions we check if the folder still has the message we are
1140 * showing or do not. If do not, we do nothing. Which means we are still
1141 * not attached to any header folder and thus next/prev buttons are
1142 * still dimmed. Once the message that is shown by msg-view is found, the
1143 * new model of header-view will be attached and the references will be set.
1144 * On each further insertions dimming rules will be checked. However
1145 * this requires extra CPU time at least works.
1146 * (An message might be deleted from TnyFolder and thus will not be
1147 * inserted into the model again for example if it is removed by the
1148 * imap server and the header view is refreshed.)
1151 modest_msg_view_window_on_row_inserted (GtkTreeModel *model,
1152 GtkTreePath *tree_path,
1153 GtkTreeIter *tree_iter,
1154 ModestMsgViewWindow *window)
1156 ModestMsgViewWindowPrivate *priv = NULL;
1157 TnyHeader *header = NULL;
1159 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window));
1160 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1162 g_assert (model == priv->header_model);
1164 /* Check if the newly inserted message is the same we are actually
1165 * showing. IF not, we should remain detached from the header model
1166 * and thus prev and next toolbar buttons should remain dimmed. */
1167 gtk_tree_model_get (model, tree_iter,
1168 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
1171 if (TNY_IS_HEADER (header)) {
1174 uid = modest_tny_folder_get_header_unique_id (header);
1175 if (!g_str_equal(priv->msg_uid, uid)) {
1176 check_dimming_rules_after_change (window);
1178 g_object_unref (G_OBJECT(header));
1182 g_object_unref(G_OBJECT(header));
1185 if (priv->row_reference) {
1186 gtk_tree_row_reference_free (priv->row_reference);
1189 /* Setup row_reference for the actual msg. */
1190 priv->row_reference = gtk_tree_row_reference_new (priv->header_model, tree_path);
1191 if (priv->row_reference == NULL) {
1192 g_warning("No reference for msg header item.");
1196 /* Now set up next_row_reference. */
1197 if (priv->next_row_reference) {
1198 gtk_tree_row_reference_free (priv->next_row_reference);
1201 priv->next_row_reference = gtk_tree_row_reference_copy (priv->row_reference);
1202 select_next_valid_row (priv->header_model,
1203 &(priv->next_row_reference), FALSE, priv->is_outbox);
1205 /* Connect the remaining callbacks to become able to detect
1206 * changes in header-view. */
1207 priv->row_changed_handler =
1208 g_signal_connect (priv->header_model, "row-changed",
1209 G_CALLBACK (modest_msg_view_window_on_row_changed),
1211 priv->row_deleted_handler =
1212 g_signal_connect (priv->header_model, "row-deleted",
1213 G_CALLBACK (modest_msg_view_window_on_row_deleted),
1215 priv->rows_reordered_handler =
1216 g_signal_connect (priv->header_model, "rows-reordered",
1217 G_CALLBACK (modest_msg_view_window_on_row_reordered),
1220 check_dimming_rules_after_change (window);
1224 modest_msg_view_window_on_row_reordered (GtkTreeModel *header_model,
1228 ModestMsgViewWindow *window)
1230 ModestMsgViewWindowPrivate *priv = NULL;
1231 gboolean already_changed = FALSE;
1233 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(window);
1235 /* If the current row was reordered select the proper next
1236 valid row. The same if the next row reference changes */
1237 if (priv->row_reference &&
1238 gtk_tree_row_reference_valid (priv->row_reference)) {
1240 path = gtk_tree_row_reference_get_path (priv->row_reference);
1241 if (gtk_tree_path_compare (path, arg1) == 0) {
1242 if (priv->next_row_reference) {
1243 gtk_tree_row_reference_free (priv->next_row_reference);
1245 priv->next_row_reference = gtk_tree_row_reference_copy (priv->row_reference);
1246 select_next_valid_row (header_model, &(priv->next_row_reference), FALSE, priv->is_outbox);
1247 already_changed = TRUE;
1249 gtk_tree_path_free (path);
1251 if (!already_changed &&
1252 priv->next_row_reference &&
1253 gtk_tree_row_reference_valid (priv->next_row_reference)) {
1255 path = gtk_tree_row_reference_get_path (priv->next_row_reference);
1256 if (gtk_tree_path_compare (path, arg1) == 0) {
1257 if (priv->next_row_reference) {
1258 gtk_tree_row_reference_free (priv->next_row_reference);
1260 priv->next_row_reference = gtk_tree_row_reference_copy (priv->row_reference);
1261 select_next_valid_row (header_model, &(priv->next_row_reference), FALSE, priv->is_outbox);
1263 gtk_tree_path_free (path);
1265 check_dimming_rules_after_change (window);
1268 /* The modest_msg_view_window_update_model_replaced implements update
1269 * function for ModestHeaderViewObserver. Checks whether the TnyFolder
1270 * actually belongs to the header-view is the same as the TnyFolder of
1271 * the message of msg-view or not. If they are different, there is
1272 * nothing to do. If they are the same, then the model has replaced and
1273 * the reference in msg-view shall be replaced from the old model to
1274 * the new model. In this case the view will be detached from it's
1275 * header folder. From this point the next/prev buttons are dimmed.
1278 modest_msg_view_window_update_model_replaced (ModestHeaderViewObserver *observer,
1279 GtkTreeModel *model,
1280 const gchar *tny_folder_id)
1282 ModestMsgViewWindowPrivate *priv = NULL;
1283 ModestMsgViewWindow *window = NULL;
1285 g_assert(MODEST_IS_HEADER_VIEW_OBSERVER(observer));
1286 g_assert(MODEST_IS_MSG_VIEW_WINDOW(observer));
1288 window = MODEST_MSG_VIEW_WINDOW(observer);
1289 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(window);
1291 /* If there is an other folder in the header-view then we do
1292 * not care about it's model (msg list). Else if the
1293 * header-view shows the folder the msg shown by us is in, we
1294 * shall replace our model reference and make some check. */
1295 if(model == NULL || tny_folder_id == NULL ||
1296 (priv->header_folder_id && !g_str_equal(tny_folder_id, priv->header_folder_id)))
1299 /* Model is changed(replaced), so we should forget the old
1300 * one. Because there might be other references and there
1301 * might be some change on the model even if we unreferenced
1302 * it, we need to disconnect our signals here. */
1303 if (priv->header_model) {
1304 if (g_signal_handler_is_connected(G_OBJECT (priv->header_model),
1305 priv->row_changed_handler))
1306 g_signal_handler_disconnect(G_OBJECT (priv->header_model),
1307 priv->row_changed_handler);
1308 if (g_signal_handler_is_connected(G_OBJECT (priv->header_model),
1309 priv->row_deleted_handler))
1310 g_signal_handler_disconnect(G_OBJECT (priv->header_model),
1311 priv->row_deleted_handler);
1312 if (g_signal_handler_is_connected(G_OBJECT (priv->header_model),
1313 priv->row_inserted_handler))
1314 g_signal_handler_disconnect(G_OBJECT (priv->header_model),
1315 priv->row_inserted_handler);
1316 if (g_signal_handler_is_connected(G_OBJECT (priv->header_model),
1317 priv->rows_reordered_handler))
1318 g_signal_handler_disconnect(G_OBJECT (priv->header_model),
1319 priv->rows_reordered_handler);
1322 if (priv->row_reference)
1323 gtk_tree_row_reference_free (priv->row_reference);
1324 if (priv->next_row_reference)
1325 gtk_tree_row_reference_free (priv->next_row_reference);
1326 g_object_unref(priv->header_model);
1329 priv->row_changed_handler = 0;
1330 priv->row_deleted_handler = 0;
1331 priv->row_inserted_handler = 0;
1332 priv->rows_reordered_handler = 0;
1333 priv->next_row_reference = NULL;
1334 priv->row_reference = NULL;
1335 priv->header_model = NULL;
1338 priv->header_model = g_object_ref (model);
1340 /* Also we must connect to the new model for row insertions.
1341 * Only for insertions now. We will need other ones only after
1342 * the msg is show by msg-view is added to the new model. */
1343 priv->row_inserted_handler =
1344 g_signal_connect (priv->header_model, "row-inserted",
1345 G_CALLBACK(modest_msg_view_window_on_row_inserted),
1348 modest_ui_actions_check_menu_dimming_rules(MODEST_WINDOW(window));
1349 modest_ui_actions_check_toolbar_dimming_rules(MODEST_WINDOW(window));
1353 modest_msg_view_window_toolbar_on_transfer_mode (ModestMsgViewWindow *self)
1355 ModestMsgViewWindowPrivate *priv= NULL;
1357 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self), FALSE);
1358 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
1360 return priv->current_toolbar_mode == TOOLBAR_MODE_TRANSFER;
1364 modest_msg_view_window_get_header (ModestMsgViewWindow *self)
1366 ModestMsgViewWindowPrivate *priv= NULL;
1368 TnyHeader *header = NULL;
1369 GtkTreePath *path = NULL;
1372 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self), NULL);
1373 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
1375 /* If the message was not obtained from a treemodel,
1376 * for instance if it was opened directly by the search UI:
1378 if (priv->header_model == NULL ||
1379 priv->row_reference == NULL ||
1380 !gtk_tree_row_reference_valid (priv->row_reference)) {
1381 msg = modest_msg_view_window_get_message (self);
1383 header = tny_msg_get_header (msg);
1384 g_object_unref (msg);
1389 /* Get iter of the currently selected message in the header view: */
1390 path = gtk_tree_row_reference_get_path (priv->row_reference);
1391 g_return_val_if_fail (path != NULL, NULL);
1392 gtk_tree_model_get_iter (priv->header_model,
1396 /* Get current message header */
1397 gtk_tree_model_get (priv->header_model, &iter,
1398 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
1401 gtk_tree_path_free (path);
1406 modest_msg_view_window_get_message (ModestMsgViewWindow *self)
1408 ModestMsgViewWindowPrivate *priv;
1410 g_return_val_if_fail (self, NULL);
1412 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(self);
1414 return tny_msg_view_get_msg (TNY_MSG_VIEW (priv->msg_view));
1418 modest_msg_view_window_get_message_uid (ModestMsgViewWindow *self)
1420 ModestMsgViewWindowPrivate *priv;
1422 g_return_val_if_fail (self, NULL);
1424 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
1426 return (const gchar*) priv->msg_uid;
1430 modest_msg_view_window_toggle_find_toolbar (GtkToggleAction *toggle,
1433 ModestMsgViewWindow *window = MODEST_MSG_VIEW_WINDOW (data);
1434 ModestMsgViewWindowPrivate *priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1435 ModestWindowPrivate *parent_priv = MODEST_WINDOW_GET_PRIVATE (window);
1439 is_active = gtk_toggle_action_get_active (toggle);
1442 gtk_widget_show (priv->find_toolbar);
1443 hildon_find_toolbar_highlight_entry (HILDON_FIND_TOOLBAR (priv->find_toolbar), TRUE);
1445 gtk_widget_hide (priv->find_toolbar);
1446 modest_msg_view_grab_focus (MODEST_MSG_VIEW (priv->msg_view));
1449 /* update the toggle buttons status */
1450 action = gtk_ui_manager_get_action (parent_priv->ui_manager, "/ToolBar/FindInMessage");
1451 modest_utils_toggle_action_set_active_block_notify (GTK_TOGGLE_ACTION (action), is_active);
1452 action = gtk_ui_manager_get_action (parent_priv->ui_manager, "/MenuBar/ToolsMenu/ToolsFindInMessageMenu");
1453 modest_utils_toggle_action_set_active_block_notify (GTK_TOGGLE_ACTION (action), is_active);
1458 modest_msg_view_window_find_toolbar_close (GtkWidget *widget,
1459 ModestMsgViewWindow *obj)
1461 GtkToggleAction *toggle;
1462 ModestWindowPrivate *parent_priv;
1463 ModestMsgViewWindowPrivate *priv;
1465 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (obj);
1466 parent_priv = MODEST_WINDOW_GET_PRIVATE (obj);
1468 toggle = GTK_TOGGLE_ACTION (gtk_ui_manager_get_action (parent_priv->ui_manager, "/ToolBar/FindInMessage"));
1469 gtk_toggle_action_set_active (toggle, FALSE);
1470 modest_msg_view_grab_focus (MODEST_MSG_VIEW (priv->msg_view));
1474 modest_msg_view_window_find_toolbar_search (GtkWidget *widget,
1475 ModestMsgViewWindow *obj)
1477 gchar *current_search;
1478 ModestMsgViewWindowPrivate *priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (obj);
1480 if (modest_mime_part_view_is_empty (MODEST_MIME_PART_VIEW (priv->msg_view))) {
1481 hildon_banner_show_information (NULL, NULL, _("mail_ib_nothing_to_find"));
1485 g_object_get (G_OBJECT (widget), "prefix", ¤t_search, NULL);
1487 if ((current_search == NULL) || (strcmp (current_search, "") == 0)) {
1488 g_free (current_search);
1489 hildon_banner_show_information (NULL, NULL, _CS("ecdg_ib_find_rep_enter_text"));
1493 if ((priv->last_search == NULL) || (strcmp (priv->last_search, current_search) != 0)) {
1495 g_free (priv->last_search);
1496 priv->last_search = g_strdup (current_search);
1497 result = modest_isearch_view_search (MODEST_ISEARCH_VIEW (priv->msg_view),
1500 hildon_banner_show_information (NULL, NULL, _HL("ckct_ib_find_no_matches"));
1501 g_free (priv->last_search);
1502 priv->last_search = NULL;
1504 modest_msg_view_grab_focus (MODEST_MSG_VIEW (priv->msg_view));
1505 hildon_find_toolbar_highlight_entry (HILDON_FIND_TOOLBAR (priv->find_toolbar), TRUE);
1508 if (!modest_isearch_view_search_next (MODEST_ISEARCH_VIEW (priv->msg_view))) {
1509 hildon_banner_show_information (NULL, NULL, _HL("ckct_ib_find_search_complete"));
1510 g_free (priv->last_search);
1511 priv->last_search = NULL;
1513 modest_msg_view_grab_focus (MODEST_MSG_VIEW (priv->msg_view));
1514 hildon_find_toolbar_highlight_entry (HILDON_FIND_TOOLBAR (priv->find_toolbar), TRUE);
1518 g_free (current_search);
1523 modest_msg_view_window_set_zoom (ModestWindow *window,
1526 ModestMsgViewWindowPrivate *priv;
1527 ModestWindowPrivate *parent_priv;
1528 GtkAction *action = NULL;
1529 gint int_zoom = (gint) rint (zoom*100.0+0.1);
1531 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window));
1533 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1534 parent_priv = MODEST_WINDOW_GET_PRIVATE (window);
1535 modest_zoomable_set_zoom (MODEST_ZOOMABLE (priv->msg_view), zoom);
1537 action = gtk_ui_manager_get_action (parent_priv->ui_manager,
1538 "/MenuBar/ViewMenu/ZoomMenu/Zoom50Menu");
1540 gtk_radio_action_set_current_value (GTK_RADIO_ACTION (action), int_zoom);
1544 modest_msg_view_window_get_zoom (ModestWindow *window)
1546 ModestMsgViewWindowPrivate *priv;
1548 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), 1.0);
1550 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1551 return modest_zoomable_get_zoom (MODEST_ZOOMABLE (priv->msg_view));
1555 modest_msg_view_window_zoom_plus (ModestWindow *window)
1557 ModestWindowPrivate *parent_priv;
1558 GtkRadioAction *zoom_radio_action;
1559 GSList *group, *node;
1561 parent_priv = MODEST_WINDOW_GET_PRIVATE (window);
1562 zoom_radio_action = GTK_RADIO_ACTION (gtk_ui_manager_get_action (parent_priv->ui_manager,
1563 "/MenuBar/ViewMenu/ZoomMenu/Zoom50Menu"));
1565 group = gtk_radio_action_get_group (zoom_radio_action);
1567 if (gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (group->data))) {
1568 hildon_banner_show_information (NULL, NULL, _CS("ckct_ib_max_zoom_level_reached"));
1572 for (node = group; node != NULL; node = g_slist_next (node)) {
1573 if ((node->next != NULL) && gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (node->next->data))) {
1574 gtk_toggle_action_set_active (GTK_TOGGLE_ACTION (node->data), TRUE);
1582 modest_msg_view_window_zoom_minus (ModestWindow *window)
1584 ModestWindowPrivate *parent_priv;
1585 GtkRadioAction *zoom_radio_action;
1586 GSList *group, *node;
1588 parent_priv = MODEST_WINDOW_GET_PRIVATE (window);
1589 zoom_radio_action = GTK_RADIO_ACTION (gtk_ui_manager_get_action (parent_priv->ui_manager,
1590 "/MenuBar/ViewMenu/ZoomMenu/Zoom50Menu"));
1592 group = gtk_radio_action_get_group (zoom_radio_action);
1594 for (node = group; node != NULL; node = g_slist_next (node)) {
1595 if (gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (node->data))) {
1596 if (node->next != NULL) {
1597 gtk_toggle_action_set_active (GTK_TOGGLE_ACTION (node->next->data), TRUE);
1600 hildon_banner_show_information (NULL, NULL,
1601 _CS("ckct_ib_min_zoom_level_reached"));
1611 modest_msg_view_window_key_event (GtkWidget *window,
1617 focus = gtk_window_get_focus (GTK_WINDOW (window));
1619 /* for the find toolbar case */
1620 if (focus && GTK_IS_ENTRY (focus)) {
1621 if (event->keyval == GDK_BackSpace) {
1623 copy = gdk_event_copy ((GdkEvent *) event);
1624 gtk_widget_event (focus, copy);
1625 gdk_event_free (copy);
1630 if (event->keyval == GDK_Up || event->keyval == GDK_KP_Up ||
1631 event->keyval == GDK_Down || event->keyval == GDK_KP_Down ||
1632 event->keyval == GDK_Page_Up || event->keyval == GDK_KP_Page_Up ||
1633 event->keyval == GDK_Page_Down || event->keyval == GDK_KP_Page_Down ||
1634 event->keyval == GDK_Home || event->keyval == GDK_KP_Home ||
1635 event->keyval == GDK_End || event->keyval == GDK_KP_End) {
1636 /* ModestMsgViewWindowPrivate *priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window); */
1637 /* gboolean return_value; */
1639 if (event->type == GDK_KEY_PRESS) {
1640 GtkScrollType scroll_type;
1642 switch (event->keyval) {
1645 scroll_type = GTK_SCROLL_STEP_UP; break;
1648 scroll_type = GTK_SCROLL_STEP_DOWN; break;
1650 case GDK_KP_Page_Up:
1651 scroll_type = GTK_SCROLL_PAGE_UP; break;
1653 case GDK_KP_Page_Down:
1654 scroll_type = GTK_SCROLL_PAGE_DOWN; break;
1657 scroll_type = GTK_SCROLL_START; break;
1660 scroll_type = GTK_SCROLL_END; break;
1661 default: scroll_type = GTK_SCROLL_NONE;
1664 /* g_signal_emit_by_name (G_OBJECT (priv->main_scroll), "scroll-child", */
1665 /* scroll_type, FALSE, &return_value); */
1676 modest_msg_view_window_last_message_selected (ModestMsgViewWindow *window)
1679 ModestMsgViewWindowPrivate *priv;
1680 GtkTreeIter tmp_iter;
1681 gboolean is_last_selected;
1683 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), TRUE);
1684 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1686 /*if no model (so no rows at all), then virtually we are the last*/
1687 if (!priv->header_model || !priv->row_reference)
1690 if (!gtk_tree_row_reference_valid (priv->row_reference))
1693 path = gtk_tree_row_reference_get_path (priv->row_reference);
1697 is_last_selected = TRUE;
1698 while (is_last_selected) {
1700 gtk_tree_path_next (path);
1701 if (!gtk_tree_model_get_iter (priv->header_model, &tmp_iter, path))
1703 gtk_tree_model_get (priv->header_model, &tmp_iter,
1704 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
1707 if (msg_is_visible (header, priv->is_outbox))
1708 is_last_selected = FALSE;
1709 g_object_unref(G_OBJECT(header));
1712 gtk_tree_path_free (path);
1713 return is_last_selected;
1717 modest_msg_view_window_has_headers_model (ModestMsgViewWindow *window)
1719 ModestMsgViewWindowPrivate *priv;
1721 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), TRUE);
1722 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1724 return priv->header_model != NULL;
1728 modest_msg_view_window_is_search_result (ModestMsgViewWindow *window)
1730 ModestMsgViewWindowPrivate *priv;
1732 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), TRUE);
1733 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1735 return priv->is_search_result;
1739 msg_is_visible (TnyHeader *header, gboolean check_outbox)
1741 if ((tny_header_get_flags(header) & TNY_HEADER_FLAG_DELETED))
1743 if (!check_outbox) {
1746 ModestTnySendQueueStatus status;
1747 status = modest_tny_all_send_queues_get_msg_status (header);
1748 return ((status != MODEST_TNY_SEND_QUEUE_FAILED) &&
1749 (status != MODEST_TNY_SEND_QUEUE_SENDING));
1754 modest_msg_view_window_first_message_selected (ModestMsgViewWindow *window)
1757 ModestMsgViewWindowPrivate *priv;
1758 gboolean is_first_selected;
1759 GtkTreeIter tmp_iter;
1761 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), TRUE);
1762 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1764 /*if no model (so no rows at all), then virtually we are the first*/
1765 if (!priv->header_model || !priv->row_reference)
1768 if (!gtk_tree_row_reference_valid (priv->row_reference))
1771 path = gtk_tree_row_reference_get_path (priv->row_reference);
1775 is_first_selected = TRUE;
1776 while (is_first_selected) {
1778 if(!gtk_tree_path_prev (path))
1780 /* Here the 'if' is needless for logic, but let make sure
1781 * iter is valid for gtk_tree_model_get. */
1782 if (!gtk_tree_model_get_iter (priv->header_model, &tmp_iter, path))
1784 gtk_tree_model_get (priv->header_model, &tmp_iter,
1785 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
1788 if (msg_is_visible (header, priv->is_outbox))
1789 is_first_selected = FALSE;
1790 g_object_unref(G_OBJECT(header));
1793 gtk_tree_path_free (path);
1794 return is_first_selected;
1799 GtkTreeRowReference *row_reference;
1803 message_reader_performer (gboolean canceled,
1805 GtkWindow *parent_window,
1806 TnyAccount *account,
1809 ModestMailOperation *mail_op = NULL;
1810 MsgReaderInfo *info;
1812 info = (MsgReaderInfo *) user_data;
1813 if (canceled || err) {
1817 /* Register the header - it'll be unregistered in the callback */
1818 modest_window_mgr_register_header (modest_runtime_get_window_mgr (), info->header, NULL);
1820 /* New mail operation */
1821 mail_op = modest_mail_operation_new_with_error_handling (G_OBJECT(parent_window),
1822 modest_ui_actions_disk_operations_error_handler,
1825 modest_mail_operation_queue_add (modest_runtime_get_mail_operation_queue (), mail_op);
1826 modest_mail_operation_get_msg (mail_op, info->header, TRUE, view_msg_cb, info->row_reference);
1827 g_object_unref (mail_op);
1829 /* Update dimming rules */
1830 modest_ui_actions_check_toolbar_dimming_rules (MODEST_WINDOW (parent_window));
1831 modest_ui_actions_check_menu_dimming_rules (MODEST_WINDOW (parent_window));
1834 /* Frees. The row_reference will be freed by the view_msg_cb callback */
1835 g_object_unref (info->header);
1836 g_slice_free (MsgReaderInfo, info);
1841 * Reads the message whose summary item is @header. It takes care of
1842 * several things, among others:
1844 * If the message was not previously downloaded then ask the user
1845 * before downloading. If there is no connection launch the connection
1846 * dialog. Update toolbar dimming rules.
1848 * Returns: TRUE if the mail operation was started, otherwise if the
1849 * user do not want to download the message, or if the user do not
1850 * want to connect, then the operation is not issued
1853 message_reader (ModestMsgViewWindow *window,
1854 ModestMsgViewWindowPrivate *priv,
1856 GtkTreeRowReference *row_reference)
1858 gboolean already_showing = FALSE;
1859 ModestWindow *msg_window = NULL;
1860 ModestWindowMgr *mgr;
1861 TnyAccount *account;
1863 MsgReaderInfo *info;
1865 g_return_val_if_fail (row_reference != NULL, FALSE);
1867 mgr = modest_runtime_get_window_mgr ();
1868 already_showing = modest_window_mgr_find_registered_header (mgr, header, &msg_window);
1869 if (already_showing && (msg_window != MODEST_WINDOW (window))) {
1872 gtk_window_present (GTK_WINDOW (msg_window));
1873 g_signal_emit_by_name (G_OBJECT (window), "delete-event", NULL, &retval);
1877 /* Msg download completed */
1878 if (!(tny_header_get_flags (header) & TNY_HEADER_FLAG_CACHED)) {
1879 /* Ask the user if he wants to download the message if
1881 if (!tny_device_is_online (modest_runtime_get_device())) {
1882 GtkResponseType response;
1884 response = modest_platform_run_confirmation_dialog (GTK_WINDOW (window),
1885 _("mcen_nc_get_msg"));
1886 if (response == GTK_RESPONSE_CANCEL)
1889 folder = tny_header_get_folder (header);
1890 info = g_slice_new (MsgReaderInfo);
1891 info->header = g_object_ref (header);
1892 info->row_reference = gtk_tree_row_reference_copy (row_reference);
1894 /* Offer the connection dialog if necessary */
1895 modest_platform_connect_if_remote_and_perform ((GtkWindow *) window,
1897 TNY_FOLDER_STORE (folder),
1898 message_reader_performer,
1900 g_object_unref (folder);
1905 folder = tny_header_get_folder (header);
1906 account = tny_folder_get_account (folder);
1907 info = g_slice_new (MsgReaderInfo);
1908 info->header = g_object_ref (header);
1909 info->row_reference = gtk_tree_row_reference_copy (row_reference);
1911 message_reader_performer (FALSE, NULL, (GtkWindow *) window, account, info);
1912 g_object_unref (account);
1913 g_object_unref (folder);
1919 modest_msg_view_window_select_next_message (ModestMsgViewWindow *window)
1921 ModestMsgViewWindowPrivate *priv;
1922 GtkTreePath *path= NULL;
1923 GtkTreeIter tmp_iter;
1925 gboolean retval = TRUE;
1926 GtkTreeRowReference *row_reference = NULL;
1928 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), FALSE);
1929 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1931 if (!priv->row_reference)
1934 /* Update the next row reference if it's not valid. This could
1935 happen if for example the header which it was pointing to,
1936 was deleted. The best place to do it is in the row-deleted
1937 handler but the tinymail model do not work like the glib
1938 tree models and reports the deletion when the row is still
1940 if (!gtk_tree_row_reference_valid (priv->next_row_reference)) {
1941 if (gtk_tree_row_reference_valid (priv->row_reference)) {
1942 priv->next_row_reference = gtk_tree_row_reference_copy (priv->row_reference);
1943 select_next_valid_row (priv->header_model, &(priv->next_row_reference), FALSE, priv->is_outbox);
1946 if (priv->next_row_reference)
1947 path = gtk_tree_row_reference_get_path (priv->next_row_reference);
1951 row_reference = gtk_tree_row_reference_copy (priv->next_row_reference);
1953 gtk_tree_model_get_iter (priv->header_model,
1956 gtk_tree_path_free (path);
1958 gtk_tree_model_get (priv->header_model, &tmp_iter,
1959 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
1962 /* Read the message & show it */
1963 if (!message_reader (window, priv, header, row_reference)) {
1966 gtk_tree_row_reference_free (row_reference);
1969 g_object_unref (header);
1975 modest_msg_view_window_select_previous_message (ModestMsgViewWindow *window)
1977 ModestMsgViewWindowPrivate *priv = NULL;
1979 gboolean finished = FALSE;
1980 gboolean retval = FALSE;
1982 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), FALSE);
1983 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1985 /* Return inmediatly if there is no header model */
1986 if (!priv->header_model || !priv->row_reference)
1989 path = gtk_tree_row_reference_get_path (priv->row_reference);
1990 while (!finished && gtk_tree_path_prev (path)) {
1994 gtk_tree_model_get_iter (priv->header_model, &iter, path);
1995 gtk_tree_model_get (priv->header_model, &iter,
1996 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
2000 if (msg_is_visible (header, priv->is_outbox)) {
2001 GtkTreeRowReference *row_reference;
2002 row_reference = gtk_tree_row_reference_new (priv->header_model, path);
2003 /* Read the message & show it */
2004 retval = message_reader (window, priv, header, row_reference);
2005 gtk_tree_row_reference_free (row_reference);
2009 g_object_unref (header);
2013 gtk_tree_path_free (path);
2018 view_msg_cb (ModestMailOperation *mail_op,
2025 ModestMsgViewWindow *self = NULL;
2026 ModestMsgViewWindowPrivate *priv = NULL;
2027 GtkTreeRowReference *row_reference = NULL;
2029 /* Unregister the header (it was registered before creating the mail operation) */
2030 modest_window_mgr_unregister_header (modest_runtime_get_window_mgr (), header);
2032 row_reference = (GtkTreeRowReference *) user_data;
2034 gtk_tree_row_reference_free (row_reference);
2038 /* If there was any error */
2039 if (!modest_ui_actions_msg_retrieval_check (mail_op, header, msg)) {
2040 gtk_tree_row_reference_free (row_reference);
2044 /* Get the window */
2045 self = (ModestMsgViewWindow *) modest_mail_operation_get_source (mail_op);
2046 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self));
2047 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
2049 /* Update the row reference */
2050 if (priv->row_reference != NULL) {
2051 gtk_tree_row_reference_free (priv->row_reference);
2052 priv->row_reference = gtk_tree_row_reference_copy (row_reference);
2053 if (priv->next_row_reference != NULL) {
2054 gtk_tree_row_reference_free (priv->next_row_reference);
2056 priv->next_row_reference = gtk_tree_row_reference_copy (priv->row_reference);
2057 select_next_valid_row (priv->header_model, &(priv->next_row_reference), TRUE, priv->is_outbox);
2060 /* Mark header as read */
2061 if (!(tny_header_get_flags (header) & TNY_HEADER_FLAG_SEEN))
2062 tny_header_set_flag (header, TNY_HEADER_FLAG_SEEN);
2064 /* Set new message */
2065 if (priv->msg_view != NULL && TNY_IS_MSG_VIEW (priv->msg_view)) {
2066 tny_msg_view_set_msg (TNY_MSG_VIEW (priv->msg_view), msg);
2067 modest_msg_view_window_update_priority (self);
2068 update_window_title (MODEST_MSG_VIEW_WINDOW (self));
2069 modest_msg_view_grab_focus (MODEST_MSG_VIEW (priv->msg_view));
2072 /* Set the new message uid of the window */
2073 if (priv->msg_uid) {
2074 g_free (priv->msg_uid);
2075 priv->msg_uid = modest_tny_folder_get_header_unique_id (header);
2078 /* Notify the observers */
2079 g_signal_emit (G_OBJECT (self), signals[MSG_CHANGED_SIGNAL],
2080 0, priv->header_model, priv->row_reference);
2083 g_object_unref (self);
2084 gtk_tree_row_reference_free (row_reference);
2088 modest_msg_view_window_get_folder_type (ModestMsgViewWindow *window)
2090 ModestMsgViewWindowPrivate *priv;
2092 TnyFolderType folder_type;
2094 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
2096 folder_type = TNY_FOLDER_TYPE_UNKNOWN;
2098 msg = tny_msg_view_get_msg (TNY_MSG_VIEW (priv->msg_view));
2102 folder = tny_msg_get_folder (msg);
2104 folder_type = modest_tny_folder_guess_folder_type (folder);
2105 g_object_unref (folder);
2107 g_object_unref (msg);
2115 modest_msg_view_window_update_priority (ModestMsgViewWindow *window)
2117 ModestMsgViewWindowPrivate *priv;
2118 TnyHeader *header = NULL;
2119 TnyHeaderFlags flags = 0;
2121 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
2123 if (priv->header_model && priv->row_reference) {
2125 GtkTreePath *path = NULL;
2127 path = gtk_tree_row_reference_get_path (priv->row_reference);
2128 g_return_if_fail (path != NULL);
2129 gtk_tree_model_get_iter (priv->header_model,
2131 gtk_tree_row_reference_get_path (priv->row_reference));
2133 gtk_tree_model_get (priv->header_model, &iter, TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
2135 gtk_tree_path_free (path);
2138 msg = tny_msg_view_get_msg (TNY_MSG_VIEW (priv->msg_view));
2140 header = tny_msg_get_header (msg);
2141 g_object_unref (msg);
2146 flags = tny_header_get_flags (header);
2147 g_object_unref(G_OBJECT(header));
2150 modest_msg_view_set_priority (MODEST_MSG_VIEW(priv->msg_view), flags);
2155 toolbar_resize (ModestMsgViewWindow *self)
2157 ModestMsgViewWindowPrivate *priv = NULL;
2158 ModestWindowPrivate *parent_priv = NULL;
2160 gint static_button_size;
2161 ModestWindowMgr *mgr;
2163 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self));
2164 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
2165 parent_priv = MODEST_WINDOW_GET_PRIVATE(self);
2167 mgr = modest_runtime_get_window_mgr ();
2168 static_button_size = modest_window_mgr_get_fullscreen_mode (mgr)?118:108;
2170 if (parent_priv->toolbar) {
2171 /* left size buttons */
2172 widget = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/ToolbarMessageReply");
2173 gtk_tool_item_set_expand (GTK_TOOL_ITEM (widget), FALSE);
2174 gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (widget), FALSE);
2175 gtk_widget_set_size_request (GTK_WIDGET (widget), static_button_size, -1);
2176 widget = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/ToolbarMessageMoveTo");
2177 gtk_tool_item_set_expand (GTK_TOOL_ITEM (widget), FALSE);
2178 gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (widget), FALSE);
2179 gtk_widget_set_size_request (GTK_WIDGET (widget), static_button_size, -1);
2180 widget = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/ToolbarDeleteMessage");
2181 gtk_tool_item_set_expand (GTK_TOOL_ITEM (widget), FALSE);
2182 gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (widget), FALSE);
2183 gtk_widget_set_size_request (GTK_WIDGET (widget), static_button_size, -1);
2184 widget = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/FindInMessage");
2185 gtk_tool_item_set_expand (GTK_TOOL_ITEM (widget), FALSE);
2186 gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (widget), FALSE);
2187 gtk_widget_set_size_request (GTK_WIDGET (widget), static_button_size, -1);
2189 gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (priv->progress_toolitem), FALSE);
2190 gtk_tool_item_set_expand (GTK_TOOL_ITEM (priv->progress_toolitem), TRUE);
2191 gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (priv->cancel_toolitem), FALSE);
2192 gtk_tool_item_set_expand (GTK_TOOL_ITEM (priv->cancel_toolitem), FALSE);
2193 gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (priv->next_toolitem), TRUE);
2194 gtk_tool_item_set_expand (GTK_TOOL_ITEM (priv->next_toolitem), TRUE);
2195 gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (priv->prev_toolitem), TRUE);
2196 gtk_tool_item_set_expand (GTK_TOOL_ITEM (priv->prev_toolitem), TRUE);
2202 modest_msg_view_window_window_state_event (GtkWidget *widget, GdkEventWindowState *event, gpointer userdata)
2204 if (event->changed_mask & GDK_WINDOW_STATE_FULLSCREEN) {
2205 ModestWindowPrivate *parent_priv;
2206 ModestWindowMgr *mgr;
2207 gboolean is_fullscreen;
2208 GtkAction *fs_toggle_action;
2211 mgr = modest_runtime_get_window_mgr ();
2212 is_fullscreen = (modest_window_mgr_get_fullscreen_mode (mgr))?1:0;
2214 parent_priv = MODEST_WINDOW_GET_PRIVATE (widget);
2216 fs_toggle_action = gtk_ui_manager_get_action (parent_priv->ui_manager, "/MenuBar/ViewMenu/ViewToggleFullscreenMenu");
2217 active = (gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (fs_toggle_action)))?1:0;
2218 if (is_fullscreen != active) {
2219 gtk_toggle_action_set_active (GTK_TOGGLE_ACTION (fs_toggle_action), is_fullscreen);
2221 toolbar_resize (MODEST_MSG_VIEW_WINDOW (widget));
2229 modest_msg_view_window_show_toolbar (ModestWindow *self,
2230 gboolean show_toolbar)
2232 ModestMsgViewWindowPrivate *priv = NULL;
2233 ModestWindowPrivate *parent_priv;
2234 GtkWidget *reply_button = NULL, *menu = NULL;
2235 GtkWidget *placeholder = NULL;
2237 const gchar *action_name;
2240 parent_priv = MODEST_WINDOW_GET_PRIVATE(self);
2241 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(self);
2243 /* Set optimized view status */
2244 priv->optimized_view = !show_toolbar;
2246 if (!parent_priv->toolbar) {
2247 parent_priv->toolbar = gtk_ui_manager_get_widget (parent_priv->ui_manager,
2249 gtk_widget_set_no_show_all (parent_priv->toolbar, TRUE);
2251 priv->progress_toolitem = GTK_WIDGET (gtk_tool_item_new ());
2252 priv->cancel_toolitem = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/ToolbarCancel");
2253 priv->next_toolitem = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/ToolbarMessageNext");
2254 priv->prev_toolitem = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/ToolbarMessageBack");
2255 toolbar_resize (MODEST_MSG_VIEW_WINDOW (self));
2257 /* Add ProgressBar (Transfer toolbar) */
2258 priv->progress_bar = modest_progress_bar_new ();
2259 gtk_widget_set_no_show_all (priv->progress_bar, TRUE);
2260 placeholder = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/ProgressbarView");
2261 insert_index = gtk_toolbar_get_item_index(GTK_TOOLBAR (parent_priv->toolbar), GTK_TOOL_ITEM(placeholder));
2262 gtk_container_add (GTK_CONTAINER (priv->progress_toolitem), priv->progress_bar);
2263 gtk_toolbar_insert(GTK_TOOLBAR(parent_priv->toolbar), GTK_TOOL_ITEM (priv->progress_toolitem), insert_index);
2265 /* Connect cancel 'clicked' signal to abort progress mode */
2266 g_signal_connect(priv->cancel_toolitem, "clicked",
2267 G_CALLBACK(cancel_progressbar),
2270 /* Add it to the observers list */
2271 priv->progress_widgets = g_slist_prepend(priv->progress_widgets, priv->progress_bar);
2274 hildon_window_add_toolbar (HILDON_WINDOW (self),
2275 GTK_TOOLBAR (parent_priv->toolbar));
2277 /* Set reply button tap and hold menu */
2278 reply_button = gtk_ui_manager_get_widget (parent_priv->ui_manager,
2279 "/ToolBar/ToolbarMessageReply");
2280 menu = gtk_ui_manager_get_widget (parent_priv->ui_manager,
2281 "/ToolbarReplyCSM");
2282 gtk_widget_tap_and_hold_setup (GTK_WIDGET (reply_button), menu, NULL, 0);
2286 /* Quick hack: this prevents toolbar icons "dance" when progress bar show status is changed */
2287 /* TODO: resize mode migth be GTK_RESIZE_QUEUE, in order to avoid unneccesary shows */
2288 gtk_container_set_resize_mode (GTK_CONTAINER(parent_priv->toolbar), GTK_RESIZE_IMMEDIATE);
2290 gtk_widget_show (GTK_WIDGET (parent_priv->toolbar));
2291 if (modest_msg_view_window_transfer_mode_enabled (MODEST_MSG_VIEW_WINDOW (self)))
2292 set_toolbar_mode (MODEST_MSG_VIEW_WINDOW (self), TOOLBAR_MODE_TRANSFER);
2294 set_toolbar_mode (MODEST_MSG_VIEW_WINDOW (self), TOOLBAR_MODE_NORMAL);
2297 gtk_widget_set_no_show_all (parent_priv->toolbar, TRUE);
2298 gtk_widget_hide (GTK_WIDGET (parent_priv->toolbar));
2301 /* Update also the actions (to update the toggles in the
2302 menus), we have to do it manually because some other window
2303 of the same time could have changed it (remember that the
2304 toolbar fullscreen mode is shared by all the windows of the
2306 if (modest_window_mgr_get_fullscreen_mode (modest_runtime_get_window_mgr ()))
2307 action_name = "/MenuBar/ViewMenu/ViewShowToolbarMenu/ViewShowToolbarFullScreenMenu";
2309 action_name = "/MenuBar/ViewMenu/ViewShowToolbarMenu/ViewShowToolbarNormalScreenMenu";
2311 action = gtk_ui_manager_get_action (parent_priv->ui_manager, action_name);
2312 modest_utils_toggle_action_set_active_block_notify (GTK_TOGGLE_ACTION (action),
2317 modest_msg_view_window_clipboard_owner_change (GtkClipboard *clipboard,
2319 ModestMsgViewWindow *window)
2321 if (!GTK_WIDGET_VISIBLE (window))
2324 modest_window_check_dimming_rules_group (MODEST_WINDOW (window), MODEST_DIMMING_RULES_CLIPBOARD);
2328 modest_msg_view_window_transfer_mode_enabled (ModestMsgViewWindow *self)
2330 ModestMsgViewWindowPrivate *priv;
2332 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self), FALSE);
2333 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(self);
2335 return priv->current_toolbar_mode == TOOLBAR_MODE_TRANSFER;
2339 cancel_progressbar (GtkToolButton *toolbutton,
2340 ModestMsgViewWindow *self)
2343 ModestMsgViewWindowPrivate *priv;
2345 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(self);
2347 /* Get operation observers and cancel its current operation */
2348 tmp = priv->progress_widgets;
2350 modest_progress_object_cancel_current_operation (MODEST_PROGRESS_OBJECT(tmp->data));
2351 tmp=g_slist_next(tmp);
2355 observers_empty (ModestMsgViewWindow *self)
2358 ModestMsgViewWindowPrivate *priv;
2359 gboolean is_empty = TRUE;
2360 guint pending_ops = 0;
2362 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(self);
2363 tmp = priv->progress_widgets;
2365 /* Check all observers */
2366 while (tmp && is_empty) {
2367 pending_ops = modest_progress_object_num_pending_operations (MODEST_PROGRESS_OBJECT(tmp->data));
2368 is_empty = pending_ops == 0;
2370 tmp = g_slist_next(tmp);
2377 on_account_removed (TnyAccountStore *account_store,
2378 TnyAccount *account,
2381 /* Do nothing if it's a transport account, because we only
2382 show the messages of a store account */
2383 if (tny_account_get_account_type(account) == TNY_ACCOUNT_TYPE_STORE) {
2384 const gchar *parent_acc = NULL;
2385 const gchar *our_acc = NULL;
2387 our_acc = modest_window_get_active_account (MODEST_WINDOW (user_data));
2388 parent_acc = modest_tny_account_get_parent_modest_account_name_for_server_account (account);
2390 /* Close this window if I'm showing a message of the removed account */
2391 if (strcmp (parent_acc, our_acc) == 0)
2392 modest_ui_actions_on_close_window (NULL, MODEST_WINDOW (user_data));
2397 on_mail_operation_started (ModestMailOperation *mail_op,
2400 ModestMsgViewWindow *self;
2401 ModestMailOperationTypeOperation op_type;
2403 ModestMsgViewWindowPrivate *priv;
2404 GObject *source = NULL;
2406 self = MODEST_MSG_VIEW_WINDOW (user_data);
2407 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
2408 op_type = modest_mail_operation_get_type_operation (mail_op);
2409 tmp = priv->progress_widgets;
2410 source = modest_mail_operation_get_source(mail_op);
2411 if (G_OBJECT (self) == source) {
2412 if (op_type == MODEST_MAIL_OPERATION_TYPE_RECEIVE ) {
2413 set_toolbar_transfer_mode(self);
2415 modest_progress_object_add_operation (
2416 MODEST_PROGRESS_OBJECT (tmp->data),
2418 tmp = g_slist_next (tmp);
2422 g_object_unref (source);
2426 on_mail_operation_finished (ModestMailOperation *mail_op,
2429 ModestMsgViewWindow *self;
2430 ModestMailOperationTypeOperation op_type;
2432 ModestMsgViewWindowPrivate *priv;
2434 self = MODEST_MSG_VIEW_WINDOW (user_data);
2435 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
2436 op_type = modest_mail_operation_get_type_operation (mail_op);
2437 tmp = priv->progress_widgets;
2439 if (op_type == MODEST_MAIL_OPERATION_TYPE_RECEIVE ) {
2441 modest_progress_object_remove_operation (MODEST_PROGRESS_OBJECT (tmp->data),
2443 tmp = g_slist_next (tmp);
2446 /* If no more operations are being observed, NORMAL mode is enabled again */
2447 if (observers_empty (self)) {
2448 set_toolbar_mode (self, TOOLBAR_MODE_NORMAL);
2451 /* Update dimming rules. We have to do this right here
2452 and not in view_msg_cb because at that point the
2453 transfer mode is still enabled so the dimming rule
2454 won't let the user delete the message that has been
2455 readed for example */
2456 modest_ui_actions_check_toolbar_dimming_rules (MODEST_WINDOW (self));
2457 modest_ui_actions_check_menu_dimming_rules (MODEST_WINDOW (self));
2462 on_queue_changed (ModestMailOperationQueue *queue,
2463 ModestMailOperation *mail_op,
2464 ModestMailOperationQueueNotification type,
2465 ModestMsgViewWindow *self)
2467 ModestMsgViewWindowPrivate *priv;
2469 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
2471 /* If this operations was created by another window, do nothing */
2472 if (!modest_mail_operation_is_mine (mail_op, G_OBJECT(self)))
2475 if (type == MODEST_MAIL_OPERATION_QUEUE_OPERATION_ADDED) {
2476 priv->sighandlers = modest_signal_mgr_connect (priv->sighandlers,
2478 "operation-started",
2479 G_CALLBACK (on_mail_operation_started),
2481 priv->sighandlers = modest_signal_mgr_connect (priv->sighandlers,
2483 "operation-finished",
2484 G_CALLBACK (on_mail_operation_finished),
2486 } else if (type == MODEST_MAIL_OPERATION_QUEUE_OPERATION_REMOVED) {
2487 priv->sighandlers = modest_signal_mgr_disconnect (priv->sighandlers,
2489 "operation-started");
2490 priv->sighandlers = modest_signal_mgr_disconnect (priv->sighandlers,
2492 "operation-finished");
2497 modest_msg_view_window_get_attachments (ModestMsgViewWindow *win)
2499 ModestMsgViewWindowPrivate *priv;
2500 TnyList *selected_attachments = NULL;
2502 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (win), NULL);
2503 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (win);
2505 selected_attachments = modest_msg_view_get_selected_attachments (MODEST_MSG_VIEW (priv->msg_view));
2507 return selected_attachments;
2513 guint banner_idle_id;
2514 } DecodeAsyncHelper;
2517 decode_async_banner_idle (gpointer user_data)
2519 DecodeAsyncHelper *helper = (DecodeAsyncHelper *) user_data;
2521 helper->banner_idle_id = 0;
2522 helper->banner = hildon_banner_show_animation (NULL, NULL, _("mail_me_opening"));
2523 g_object_ref (helper->banner);
2529 on_decode_to_stream_async_handler (TnyMimePart *mime_part,
2535 DecodeAsyncHelper *helper = (DecodeAsyncHelper *) user_data;
2537 if (helper->banner_idle_id > 0) {
2538 g_source_remove (helper->banner_idle_id);
2539 helper->banner_idle_id = 0;
2541 if (helper->banner) {
2542 gtk_widget_destroy (helper->banner);
2544 if (cancelled || err) {
2545 modest_platform_information_banner (NULL, NULL,
2546 _("mail_ib_file_operation_failed"));
2550 /* make the file read-only */
2551 g_chmod(helper->filepath, 0444);
2553 /* Activate the file */
2554 modest_platform_activate_file (helper->filepath, modest_tny_mime_part_get_content_type (mime_part));
2558 g_free (helper->filepath);
2559 g_object_unref (helper->banner);
2560 g_slice_free (DecodeAsyncHelper, helper);
2564 modest_msg_view_window_view_attachment (ModestMsgViewWindow *window,
2565 TnyMimePart *mime_part)
2567 ModestMsgViewWindowPrivate *priv;
2568 const gchar *msg_uid;
2569 gchar *attachment_uid = NULL;
2570 gint attachment_index = 0;
2571 TnyList *attachments;
2572 TnyMimePart *window_msg;
2574 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window));
2575 g_return_if_fail (TNY_IS_MIME_PART (mime_part) || (mime_part == NULL));
2576 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
2578 msg_uid = modest_msg_view_window_get_message_uid (MODEST_MSG_VIEW_WINDOW (window));
2579 attachments = modest_msg_view_get_attachments (MODEST_MSG_VIEW (priv->msg_view));
2580 attachment_index = modest_list_index (attachments, (GObject *) mime_part);
2581 g_object_unref (attachments);
2583 if (msg_uid && attachment_index >= 0) {
2584 attachment_uid = g_strdup_printf ("%s/%d", msg_uid, attachment_index);
2587 if (mime_part == NULL) {
2588 gboolean error = FALSE;
2589 TnyList *selected_attachments = modest_msg_view_get_selected_attachments (MODEST_MSG_VIEW (priv->msg_view));
2590 if (selected_attachments == NULL || tny_list_get_length (selected_attachments) == 0) {
2592 } else if (tny_list_get_length (selected_attachments) > 1) {
2593 hildon_banner_show_information (NULL, NULL, _("mcen_ib_unable_to_display_more"));
2597 iter = tny_list_create_iterator (selected_attachments);
2598 mime_part = (TnyMimePart *) tny_iterator_get_current (iter);
2599 g_object_unref (iter);
2601 g_object_unref (selected_attachments);
2606 g_object_ref (mime_part);
2609 if (tny_mime_part_is_purged (mime_part)) {
2610 g_object_unref (mime_part);
2614 /* we also check for mime_part == priv->msg, as this means it's a direct attachment
2615 * shown as attachment, so it should behave as a file */
2616 window_msg = TNY_MIME_PART (tny_msg_view_get_msg (TNY_MSG_VIEW (priv->msg_view)));
2617 if (!modest_tny_mime_part_is_msg (mime_part)||
2618 mime_part == window_msg) {
2619 gchar *filepath = NULL;
2620 const gchar *att_filename = tny_mime_part_get_filename (mime_part);
2621 gboolean show_error_banner = FALSE;
2622 TnyFsStream *temp_stream = NULL;
2623 temp_stream = modest_utils_create_temp_stream (att_filename, attachment_uid,
2626 if (temp_stream != NULL) {
2627 DecodeAsyncHelper *helper = g_slice_new (DecodeAsyncHelper);
2628 helper->filepath = g_strdup (filepath);
2629 helper->banner = NULL;
2630 helper->banner_idle_id = g_timeout_add (1000, decode_async_banner_idle, helper);
2631 tny_mime_part_decode_to_stream_async (mime_part, TNY_STREAM (temp_stream),
2632 on_decode_to_stream_async_handler,
2635 g_object_unref (temp_stream);
2636 /* NOTE: files in the temporary area will be automatically
2637 * cleaned after some time if they are no longer in use */
2640 const gchar *content_type;
2641 /* the file may already exist but it isn't writable,
2642 * let's try to open it anyway */
2643 content_type = modest_tny_mime_part_get_content_type (mime_part);
2644 modest_platform_activate_file (filepath, content_type);
2646 g_warning ("%s: modest_utils_create_temp_stream failed", __FUNCTION__);
2647 show_error_banner = TRUE;
2652 if (show_error_banner)
2653 modest_platform_information_banner (NULL, NULL, _("mail_ib_file_operation_failed"));
2655 /* message attachment */
2656 TnyHeader *header = NULL;
2657 ModestWindowMgr *mgr;
2658 ModestWindow *msg_win = NULL;
2661 header = tny_msg_get_header (TNY_MSG (mime_part));
2662 mgr = modest_runtime_get_window_mgr ();
2663 found = modest_window_mgr_find_registered_header (mgr, header, &msg_win);
2666 if (msg_win) /* there is already a window for this uid; top it */
2667 gtk_window_present (GTK_WINDOW(msg_win));
2669 /* if it's found, but there is no msg_win, it's probably in the process of being created;
2670 * thus, we don't do anything */
2671 g_warning ("window for is already being created");
2673 /* it's not found, so create a new window for it */
2674 modest_window_mgr_register_header (mgr, header, attachment_uid); /* register the uid before building the window */
2675 gchar *account = g_strdup (modest_window_get_active_account (MODEST_WINDOW (window)));
2677 account = modest_account_mgr_get_default_account (modest_runtime_get_account_mgr ());
2678 msg_win = modest_msg_view_window_new_for_attachment (TNY_MSG (mime_part), account,
2679 NULL, attachment_uid);
2680 modest_window_set_zoom (MODEST_WINDOW (msg_win),
2681 modest_window_get_zoom (MODEST_WINDOW (window)));
2682 modest_window_mgr_register_window (mgr, msg_win, MODEST_WINDOW (window));
2683 gtk_widget_show_all (GTK_WIDGET (msg_win));
2686 g_object_unref (window_msg);
2687 g_object_unref (mime_part);
2700 GnomeVFSResult result;
2703 static void save_mime_part_info_free (SaveMimePartInfo *info, gboolean with_struct);
2704 static gboolean idle_save_mime_part_show_result (SaveMimePartInfo *info);
2705 static gpointer save_mime_part_to_file (SaveMimePartInfo *info);
2706 static void save_mime_parts_to_file_with_checks (SaveMimePartInfo *info);
2709 save_mime_part_info_free (SaveMimePartInfo *info, gboolean with_struct)
2713 for (node = info->pairs; node != NULL; node = g_list_next (node)) {
2714 SaveMimePartPair *pair = (SaveMimePartPair *) node->data;
2715 g_free (pair->filename);
2716 g_object_unref (pair->part);
2717 g_slice_free (SaveMimePartPair, pair);
2719 g_list_free (info->pairs);
2722 gtk_widget_destroy (info->banner);
2723 g_slice_free (SaveMimePartInfo, info);
2728 idle_save_mime_part_show_result (SaveMimePartInfo *info)
2730 if (info->pairs != NULL) {
2731 save_mime_part_to_file (info);
2733 /* This is a GDK lock because we are an idle callback and
2734 * hildon_banner_show_information is or does Gtk+ code */
2736 gdk_threads_enter (); /* CHECKED */
2737 save_mime_part_info_free (info, TRUE);
2738 if (info->result == GNOME_VFS_OK) {
2739 hildon_banner_show_information (NULL, NULL, _CS("sfil_ib_saved"));
2740 } else if (info->result == GNOME_VFS_ERROR_NO_SPACE) {
2741 hildon_banner_show_information (NULL, NULL,
2742 _KR("cerm_device_memory_full"));
2744 hildon_banner_show_information (NULL, NULL,
2745 _("mail_ib_file_operation_failed"));
2747 gdk_threads_leave (); /* CHECKED */
2754 save_mime_part_to_file (SaveMimePartInfo *info)
2756 GnomeVFSHandle *handle;
2758 SaveMimePartPair *pair = (SaveMimePartPair *) info->pairs->data;
2760 info->result = gnome_vfs_create (&handle, pair->filename, GNOME_VFS_OPEN_WRITE, FALSE, 0644);
2761 if (info->result == GNOME_VFS_OK) {
2762 GError *error = NULL;
2763 stream = tny_vfs_stream_new (handle);
2764 if (tny_mime_part_decode_to_stream (pair->part, stream, &error) < 0) {
2765 g_warning ("modest: could not save attachment %s: %d (%s)\n", pair->filename, error?error->code:-1, error?error->message:"Unknown error");
2767 info->result = GNOME_VFS_ERROR_IO;
2769 g_object_unref (G_OBJECT (stream));
2770 g_object_unref (pair->part);
2771 g_slice_free (SaveMimePartPair, pair);
2772 info->pairs = g_list_delete_link (info->pairs, info->pairs);
2774 g_warning ("modest: could not create save attachment %s: %s\n", pair->filename, gnome_vfs_result_to_string (info->result));
2775 save_mime_part_info_free (info, FALSE);
2778 g_idle_add ((GSourceFunc) idle_save_mime_part_show_result, info);
2783 save_mime_parts_to_file_with_checks (SaveMimePartInfo *info)
2785 gboolean is_ok = TRUE;
2786 gint replaced_files = 0;
2787 const GList *files = info->pairs;
2790 for (iter = files; (iter != NULL) && (replaced_files < 2); iter = g_list_next(iter)) {
2791 SaveMimePartPair *pair = iter->data;
2792 if (modest_utils_file_exists (pair->filename)) {
2796 if (replaced_files) {
2797 GtkWidget *confirm_overwrite_dialog;
2798 const gchar *message = (replaced_files == 1) ?
2799 _FM("docm_nc_replace_file") : _FM("docm_nc_replace_multiple");
2800 confirm_overwrite_dialog = hildon_note_new_confirmation (NULL, message);
2801 if (gtk_dialog_run (GTK_DIALOG (confirm_overwrite_dialog)) != GTK_RESPONSE_OK) {
2804 gtk_widget_destroy (confirm_overwrite_dialog);
2808 save_mime_part_info_free (info, TRUE);
2810 GtkWidget *banner = hildon_banner_show_animation (NULL, NULL,
2811 _CS("sfil_ib_saving"));
2812 info->banner = banner;
2813 g_thread_create ((GThreadFunc)save_mime_part_to_file, info, FALSE, NULL);
2819 save_attachments_response (GtkDialog *dialog,
2823 TnyList *mime_parts;
2825 GList *files_to_save = NULL;
2827 mime_parts = TNY_LIST (user_data);
2829 if (arg1 != GTK_RESPONSE_OK)
2832 chooser_uri = gtk_file_chooser_get_uri (GTK_FILE_CHOOSER (dialog));
2834 if (!modest_utils_folder_writable (chooser_uri)) {
2835 hildon_banner_show_information
2836 (NULL, NULL, _FM("sfil_ib_readonly_location"));
2840 iter = tny_list_create_iterator (mime_parts);
2841 while (!tny_iterator_is_done (iter)) {
2842 TnyMimePart *mime_part = (TnyMimePart *) tny_iterator_get_current (iter);
2844 if ((modest_tny_mime_part_is_attachment_for_modest (mime_part)) &&
2845 !tny_mime_part_is_purged (mime_part) &&
2846 (tny_mime_part_get_filename (mime_part) != NULL)) {
2847 SaveMimePartPair *pair;
2849 pair = g_slice_new0 (SaveMimePartPair);
2851 if (tny_list_get_length (mime_parts) > 1) {
2853 gnome_vfs_escape_slashes (tny_mime_part_get_filename (mime_part));
2854 pair->filename = g_build_filename (chooser_uri, escaped, NULL);
2857 pair->filename = g_strdup (chooser_uri);
2859 pair->part = mime_part;
2860 files_to_save = g_list_prepend (files_to_save, pair);
2862 tny_iterator_next (iter);
2864 g_object_unref (iter);
2866 g_free (chooser_uri);
2868 if (files_to_save != NULL) {
2869 SaveMimePartInfo *info = g_slice_new0 (SaveMimePartInfo);
2870 info->pairs = files_to_save;
2871 info->result = TRUE;
2872 save_mime_parts_to_file_with_checks (info);
2876 /* Free and close the dialog */
2877 g_object_unref (mime_parts);
2878 gtk_widget_destroy (GTK_WIDGET (dialog));
2882 modest_msg_view_window_save_attachments (ModestMsgViewWindow *window, TnyList *mime_parts)
2884 ModestMsgViewWindowPrivate *priv;
2885 GtkWidget *save_dialog = NULL;
2886 gchar *folder = NULL;
2887 gchar *filename = NULL;
2888 gchar *save_multiple_str = NULL;
2891 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window));
2892 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
2894 if (mime_parts == NULL) {
2895 mime_parts = modest_msg_view_get_selected_attachments (MODEST_MSG_VIEW (priv->msg_view));
2896 if (mime_parts == NULL || tny_list_get_length (mime_parts) == 0)
2899 g_object_ref (mime_parts);
2902 window_msg = tny_msg_view_get_msg (TNY_MSG_VIEW (priv->msg_view));
2903 /* prepare dialog */
2904 if (tny_list_get_length (mime_parts) == 1) {
2906 /* only one attachment selected */
2907 iter = tny_list_create_iterator (mime_parts);
2908 TnyMimePart *mime_part = (TnyMimePart *) tny_iterator_get_current (iter);
2909 g_object_unref (iter);
2910 if (!modest_tny_mime_part_is_msg (mime_part) &&
2911 modest_tny_mime_part_is_attachment_for_modest (mime_part) &&
2912 !tny_mime_part_is_purged (mime_part)) {
2913 filename = g_strdup (tny_mime_part_get_filename (mime_part));
2915 /* TODO: show any error? */
2916 g_warning ("Tried to save a non-file attachment");
2917 g_object_unref (mime_parts);
2920 g_object_unref (mime_part);
2922 save_multiple_str = g_strdup_printf (_FM("sfil_va_number_of_objects_attachments"),
2923 tny_list_get_length (mime_parts));
2925 g_object_unref (window_msg);
2927 save_dialog = hildon_file_chooser_dialog_new (GTK_WINDOW (window),
2928 GTK_FILE_CHOOSER_ACTION_SAVE);
2931 folder = g_build_filename (g_get_home_dir (), DEFAULT_FOLDER, NULL);
2932 gtk_file_chooser_set_current_folder (GTK_FILE_CHOOSER (save_dialog), folder);
2937 gtk_file_chooser_set_current_name (GTK_FILE_CHOOSER (save_dialog),
2942 /* if multiple, set multiple string */
2943 if (save_multiple_str) {
2944 g_object_set (G_OBJECT (save_dialog), "save-multiple", save_multiple_str, NULL);
2945 gtk_window_set_title (GTK_WINDOW (save_dialog), _FM("sfil_ti_save_objects_files"));
2948 /* We must run this asynchronously, because the hildon dialog
2949 performs a gtk_dialog_run by itself which leads to gdk
2951 g_signal_connect (save_dialog, "response",
2952 G_CALLBACK (save_attachments_response), mime_parts);
2954 gtk_widget_show_all (save_dialog);
2958 show_remove_attachment_information (gpointer userdata)
2960 ModestMsgViewWindow *window = (ModestMsgViewWindow *) userdata;
2961 ModestMsgViewWindowPrivate *priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
2963 /* We're outside the main lock */
2964 gdk_threads_enter ();
2966 if (priv->remove_attachment_banner != NULL) {
2967 gtk_widget_destroy (priv->remove_attachment_banner);
2968 g_object_unref (priv->remove_attachment_banner);
2971 priv->remove_attachment_banner = g_object_ref (
2972 hildon_banner_show_animation (NULL, NULL, _("mcen_ib_removing_attachment")));
2974 gdk_threads_leave ();
2980 modest_msg_view_window_remove_attachments (ModestMsgViewWindow *window, gboolean get_all)
2982 ModestMsgViewWindowPrivate *priv;
2983 TnyList *mime_parts = NULL;
2984 gchar *confirmation_message;
2990 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window));
2991 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
2994 mime_parts = modest_msg_view_get_attachments (MODEST_MSG_VIEW (priv->msg_view));
2996 mime_parts = modest_msg_view_get_selected_attachments (MODEST_MSG_VIEW (priv->msg_view));
2998 /* Remove already purged messages from mime parts list */
2999 iter = tny_list_create_iterator (mime_parts);
3000 while (!tny_iterator_is_done (iter)) {
3001 TnyMimePart *part = TNY_MIME_PART (tny_iterator_get_current (iter));
3002 tny_iterator_next (iter);
3003 if (tny_mime_part_is_purged (part)) {
3004 tny_list_remove (mime_parts, (GObject *) part);
3006 g_object_unref (part);
3008 g_object_unref (iter);
3010 if (tny_list_get_length (mime_parts) == 0) {
3011 g_object_unref (mime_parts);
3015 n_attachments = tny_list_get_length (mime_parts);
3016 if (n_attachments == 1) {
3020 iter = tny_list_create_iterator (mime_parts);
3021 part = (TnyMimePart *) tny_iterator_get_current (iter);
3022 g_object_unref (iter);
3023 if (modest_tny_mime_part_is_msg (part)) {
3025 header = tny_msg_get_header (TNY_MSG (part));
3026 filename = tny_header_dup_subject (header);
3027 g_object_unref (header);
3028 if (filename == NULL)
3029 filename = g_strdup (_("mail_va_no_subject"));
3031 filename = g_strdup (tny_mime_part_get_filename (TNY_MIME_PART (part)));
3033 confirmation_message = g_strdup_printf (_("mcen_nc_purge_file_text"), filename);
3035 g_object_unref (part);
3037 confirmation_message = g_strdup_printf (ngettext("mcen_nc_purge_file_text",
3038 "mcen_nc_purge_files_text",
3039 n_attachments), n_attachments);
3041 response = modest_platform_run_confirmation_dialog (GTK_WINDOW (window),
3042 confirmation_message);
3043 g_free (confirmation_message);
3045 if (response != GTK_RESPONSE_OK) {
3046 g_object_unref (mime_parts);
3050 priv->purge_timeout = g_timeout_add (2000, show_remove_attachment_information, window);
3052 iter = tny_list_create_iterator (mime_parts);
3053 while (!tny_iterator_is_done (iter)) {
3056 part = (TnyMimePart *) tny_iterator_get_current (iter);
3057 tny_mime_part_set_purged (TNY_MIME_PART (part));
3058 g_object_unref (part);
3059 tny_iterator_next (iter);
3061 g_object_unref (iter);
3063 msg = tny_msg_view_get_msg (TNY_MSG_VIEW (priv->msg_view));
3064 tny_msg_view_clear (TNY_MSG_VIEW (priv->msg_view));
3065 tny_msg_rewrite_cache (msg);
3066 tny_msg_view_set_msg (TNY_MSG_VIEW (priv->msg_view), msg);
3067 g_object_unref (msg);
3069 g_object_unref (mime_parts);
3071 if (priv->purge_timeout > 0) {
3072 g_source_remove (priv->purge_timeout);
3073 priv->purge_timeout = 0;
3076 if (priv->remove_attachment_banner) {
3077 gtk_widget_destroy (priv->remove_attachment_banner);
3078 g_object_unref (priv->remove_attachment_banner);
3079 priv->remove_attachment_banner = NULL;
3087 update_window_title (ModestMsgViewWindow *window)
3089 ModestMsgViewWindowPrivate *priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
3091 TnyHeader *header = NULL;
3092 gchar *subject = NULL;
3094 msg = tny_msg_view_get_msg (TNY_MSG_VIEW (priv->msg_view));
3097 header = tny_msg_get_header (msg);
3098 subject = tny_header_dup_subject (header);
3099 g_object_unref (header);
3100 g_object_unref (msg);
3103 if ((subject == NULL)||(subject[0] == '\0')) {
3105 subject = g_strdup (_("mail_va_no_subject"));
3108 gtk_window_set_title (GTK_WINDOW (window), subject);
3112 static void on_move_focus (GtkWidget *widget,
3113 GtkDirectionType direction,
3116 g_signal_stop_emission_by_name (G_OBJECT (widget), "move-focus");
3120 fetch_image_open_stream (TnyStreamCache *self, gint64 *expected_size, gchar *uri)
3122 GnomeVFSResult result;
3123 GnomeVFSHandle *handle = NULL;
3124 GnomeVFSFileInfo *info = NULL;
3127 result = gnome_vfs_open (&handle, uri, GNOME_VFS_OPEN_READ);
3128 if (result != GNOME_VFS_OK) {
3133 info = gnome_vfs_file_info_new ();
3134 result = gnome_vfs_get_file_info_from_handle (handle, info, GNOME_VFS_FILE_INFO_DEFAULT);
3135 if (result != GNOME_VFS_OK || ! (info->valid_fields & GNOME_VFS_FILE_INFO_FIELDS_SIZE)) {
3136 /* We put a "safe" default size for going to cache */
3137 *expected_size = (300*1024);
3139 *expected_size = info->size;
3141 gnome_vfs_file_info_unref (info);
3143 stream = tny_vfs_stream_new (handle);
3152 TnyStream *output_stream;
3153 GtkWidget *msg_view;
3157 on_fetch_image_idle_refresh_view (gpointer userdata)
3160 FetchImageData *fidata = (FetchImageData *) userdata;
3161 g_message ("REFRESH VIEW");
3162 if (GTK_WIDGET_DRAWABLE (fidata->msg_view)) {
3163 g_message ("QUEUING DRAW");
3164 gtk_widget_queue_draw (fidata->msg_view);
3166 g_object_unref (fidata->msg_view);
3167 g_slice_free (FetchImageData, fidata);
3172 on_fetch_image_thread (gpointer userdata)
3174 FetchImageData *fidata = (FetchImageData *) userdata;
3175 TnyStreamCache *cache;
3176 TnyStream *cache_stream;
3178 cache = modest_runtime_get_images_cache ();
3179 cache_stream = tny_stream_cache_get_stream (cache, fidata->cache_id, (TnyStreamCacheOpenStreamFetcher) fetch_image_open_stream, (gpointer) fidata->uri);
3180 g_free (fidata->cache_id);
3181 g_free (fidata->uri);
3183 if (cache_stream != NULL) {
3184 tny_stream_write_to_stream (cache_stream, fidata->output_stream);
3185 tny_stream_close (cache_stream);
3186 g_object_unref (cache_stream);
3189 tny_stream_close (fidata->output_stream);
3190 g_object_unref (fidata->output_stream);
3193 gdk_threads_enter ();
3194 g_idle_add (on_fetch_image_idle_refresh_view, fidata);
3195 gdk_threads_leave ();
3201 on_fetch_image (ModestMsgView *msgview,
3204 ModestMsgViewWindow *window)
3206 const gchar *current_account;
3207 ModestMsgViewWindowPrivate *priv;
3208 FetchImageData *fidata;
3210 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
3212 current_account = modest_window_get_active_account (MODEST_WINDOW (window));
3214 fidata = g_slice_new0 (FetchImageData);
3215 fidata->msg_view = g_object_ref (msgview);
3216 fidata->uri = g_strdup (uri);
3217 fidata->cache_id = modest_images_cache_get_id (current_account, uri);
3218 fidata->output_stream = g_object_ref (stream);
3220 if (g_thread_create (on_fetch_image_thread, fidata, FALSE, NULL) == NULL) {
3221 g_object_unref (fidata->output_stream);
3222 g_free (fidata->cache_id);
3223 g_free (fidata->uri);
3224 g_object_unref (fidata->msg_view);
3225 g_slice_free (FetchImageData, fidata);
3226 tny_stream_close (stream);
3234 modest_msg_view_window_add_to_contacts (ModestMsgViewWindow *self)
3236 modest_ui_actions_on_add_to_contacts (NULL, MODEST_WINDOW (self));
3241 modest_msg_view_window_fetch_images (ModestMsgViewWindow *self)
3243 ModestMsgViewWindowPrivate *priv;
3244 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
3246 modest_msg_view_request_fetch_images (MODEST_MSG_VIEW (priv->msg_view));
3250 modest_msg_view_window_has_blocked_external_images (ModestMsgViewWindow *self)
3252 ModestMsgViewWindowPrivate *priv;
3253 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
3255 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self), FALSE);
3257 return modest_msg_view_has_blocked_external_images (MODEST_MSG_VIEW (priv->msg_view));
3261 modest_msg_view_window_reload (ModestMsgViewWindow *self)
3263 ModestMsgViewWindowPrivate *priv;
3266 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self));
3268 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
3269 header = modest_msg_view_window_get_header (MODEST_MSG_VIEW_WINDOW (self));
3271 if (!message_reader (self, priv, header, priv->row_reference)) {
3272 g_warning ("Shouldn't happen, trying to reload a message failed");
3275 g_object_unref (header);