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;
240 TnyMimePart *other_body;
245 #define MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE((o), \
246 MODEST_TYPE_MSG_VIEW_WINDOW, \
247 ModestMsgViewWindowPrivate))
249 static GtkWindowClass *parent_class = NULL;
251 /* uncomment the following if you have defined any signals */
252 static guint signals[LAST_SIGNAL] = {0};
255 modest_msg_view_window_get_type (void)
257 static GType my_type = 0;
259 static const GTypeInfo my_info = {
260 sizeof(ModestMsgViewWindowClass),
261 NULL, /* base init */
262 NULL, /* base finalize */
263 (GClassInitFunc) modest_msg_view_window_class_init,
264 NULL, /* class finalize */
265 NULL, /* class data */
266 sizeof(ModestMsgViewWindow),
268 (GInstanceInitFunc) modest_msg_view_window_init,
271 my_type = g_type_register_static (MODEST_TYPE_WINDOW,
272 "ModestMsgViewWindow",
275 static const GInterfaceInfo modest_header_view_observer_info =
277 (GInterfaceInitFunc) modest_header_view_observer_init,
278 NULL, /* interface_finalize */
279 NULL /* interface_data */
282 g_type_add_interface_static (my_type,
283 MODEST_TYPE_HEADER_VIEW_OBSERVER,
284 &modest_header_view_observer_info);
290 save_state (ModestWindow *self)
292 modest_widget_memory_save (modest_runtime_get_conf (),
294 MODEST_CONF_MSG_VIEW_WINDOW_KEY);
299 restore_settings (ModestMsgViewWindow *self)
302 ModestWindowPrivate *parent_priv = MODEST_WINDOW_GET_PRIVATE (self);
305 conf = modest_runtime_get_conf ();
306 action = gtk_ui_manager_get_action (parent_priv->ui_manager,
307 "/MenuBar/ViewMenu/ViewShowToolbarMenu/ViewShowToolbarNormalScreenMenu");
308 gtk_toggle_action_set_active (GTK_TOGGLE_ACTION (action),
309 modest_conf_get_bool (conf, MODEST_CONF_MSG_VIEW_WINDOW_SHOW_TOOLBAR, NULL));
310 action = gtk_ui_manager_get_action (parent_priv->ui_manager,
311 "/MenuBar/ViewMenu/ViewShowToolbarMenu/ViewShowToolbarFullScreenMenu");
312 gtk_toggle_action_set_active (GTK_TOGGLE_ACTION (action),
313 modest_conf_get_bool (conf, MODEST_CONF_MSG_VIEW_WINDOW_SHOW_TOOLBAR_FULLSCREEN, NULL));
314 modest_widget_memory_restore (conf,
316 MODEST_CONF_MSG_VIEW_WINDOW_KEY);
319 static gboolean modest_msg_view_window_scroll_child (ModestMsgViewWindow *self,
320 GtkScrollType scroll_type,
324 ModestMsgViewWindowPrivate *priv;
325 gboolean return_value;
327 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(self);
328 g_signal_emit_by_name (priv->main_scroll, "scroll-child", scroll_type, horizontal, &return_value);
333 add_scroll_binding (GtkBindingSet *binding_set,
335 GtkScrollType scroll)
337 guint keypad_keyval = keyval - GDK_Left + GDK_KP_Left;
339 gtk_binding_entry_add_signal (binding_set, keyval, 0,
341 GTK_TYPE_SCROLL_TYPE, scroll,
342 G_TYPE_BOOLEAN, FALSE);
343 gtk_binding_entry_add_signal (binding_set, keypad_keyval, 0,
345 GTK_TYPE_SCROLL_TYPE, scroll,
346 G_TYPE_BOOLEAN, FALSE);
350 modest_msg_view_window_class_init (ModestMsgViewWindowClass *klass)
352 GObjectClass *gobject_class;
353 ModestWindowClass *modest_window_class;
354 GtkBindingSet *binding_set;
356 gobject_class = (GObjectClass*) klass;
357 modest_window_class = (ModestWindowClass *) klass;
359 parent_class = g_type_class_peek_parent (klass);
360 gobject_class->finalize = modest_msg_view_window_finalize;
362 modest_window_class->set_zoom_func = modest_msg_view_window_set_zoom;
363 modest_window_class->get_zoom_func = modest_msg_view_window_get_zoom;
364 modest_window_class->zoom_minus_func = modest_msg_view_window_zoom_minus;
365 modest_window_class->zoom_plus_func = modest_msg_view_window_zoom_plus;
366 modest_window_class->show_toolbar_func = modest_msg_view_window_show_toolbar;
367 modest_window_class->disconnect_signals_func = modest_msg_view_window_disconnect_signals;
369 modest_window_class->save_state_func = save_state;
371 klass->scroll_child = modest_msg_view_window_scroll_child;
373 signals[MSG_CHANGED_SIGNAL] =
374 g_signal_new ("msg-changed",
375 G_TYPE_FROM_CLASS (gobject_class),
377 G_STRUCT_OFFSET (ModestMsgViewWindowClass, msg_changed),
379 modest_marshal_VOID__POINTER_POINTER,
380 G_TYPE_NONE, 2, G_TYPE_POINTER, G_TYPE_POINTER);
382 signals[SCROLL_CHILD_SIGNAL] =
383 g_signal_new ("scroll-child",
384 G_TYPE_FROM_CLASS (gobject_class),
385 G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
386 G_STRUCT_OFFSET (ModestMsgViewWindowClass, scroll_child),
388 modest_marshal_BOOLEAN__ENUM_BOOLEAN,
389 G_TYPE_BOOLEAN, 2, GTK_TYPE_SCROLL_TYPE, G_TYPE_BOOLEAN);
391 binding_set = gtk_binding_set_by_class (klass);
392 add_scroll_binding (binding_set, GDK_Up, GTK_SCROLL_STEP_UP);
393 add_scroll_binding (binding_set, GDK_Down, GTK_SCROLL_STEP_DOWN);
394 add_scroll_binding (binding_set, GDK_Page_Up, GTK_SCROLL_PAGE_UP);
395 add_scroll_binding (binding_set, GDK_Page_Down, GTK_SCROLL_PAGE_DOWN);
396 add_scroll_binding (binding_set, GDK_Home, GTK_SCROLL_START);
397 add_scroll_binding (binding_set, GDK_End, GTK_SCROLL_END);
399 g_type_class_add_private (gobject_class, sizeof(ModestMsgViewWindowPrivate));
403 static void modest_header_view_observer_init(
404 ModestHeaderViewObserverIface *iface_class)
406 iface_class->update_func = modest_msg_view_window_update_model_replaced;
410 modest_msg_view_window_init (ModestMsgViewWindow *obj)
412 ModestMsgViewWindowPrivate *priv;
413 ModestWindowPrivate *parent_priv = NULL;
414 GtkActionGroup *action_group = NULL;
415 GError *error = NULL;
416 GdkPixbuf *window_icon;
418 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(obj);
419 parent_priv = MODEST_WINDOW_GET_PRIVATE(obj);
420 parent_priv->ui_manager = gtk_ui_manager_new();
422 action_group = gtk_action_group_new ("ModestMsgViewWindowActions");
423 gtk_action_group_set_translation_domain (action_group, GETTEXT_PACKAGE);
425 /* Add common actions */
426 gtk_action_group_add_actions (action_group,
427 modest_action_entries,
428 G_N_ELEMENTS (modest_action_entries),
430 gtk_action_group_add_toggle_actions (action_group,
431 modest_toggle_action_entries,
432 G_N_ELEMENTS (modest_toggle_action_entries),
434 gtk_action_group_add_toggle_actions (action_group,
435 msg_view_toggle_action_entries,
436 G_N_ELEMENTS (msg_view_toggle_action_entries),
438 gtk_action_group_add_radio_actions (action_group,
439 msg_view_zoom_action_entries,
440 G_N_ELEMENTS (msg_view_zoom_action_entries),
442 G_CALLBACK (modest_ui_actions_on_change_zoom),
445 gtk_ui_manager_insert_action_group (parent_priv->ui_manager, action_group, 0);
446 g_object_unref (action_group);
448 /* Load the UI definition */
449 gtk_ui_manager_add_ui_from_file (parent_priv->ui_manager, MODEST_UIDIR "modest-msg-view-window-ui.xml",
452 g_printerr ("modest: could not merge modest-msg-view-window-ui.xml: %s\n", error->message);
453 g_error_free (error);
458 /* Add accelerators */
459 gtk_window_add_accel_group (GTK_WINDOW (obj),
460 gtk_ui_manager_get_accel_group (parent_priv->ui_manager));
462 priv->is_search_result = FALSE;
463 priv->is_outbox = FALSE;
465 priv->msg_view = NULL;
466 priv->header_model = NULL;
467 priv->header_folder_id = NULL;
468 priv->clipboard_change_handler = 0;
469 priv->queue_change_handler = 0;
470 priv->account_removed_handler = 0;
471 priv->row_changed_handler = 0;
472 priv->row_deleted_handler = 0;
473 priv->row_inserted_handler = 0;
474 priv->rows_reordered_handler = 0;
475 priv->current_toolbar_mode = TOOLBAR_MODE_NORMAL;
477 priv->optimized_view = FALSE;
478 priv->progress_bar_timeout = 0;
479 priv->purge_timeout = 0;
480 priv->remove_attachment_banner = NULL;
481 priv->msg_uid = NULL;
482 priv->other_body = NULL;
484 priv->sighandlers = NULL;
487 init_window (MODEST_MSG_VIEW_WINDOW(obj));
489 /* Set window icon */
490 window_icon = modest_platform_get_icon (MODEST_APP_MSG_VIEW_ICON, MODEST_ICON_SIZE_BIG);
492 gtk_window_set_icon (GTK_WINDOW (obj), window_icon);
493 g_object_unref (window_icon);
496 hildon_program_add_window (hildon_program_get_instance(),
499 modest_window_mgr_register_help_id (modest_runtime_get_window_mgr(),
500 GTK_WINDOW(obj),"applications_email_viewer");
505 set_toolbar_transfer_mode (ModestMsgViewWindow *self)
507 ModestMsgViewWindowPrivate *priv = NULL;
509 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self), FALSE);
511 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(self);
513 set_toolbar_mode (self, TOOLBAR_MODE_TRANSFER);
515 if (priv->progress_bar_timeout > 0) {
516 g_source_remove (priv->progress_bar_timeout);
517 priv->progress_bar_timeout = 0;
524 set_toolbar_mode (ModestMsgViewWindow *self,
525 ModestToolBarModes mode)
527 ModestWindowPrivate *parent_priv;
528 ModestMsgViewWindowPrivate *priv;
529 /* GtkWidget *widget = NULL; */
531 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self));
533 parent_priv = MODEST_WINDOW_GET_PRIVATE(self);
534 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(self);
536 /* Sets current toolbar mode */
537 priv->current_toolbar_mode = mode;
539 /* Update toolbar dimming state */
540 modest_ui_actions_check_toolbar_dimming_rules (MODEST_WINDOW (self));
543 case TOOLBAR_MODE_NORMAL:
544 if (priv->progress_toolitem) {
545 gtk_tool_item_set_expand (GTK_TOOL_ITEM (priv->progress_toolitem), FALSE);
546 gtk_widget_hide (priv->progress_toolitem);
549 if (priv->progress_bar)
550 gtk_widget_hide (priv->progress_bar);
552 if (priv->cancel_toolitem)
553 gtk_widget_hide (priv->cancel_toolitem);
555 if (priv->prev_toolitem)
556 gtk_widget_show (priv->prev_toolitem);
558 if (priv->next_toolitem)
559 gtk_widget_show (priv->next_toolitem);
561 /* Hide toolbar if optimized view is enabled */
562 if (priv->optimized_view) {
563 gtk_widget_set_no_show_all (parent_priv->toolbar, TRUE);
564 gtk_widget_hide (GTK_WIDGET(parent_priv->toolbar));
568 case TOOLBAR_MODE_TRANSFER:
569 if (priv->prev_toolitem)
570 gtk_widget_hide (priv->prev_toolitem);
572 if (priv->next_toolitem)
573 gtk_widget_hide (priv->next_toolitem);
575 if (priv->progress_bar)
576 gtk_widget_show (priv->progress_bar);
578 if (priv->progress_toolitem) {
579 gtk_tool_item_set_expand (GTK_TOOL_ITEM (priv->progress_toolitem), TRUE);
580 gtk_widget_show (priv->progress_toolitem);
583 if (priv->cancel_toolitem)
584 gtk_widget_show (priv->cancel_toolitem);
586 /* Show toolbar if it's hiden (optimized view ) */
587 if (priv->optimized_view) {
588 gtk_widget_set_no_show_all (parent_priv->toolbar, FALSE);
589 gtk_widget_show (GTK_WIDGET (parent_priv->toolbar));
594 g_return_if_reached ();
601 init_window (ModestMsgViewWindow *obj)
603 GtkWidget *main_vbox;
604 ModestMsgViewWindowPrivate *priv;
606 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(obj);
608 priv->msg_view = GTK_WIDGET (tny_platform_factory_new_msg_view (modest_tny_platform_factory_get_instance ()));
609 modest_msg_view_set_shadow_type (MODEST_MSG_VIEW (priv->msg_view), GTK_SHADOW_NONE);
610 main_vbox = gtk_vbox_new (FALSE, 6);
612 #ifdef MODEST_USE_MOZEMBED
613 priv->main_scroll = priv->msg_view;
614 gtk_widget_set_size_request (priv->msg_view, -1, 1600);
616 priv->main_scroll = gtk_scrolled_window_new (NULL, NULL);
617 gtk_container_add (GTK_CONTAINER (priv->main_scroll), priv->msg_view);
619 gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (priv->main_scroll), GTK_POLICY_NEVER, GTK_POLICY_AUTOMATIC);
620 gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW (priv->main_scroll), GTK_SHADOW_NONE);
621 modest_maemo_set_thumbable_scrollbar (GTK_SCROLLED_WINDOW(priv->main_scroll), TRUE);
623 gtk_box_pack_start (GTK_BOX(main_vbox), priv->main_scroll, TRUE, TRUE, 0);
624 gtk_container_add (GTK_CONTAINER(obj), main_vbox);
626 priv->find_toolbar = hildon_find_toolbar_new (NULL);
627 hildon_window_add_toolbar (HILDON_WINDOW (obj), GTK_TOOLBAR (priv->find_toolbar));
628 gtk_widget_set_no_show_all (priv->find_toolbar, TRUE);
630 gtk_widget_show_all (GTK_WIDGET(main_vbox));
634 modest_msg_view_window_disconnect_signals (ModestWindow *self)
636 ModestMsgViewWindowPrivate *priv;
637 ModestHeaderView *header_view = NULL;
638 ModestWindow *main_window = NULL;
640 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
642 if (gtk_clipboard_get (GDK_SELECTION_PRIMARY) &&
643 g_signal_handler_is_connected (gtk_clipboard_get (GDK_SELECTION_PRIMARY),
644 priv->clipboard_change_handler))
645 g_signal_handler_disconnect (gtk_clipboard_get (GDK_SELECTION_PRIMARY),
646 priv->clipboard_change_handler);
648 if (g_signal_handler_is_connected (G_OBJECT (modest_runtime_get_mail_operation_queue ()),
649 priv->queue_change_handler))
650 g_signal_handler_disconnect (G_OBJECT (modest_runtime_get_mail_operation_queue ()),
651 priv->queue_change_handler);
653 if (g_signal_handler_is_connected (G_OBJECT (modest_runtime_get_account_store ()),
654 priv->account_removed_handler))
655 g_signal_handler_disconnect (G_OBJECT (modest_runtime_get_account_store ()),
656 priv->account_removed_handler);
658 if (priv->header_model) {
659 if (g_signal_handler_is_connected(G_OBJECT (priv->header_model),
660 priv->row_changed_handler))
661 g_signal_handler_disconnect(G_OBJECT (priv->header_model),
662 priv->row_changed_handler);
664 if (g_signal_handler_is_connected(G_OBJECT (priv->header_model),
665 priv->row_deleted_handler))
666 g_signal_handler_disconnect(G_OBJECT (priv->header_model),
667 priv->row_deleted_handler);
669 if (g_signal_handler_is_connected(G_OBJECT (priv->header_model),
670 priv->row_inserted_handler))
671 g_signal_handler_disconnect(G_OBJECT (priv->header_model),
672 priv->row_inserted_handler);
674 if (g_signal_handler_is_connected(G_OBJECT (priv->header_model),
675 priv->rows_reordered_handler))
676 g_signal_handler_disconnect(G_OBJECT (priv->header_model),
677 priv->rows_reordered_handler);
680 modest_signal_mgr_disconnect_all_and_destroy (priv->sighandlers);
681 priv->sighandlers = NULL;
683 main_window = modest_window_mgr_get_main_window (modest_runtime_get_window_mgr(),
684 FALSE); /* don't create */
688 header_view = MODEST_HEADER_VIEW(
689 modest_main_window_get_child_widget(
690 MODEST_MAIN_WINDOW(main_window),
691 MODEST_MAIN_WINDOW_WIDGET_TYPE_HEADER_VIEW));
692 if (header_view == NULL)
695 modest_header_view_remove_observer(header_view,
696 MODEST_HEADER_VIEW_OBSERVER(self));
700 modest_msg_view_window_finalize (GObject *obj)
702 ModestMsgViewWindowPrivate *priv;
704 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (obj);
706 /* Sanity check: shouldn't be needed, the window mgr should
707 call this function before */
708 modest_msg_view_window_disconnect_signals (MODEST_WINDOW (obj));
710 if (priv->other_body != NULL) {
711 g_object_unref (priv->other_body);
712 priv->other_body = NULL;
715 if (priv->header_model != NULL) {
716 g_object_unref (priv->header_model);
717 priv->header_model = NULL;
720 if (priv->progress_bar_timeout > 0) {
721 g_source_remove (priv->progress_bar_timeout);
722 priv->progress_bar_timeout = 0;
725 if (priv->remove_attachment_banner) {
726 gtk_widget_destroy (priv->remove_attachment_banner);
727 g_object_unref (priv->remove_attachment_banner);
728 priv->remove_attachment_banner = NULL;
731 if (priv->purge_timeout > 0) {
732 g_source_remove (priv->purge_timeout);
733 priv->purge_timeout = 0;
736 if (priv->row_reference) {
737 gtk_tree_row_reference_free (priv->row_reference);
738 priv->row_reference = NULL;
741 if (priv->next_row_reference) {
742 gtk_tree_row_reference_free (priv->next_row_reference);
743 priv->next_row_reference = NULL;
747 g_free (priv->msg_uid);
748 priv->msg_uid = NULL;
751 G_OBJECT_CLASS(parent_class)->finalize (obj);
755 select_next_valid_row (GtkTreeModel *model,
756 GtkTreeRowReference **row_reference,
760 GtkTreeIter tmp_iter;
762 GtkTreePath *next = NULL;
763 gboolean retval = FALSE, finished;
765 g_return_val_if_fail (gtk_tree_row_reference_valid (*row_reference), FALSE);
767 path = gtk_tree_row_reference_get_path (*row_reference);
768 gtk_tree_model_get_iter (model, &tmp_iter, path);
769 gtk_tree_row_reference_free (*row_reference);
770 *row_reference = NULL;
774 TnyHeader *header = NULL;
776 if (gtk_tree_model_iter_next (model, &tmp_iter)) {
777 gtk_tree_model_get (model, &tmp_iter,
778 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
782 if (msg_is_visible (header, is_outbox)) {
783 next = gtk_tree_model_get_path (model, &tmp_iter);
784 *row_reference = gtk_tree_row_reference_new (model, next);
785 gtk_tree_path_free (next);
789 g_object_unref (header);
792 } else if (cycle && gtk_tree_model_get_iter_first (model, &tmp_iter)) {
793 next = gtk_tree_model_get_path (model, &tmp_iter);
795 /* Ensure that we are not selecting the same */
796 if (gtk_tree_path_compare (path, next) != 0) {
797 gtk_tree_model_get (model, &tmp_iter,
798 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
801 if (msg_is_visible (header, is_outbox)) {
802 *row_reference = gtk_tree_row_reference_new (model, next);
806 g_object_unref (header);
810 /* If we ended up in the same message
811 then there is no valid next
815 gtk_tree_path_free (next);
817 /* If there are no more messages and we don't
818 want to start again in the first one then
819 there is no valid next message */
825 gtk_tree_path_free (path);
830 /* TODO: This should be in _init(), with the parameters as properties. */
832 modest_msg_view_window_construct (ModestMsgViewWindow *self,
833 const gchar *modest_account_name,
834 const gchar *msg_uid)
837 ModestMsgViewWindowPrivate *priv = NULL;
838 ModestWindowPrivate *parent_priv = NULL;
839 ModestDimmingRulesGroup *menu_rules_group = NULL;
840 ModestDimmingRulesGroup *toolbar_rules_group = NULL;
841 ModestDimmingRulesGroup *clipboard_rules_group = NULL;
843 obj = G_OBJECT (self);
844 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(obj);
845 parent_priv = MODEST_WINDOW_GET_PRIVATE(obj);
847 priv->msg_uid = g_strdup (msg_uid);
850 parent_priv->menubar = modest_maemo_utils_get_manager_menubar_as_menu (parent_priv->ui_manager, "/MenuBar");
851 hildon_window_set_menu (HILDON_WINDOW(obj), GTK_MENU(parent_priv->menubar));
852 gtk_widget_show (parent_priv->menubar);
853 parent_priv->ui_dimming_manager = modest_ui_dimming_manager_new();
855 menu_rules_group = modest_dimming_rules_group_new (MODEST_DIMMING_RULES_MENU, FALSE);
856 toolbar_rules_group = modest_dimming_rules_group_new (MODEST_DIMMING_RULES_TOOLBAR, TRUE);
857 clipboard_rules_group = modest_dimming_rules_group_new (MODEST_DIMMING_RULES_CLIPBOARD, FALSE);
859 /* Add common dimming rules */
860 modest_dimming_rules_group_add_rules (menu_rules_group,
861 modest_msg_view_menu_dimming_entries,
862 G_N_ELEMENTS (modest_msg_view_menu_dimming_entries),
863 MODEST_WINDOW (self));
864 modest_dimming_rules_group_add_rules (toolbar_rules_group,
865 modest_msg_view_toolbar_dimming_entries,
866 G_N_ELEMENTS (modest_msg_view_toolbar_dimming_entries),
867 MODEST_WINDOW (self));
868 modest_dimming_rules_group_add_rules (clipboard_rules_group,
869 modest_msg_view_clipboard_dimming_entries,
870 G_N_ELEMENTS (modest_msg_view_clipboard_dimming_entries),
871 MODEST_WINDOW (self));
873 /* Insert dimming rules group for this window */
874 modest_ui_dimming_manager_insert_rules_group (parent_priv->ui_dimming_manager, menu_rules_group);
875 modest_ui_dimming_manager_insert_rules_group (parent_priv->ui_dimming_manager, toolbar_rules_group);
876 modest_ui_dimming_manager_insert_rules_group (parent_priv->ui_dimming_manager, clipboard_rules_group);
877 g_object_unref (menu_rules_group);
878 g_object_unref (toolbar_rules_group);
879 g_object_unref (clipboard_rules_group);
881 restore_settings (MODEST_MSG_VIEW_WINDOW(obj));
883 /* g_signal_connect (G_OBJECT(obj), "delete-event", G_CALLBACK(on_delete_event), obj); */
885 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);
886 g_signal_connect (G_OBJECT(priv->msg_view), "activate_link",
887 G_CALLBACK (modest_ui_actions_on_msg_link_clicked), obj);
888 g_signal_connect (G_OBJECT(priv->msg_view), "link_hover",
889 G_CALLBACK (modest_ui_actions_on_msg_link_hover), obj);
890 g_signal_connect (G_OBJECT(priv->msg_view), "attachment_clicked",
891 G_CALLBACK (modest_ui_actions_on_msg_attachment_clicked), obj);
892 g_signal_connect (G_OBJECT(priv->msg_view), "recpt_activated",
893 G_CALLBACK (modest_ui_actions_on_msg_recpt_activated), obj);
894 g_signal_connect (G_OBJECT(priv->msg_view), "link_contextual",
895 G_CALLBACK (modest_ui_actions_on_msg_link_contextual), obj);
896 g_signal_connect (G_OBJECT (priv->msg_view), "fetch_image",
897 G_CALLBACK (on_fetch_image), obj);
899 g_signal_connect (G_OBJECT (obj), "key-release-event",
900 G_CALLBACK (modest_msg_view_window_key_event),
903 g_signal_connect (G_OBJECT (obj), "key-press-event",
904 G_CALLBACK (modest_msg_view_window_key_event),
907 g_signal_connect (G_OBJECT (obj), "window-state-event",
908 G_CALLBACK (modest_msg_view_window_window_state_event),
911 g_signal_connect (G_OBJECT (obj), "move-focus",
912 G_CALLBACK (on_move_focus), obj);
914 /* Mail Operation Queue */
915 priv->queue_change_handler = g_signal_connect (G_OBJECT (modest_runtime_get_mail_operation_queue ()),
917 G_CALLBACK (on_queue_changed),
920 /* Account manager */
921 priv->account_removed_handler = g_signal_connect (G_OBJECT (modest_runtime_get_account_store ()),
923 G_CALLBACK(on_account_removed),
926 modest_window_set_active_account (MODEST_WINDOW(obj), modest_account_name);
928 g_signal_connect (G_OBJECT (priv->find_toolbar), "close", G_CALLBACK (modest_msg_view_window_find_toolbar_close), obj);
929 g_signal_connect (G_OBJECT (priv->find_toolbar), "search", G_CALLBACK (modest_msg_view_window_find_toolbar_search), obj);
930 priv->last_search = NULL;
932 /* Init the clipboard actions dim status */
933 modest_msg_view_grab_focus(MODEST_MSG_VIEW (priv->msg_view));
935 update_window_title (MODEST_MSG_VIEW_WINDOW (obj));
941 modest_msg_view_window_new_from_uid (const gchar *modest_account_name,
942 const gchar *mailbox,
943 const gchar *msg_uid)
945 ModestMsgViewWindow *window = NULL;
946 ModestMsgViewWindowPrivate *priv = NULL;
947 TnyFolder *folder = NULL;
948 ModestWindowMgr *mgr = NULL;
950 TnyAccount *account = NULL;
952 mgr = modest_runtime_get_window_mgr ();
953 window = MODEST_MSG_VIEW_WINDOW (modest_window_mgr_get_msg_view_window (mgr));
954 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), NULL);
956 modest_msg_view_window_construct (window, modest_account_name, mailbox, msg_uid);
958 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
962 is_merge = g_str_has_prefix (msg_uid, "merge:");
964 /* Get the account */
966 account = tny_account_store_find_account (TNY_ACCOUNT_STORE (modest_runtime_get_account_store ()),
970 if (is_merge || account) {
971 OpenMsgPerformerInfo *info;
972 TnyFolder *folder = NULL;
974 /* Try to get the message, if it's already downloaded
975 we don't need to connect */
977 folder = tny_store_account_find_folder (TNY_STORE_ACCOUNT (account), msg_uid, NULL);
979 ModestTnyAccountStore *account_store;
980 ModestTnyLocalFoldersAccount *local_folders_account;
982 account_store = modest_runtime_get_account_store ();
983 local_folders_account = MODEST_TNY_LOCAL_FOLDERS_ACCOUNT (
984 modest_tny_account_store_get_local_folders_account (account_store));
985 folder = modest_tny_local_folders_account_get_merged_outbox (local_folders_account);
986 g_object_unref (local_folders_account);
990 gboolean device_online;
992 device = modest_runtime_get_device();
993 device_online = tny_device_is_online (device);
995 message_reader (window, priv, NULL, msg_uid, folder, NULL);
997 TnyMsg *msg = tny_folder_find_msg (folder, uri, NULL);
999 tny_msg_view_set_msg (TNY_MSG_VIEW (priv->msg_view), msg);
1000 g_object_unref (msg);
1002 message_reader (window, priv, NULL, msg_uid, folder, NULL);
1005 g_object_unref (folder);
1010 /* Check dimming rules */
1011 modest_ui_actions_check_toolbar_dimming_rules (MODEST_WINDOW (window));
1012 modest_ui_actions_check_menu_dimming_rules (MODEST_WINDOW (window));
1013 modest_window_check_dimming_rules_group (MODEST_WINDOW (window), MODEST_DIMMING_RULES_CLIPBOARD);
1015 return MODEST_WINDOW(window);
1020 /* FIXME: parameter checks */
1022 modest_msg_view_window_new_with_header_model (TnyMsg *msg,
1023 const gchar *modest_account_name,
1024 const gchar *mailbox, /*ignored */
1025 const gchar *msg_uid,
1026 GtkTreeModel *model,
1027 GtkTreeRowReference *row_reference)
1029 ModestMsgViewWindow *window = NULL;
1030 ModestMsgViewWindowPrivate *priv = NULL;
1031 TnyFolder *header_folder = NULL;
1032 ModestHeaderView *header_view = NULL;
1033 ModestWindow *main_window = NULL;
1034 ModestWindowMgr *mgr = NULL;
1036 MODEST_DEBUG_BLOCK (
1037 modest_tny_mime_part_to_string (TNY_MIME_PART (msg), 0);
1040 mgr = modest_runtime_get_window_mgr ();
1041 window = MODEST_MSG_VIEW_WINDOW (modest_window_mgr_get_msg_view_window (mgr));
1042 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), NULL);
1044 modest_msg_view_window_construct (window, modest_account_name, msg_uid);
1046 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1048 /* Remember the message list's TreeModel so we can detect changes
1049 * and change the list selection when necessary: */
1051 main_window = modest_window_mgr_get_main_window(mgr, FALSE); /* don't create */
1053 header_view = MODEST_HEADER_VIEW(modest_main_window_get_child_widget(
1054 MODEST_MAIN_WINDOW(main_window),
1055 MODEST_MAIN_WINDOW_WIDGET_TYPE_HEADER_VIEW));
1058 if (header_view != NULL){
1059 header_folder = modest_header_view_get_folder(header_view);
1060 /* This could happen if the header folder was
1061 unseleted before opening this msg window (for
1062 example if the user selects an account in the
1063 folder view of the main window */
1064 if (header_folder) {
1065 priv->is_outbox = (modest_tny_folder_guess_folder_type (header_folder) == TNY_FOLDER_TYPE_OUTBOX);
1066 priv->header_folder_id = tny_folder_get_id(header_folder);
1067 g_assert(priv->header_folder_id != NULL);
1068 g_object_unref(header_folder);
1072 /* Setup row references and connect signals */
1073 priv->header_model = g_object_ref (model);
1075 if (row_reference) {
1076 priv->row_reference = gtk_tree_row_reference_copy (row_reference);
1077 priv->next_row_reference = gtk_tree_row_reference_copy (row_reference);
1078 select_next_valid_row (model, &(priv->next_row_reference), TRUE, priv->is_outbox);
1080 priv->row_reference = NULL;
1081 priv->next_row_reference = NULL;
1084 /* Connect signals */
1085 priv->row_changed_handler =
1086 g_signal_connect (GTK_TREE_MODEL(model), "row-changed",
1087 G_CALLBACK(modest_msg_view_window_on_row_changed),
1089 priv->row_deleted_handler =
1090 g_signal_connect (GTK_TREE_MODEL(model), "row-deleted",
1091 G_CALLBACK(modest_msg_view_window_on_row_deleted),
1093 priv->row_inserted_handler =
1094 g_signal_connect (GTK_TREE_MODEL(model), "row-inserted",
1095 G_CALLBACK(modest_msg_view_window_on_row_inserted),
1097 priv->rows_reordered_handler =
1098 g_signal_connect(GTK_TREE_MODEL(model), "rows-reordered",
1099 G_CALLBACK(modest_msg_view_window_on_row_reordered),
1102 if (header_view != NULL){
1103 modest_header_view_add_observer(header_view,
1104 MODEST_HEADER_VIEW_OBSERVER(window));
1107 tny_msg_view_set_msg (TNY_MSG_VIEW (priv->msg_view), msg);
1108 update_window_title (MODEST_MSG_VIEW_WINDOW (window));
1109 gtk_widget_show_all (GTK_WIDGET (window));
1110 modest_msg_view_window_update_priority (window);
1112 /* Check dimming rules */
1113 modest_ui_actions_check_toolbar_dimming_rules (MODEST_WINDOW (window));
1114 modest_ui_actions_check_menu_dimming_rules (MODEST_WINDOW (window));
1115 modest_window_check_dimming_rules_group (MODEST_WINDOW (window), MODEST_DIMMING_RULES_CLIPBOARD);
1117 return MODEST_WINDOW(window);
1121 modest_msg_view_window_new_for_search_result (TnyMsg *msg,
1122 const gchar *modest_account_name,
1123 const gchar *mailbox, /*ignored*/
1124 const gchar *msg_uid)
1126 ModestMsgViewWindow *window = NULL;
1127 ModestMsgViewWindowPrivate *priv = NULL;
1128 ModestWindowMgr *mgr = NULL;
1130 mgr = modest_runtime_get_window_mgr ();
1131 window = MODEST_MSG_VIEW_WINDOW (modest_window_mgr_get_msg_view_window (mgr));
1132 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), NULL);
1133 modest_msg_view_window_construct (window, modest_account_name, msg_uid);
1135 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1137 /* Remember that this is a search result,
1138 * so we can disable some UI appropriately: */
1139 priv->is_search_result = TRUE;
1141 tny_msg_view_set_msg (TNY_MSG_VIEW (priv->msg_view), msg);
1143 update_window_title (window);
1144 gtk_widget_show_all (GTK_WIDGET (window));
1145 modest_msg_view_window_update_priority (window);
1147 /* Check dimming rules */
1148 modest_ui_actions_check_toolbar_dimming_rules (MODEST_WINDOW (window));
1149 modest_ui_actions_check_menu_dimming_rules (MODEST_WINDOW (window));
1150 modest_window_check_dimming_rules_group (MODEST_WINDOW (window), MODEST_DIMMING_RULES_CLIPBOARD);
1152 return MODEST_WINDOW(window);
1156 modest_msg_view_window_is_other_body (ModestMsgViewWindow *self)
1158 ModestMsgViewWindowPrivate *priv = NULL;
1160 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self), FALSE);
1161 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
1163 return (priv->other_body != NULL);
1167 modest_msg_view_window_new_with_other_body (TnyMsg *msg,
1168 TnyMimePart *other_body,
1169 const gchar *modest_account_name,
1170 const gchar *mailbox, /* ignored */
1171 const gchar *msg_uid)
1173 GObject *obj = NULL;
1174 ModestMsgViewWindowPrivate *priv;
1175 ModestWindowMgr *mgr = NULL;
1177 g_return_val_if_fail (msg, NULL);
1178 mgr = modest_runtime_get_window_mgr ();
1179 obj = G_OBJECT (modest_window_mgr_get_msg_view_window (mgr));
1180 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (obj);
1181 modest_msg_view_window_construct (MODEST_MSG_VIEW_WINDOW (obj),
1182 modest_account_name, msg_uid);
1185 priv->other_body = g_object_ref (other_body);
1186 modest_msg_view_set_msg_with_other_body (MODEST_MSG_VIEW (priv->msg_view), msg, other_body);
1188 tny_msg_view_set_msg (TNY_MSG_VIEW (priv->msg_view), msg);
1190 update_window_title (MODEST_MSG_VIEW_WINDOW (obj));
1192 gtk_widget_show_all (GTK_WIDGET (obj));
1194 /* Check dimming rules */
1195 modest_ui_actions_check_toolbar_dimming_rules (MODEST_WINDOW (obj));
1196 modest_ui_actions_check_menu_dimming_rules (MODEST_WINDOW (obj));
1197 modest_window_check_dimming_rules_group (MODEST_WINDOW (obj), MODEST_DIMMING_RULES_CLIPBOARD);
1199 return MODEST_WINDOW(obj);
1203 modest_msg_view_window_new_for_attachment (TnyMsg *msg,
1204 const gchar *modest_account_name,
1205 const gchar *mailbox, /* ignored */
1206 const gchar *msg_uid)
1208 return modest_msg_view_window_new_with_other_body (msg, NULL, modest_account_name, mailbox, msg_uid);
1213 modest_msg_view_window_on_row_changed (GtkTreeModel *header_model,
1216 ModestMsgViewWindow *window)
1218 check_dimming_rules_after_change (window);
1222 modest_msg_view_window_on_row_deleted(GtkTreeModel *header_model,
1224 ModestMsgViewWindow *window)
1226 check_dimming_rules_after_change (window);
1228 /* The window could have dissapeared */
1231 check_dimming_rules_after_change (ModestMsgViewWindow *window)
1233 modest_ui_actions_check_menu_dimming_rules (MODEST_WINDOW (window));
1234 modest_ui_actions_check_toolbar_dimming_rules (MODEST_WINDOW (window));
1238 /* On insertions we check if the folder still has the message we are
1239 * showing or do not. If do not, we do nothing. Which means we are still
1240 * not attached to any header folder and thus next/prev buttons are
1241 * still dimmed. Once the message that is shown by msg-view is found, the
1242 * new model of header-view will be attached and the references will be set.
1243 * On each further insertions dimming rules will be checked. However
1244 * this requires extra CPU time at least works.
1245 * (An message might be deleted from TnyFolder and thus will not be
1246 * inserted into the model again for example if it is removed by the
1247 * imap server and the header view is refreshed.)
1250 modest_msg_view_window_on_row_inserted (GtkTreeModel *model,
1251 GtkTreePath *tree_path,
1252 GtkTreeIter *tree_iter,
1253 ModestMsgViewWindow *window)
1255 ModestMsgViewWindowPrivate *priv = NULL;
1256 TnyHeader *header = NULL;
1258 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window));
1259 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1261 g_assert (model == priv->header_model);
1263 /* Check if the newly inserted message is the same we are actually
1264 * showing. IF not, we should remain detached from the header model
1265 * and thus prev and next toolbar buttons should remain dimmed. */
1266 gtk_tree_model_get (model, tree_iter,
1267 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
1270 if (TNY_IS_HEADER (header)) {
1273 uid = modest_tny_folder_get_header_unique_id (header);
1274 if (!g_str_equal(priv->msg_uid, uid)) {
1275 check_dimming_rules_after_change (window);
1277 g_object_unref (G_OBJECT(header));
1281 g_object_unref(G_OBJECT(header));
1284 if (priv->row_reference) {
1285 gtk_tree_row_reference_free (priv->row_reference);
1288 /* Setup row_reference for the actual msg. */
1289 priv->row_reference = gtk_tree_row_reference_new (priv->header_model, tree_path);
1290 if (priv->row_reference == NULL) {
1291 g_warning("No reference for msg header item.");
1295 /* Now set up next_row_reference. */
1296 if (priv->next_row_reference) {
1297 gtk_tree_row_reference_free (priv->next_row_reference);
1300 priv->next_row_reference = gtk_tree_row_reference_copy (priv->row_reference);
1301 select_next_valid_row (priv->header_model,
1302 &(priv->next_row_reference), FALSE, priv->is_outbox);
1304 /* Connect the remaining callbacks to become able to detect
1305 * changes in header-view. */
1306 priv->row_changed_handler =
1307 g_signal_connect (priv->header_model, "row-changed",
1308 G_CALLBACK (modest_msg_view_window_on_row_changed),
1310 priv->row_deleted_handler =
1311 g_signal_connect (priv->header_model, "row-deleted",
1312 G_CALLBACK (modest_msg_view_window_on_row_deleted),
1314 priv->rows_reordered_handler =
1315 g_signal_connect (priv->header_model, "rows-reordered",
1316 G_CALLBACK (modest_msg_view_window_on_row_reordered),
1319 check_dimming_rules_after_change (window);
1323 modest_msg_view_window_on_row_reordered (GtkTreeModel *header_model,
1327 ModestMsgViewWindow *window)
1329 ModestMsgViewWindowPrivate *priv = NULL;
1330 gboolean already_changed = FALSE;
1332 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(window);
1334 /* If the current row was reordered select the proper next
1335 valid row. The same if the next row reference changes */
1336 if (priv->row_reference &&
1337 gtk_tree_row_reference_valid (priv->row_reference)) {
1339 path = gtk_tree_row_reference_get_path (priv->row_reference);
1340 if (gtk_tree_path_compare (path, arg1) == 0) {
1341 if (priv->next_row_reference) {
1342 gtk_tree_row_reference_free (priv->next_row_reference);
1344 priv->next_row_reference = gtk_tree_row_reference_copy (priv->row_reference);
1345 select_next_valid_row (header_model, &(priv->next_row_reference), FALSE, priv->is_outbox);
1346 already_changed = TRUE;
1348 gtk_tree_path_free (path);
1350 if (!already_changed &&
1351 priv->next_row_reference &&
1352 gtk_tree_row_reference_valid (priv->next_row_reference)) {
1354 path = gtk_tree_row_reference_get_path (priv->next_row_reference);
1355 if (gtk_tree_path_compare (path, arg1) == 0) {
1356 if (priv->next_row_reference) {
1357 gtk_tree_row_reference_free (priv->next_row_reference);
1359 priv->next_row_reference = gtk_tree_row_reference_copy (priv->row_reference);
1360 select_next_valid_row (header_model, &(priv->next_row_reference), FALSE, priv->is_outbox);
1362 gtk_tree_path_free (path);
1364 check_dimming_rules_after_change (window);
1367 /* The modest_msg_view_window_update_model_replaced implements update
1368 * function for ModestHeaderViewObserver. Checks whether the TnyFolder
1369 * actually belongs to the header-view is the same as the TnyFolder of
1370 * the message of msg-view or not. If they are different, there is
1371 * nothing to do. If they are the same, then the model has replaced and
1372 * the reference in msg-view shall be replaced from the old model to
1373 * the new model. In this case the view will be detached from it's
1374 * header folder. From this point the next/prev buttons are dimmed.
1377 modest_msg_view_window_update_model_replaced (ModestHeaderViewObserver *observer,
1378 GtkTreeModel *model,
1379 const gchar *tny_folder_id)
1381 ModestMsgViewWindowPrivate *priv = NULL;
1382 ModestMsgViewWindow *window = NULL;
1384 g_assert(MODEST_IS_HEADER_VIEW_OBSERVER(observer));
1385 g_assert(MODEST_IS_MSG_VIEW_WINDOW(observer));
1387 window = MODEST_MSG_VIEW_WINDOW(observer);
1388 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(window);
1390 /* If there is an other folder in the header-view then we do
1391 * not care about it's model (msg list). Else if the
1392 * header-view shows the folder the msg shown by us is in, we
1393 * shall replace our model reference and make some check. */
1394 if(model == NULL || tny_folder_id == NULL ||
1395 (priv->header_folder_id && !g_str_equal(tny_folder_id, priv->header_folder_id)))
1398 /* Model is changed(replaced), so we should forget the old
1399 * one. Because there might be other references and there
1400 * might be some change on the model even if we unreferenced
1401 * it, we need to disconnect our signals here. */
1402 if (priv->header_model) {
1403 if (g_signal_handler_is_connected(G_OBJECT (priv->header_model),
1404 priv->row_changed_handler))
1405 g_signal_handler_disconnect(G_OBJECT (priv->header_model),
1406 priv->row_changed_handler);
1407 if (g_signal_handler_is_connected(G_OBJECT (priv->header_model),
1408 priv->row_deleted_handler))
1409 g_signal_handler_disconnect(G_OBJECT (priv->header_model),
1410 priv->row_deleted_handler);
1411 if (g_signal_handler_is_connected(G_OBJECT (priv->header_model),
1412 priv->row_inserted_handler))
1413 g_signal_handler_disconnect(G_OBJECT (priv->header_model),
1414 priv->row_inserted_handler);
1415 if (g_signal_handler_is_connected(G_OBJECT (priv->header_model),
1416 priv->rows_reordered_handler))
1417 g_signal_handler_disconnect(G_OBJECT (priv->header_model),
1418 priv->rows_reordered_handler);
1421 if (priv->row_reference)
1422 gtk_tree_row_reference_free (priv->row_reference);
1423 if (priv->next_row_reference)
1424 gtk_tree_row_reference_free (priv->next_row_reference);
1425 g_object_unref(priv->header_model);
1428 priv->row_changed_handler = 0;
1429 priv->row_deleted_handler = 0;
1430 priv->row_inserted_handler = 0;
1431 priv->rows_reordered_handler = 0;
1432 priv->next_row_reference = NULL;
1433 priv->row_reference = NULL;
1434 priv->header_model = NULL;
1437 priv->header_model = g_object_ref (model);
1439 /* Also we must connect to the new model for row insertions.
1440 * Only for insertions now. We will need other ones only after
1441 * the msg is show by msg-view is added to the new model. */
1442 priv->row_inserted_handler =
1443 g_signal_connect (priv->header_model, "row-inserted",
1444 G_CALLBACK(modest_msg_view_window_on_row_inserted),
1447 modest_ui_actions_check_menu_dimming_rules(MODEST_WINDOW(window));
1448 modest_ui_actions_check_toolbar_dimming_rules(MODEST_WINDOW(window));
1452 modest_msg_view_window_toolbar_on_transfer_mode (ModestMsgViewWindow *self)
1454 ModestMsgViewWindowPrivate *priv= NULL;
1456 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self), FALSE);
1457 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
1459 return priv->current_toolbar_mode == TOOLBAR_MODE_TRANSFER;
1463 modest_msg_view_window_get_header (ModestMsgViewWindow *self)
1465 ModestMsgViewWindowPrivate *priv= NULL;
1467 TnyHeader *header = NULL;
1468 GtkTreePath *path = NULL;
1471 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self), NULL);
1472 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
1474 /* If the message was not obtained from a treemodel,
1475 * for instance if it was opened directly by the search UI:
1477 if (priv->header_model == NULL ||
1478 priv->row_reference == NULL ||
1479 !gtk_tree_row_reference_valid (priv->row_reference)) {
1480 msg = modest_msg_view_window_get_message (self);
1482 header = tny_msg_get_header (msg);
1483 g_object_unref (msg);
1488 /* Get iter of the currently selected message in the header view: */
1489 path = gtk_tree_row_reference_get_path (priv->row_reference);
1490 g_return_val_if_fail (path != NULL, NULL);
1491 gtk_tree_model_get_iter (priv->header_model,
1495 /* Get current message header */
1496 gtk_tree_model_get (priv->header_model, &iter,
1497 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
1500 gtk_tree_path_free (path);
1505 modest_msg_view_window_get_message (ModestMsgViewWindow *self)
1507 ModestMsgViewWindowPrivate *priv;
1509 g_return_val_if_fail (self, NULL);
1511 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(self);
1513 return tny_msg_view_get_msg (TNY_MSG_VIEW (priv->msg_view));
1517 modest_msg_view_window_get_message_uid (ModestMsgViewWindow *self)
1519 ModestMsgViewWindowPrivate *priv;
1521 g_return_val_if_fail (self, NULL);
1523 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
1525 return (const gchar*) priv->msg_uid;
1529 modest_msg_view_window_toggle_find_toolbar (GtkToggleAction *toggle,
1532 ModestMsgViewWindow *window = MODEST_MSG_VIEW_WINDOW (data);
1533 ModestMsgViewWindowPrivate *priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1534 ModestWindowPrivate *parent_priv = MODEST_WINDOW_GET_PRIVATE (window);
1538 is_active = gtk_toggle_action_get_active (toggle);
1541 gtk_widget_show (priv->find_toolbar);
1542 hildon_find_toolbar_highlight_entry (HILDON_FIND_TOOLBAR (priv->find_toolbar), TRUE);
1544 gtk_widget_hide (priv->find_toolbar);
1545 modest_msg_view_grab_focus (MODEST_MSG_VIEW (priv->msg_view));
1548 /* update the toggle buttons status */
1549 action = gtk_ui_manager_get_action (parent_priv->ui_manager, "/ToolBar/FindInMessage");
1550 modest_utils_toggle_action_set_active_block_notify (GTK_TOGGLE_ACTION (action), is_active);
1551 action = gtk_ui_manager_get_action (parent_priv->ui_manager, "/MenuBar/ToolsMenu/ToolsFindInMessageMenu");
1552 modest_utils_toggle_action_set_active_block_notify (GTK_TOGGLE_ACTION (action), is_active);
1557 modest_msg_view_window_find_toolbar_close (GtkWidget *widget,
1558 ModestMsgViewWindow *obj)
1560 GtkToggleAction *toggle;
1561 ModestWindowPrivate *parent_priv;
1562 ModestMsgViewWindowPrivate *priv;
1564 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (obj);
1565 parent_priv = MODEST_WINDOW_GET_PRIVATE (obj);
1567 toggle = GTK_TOGGLE_ACTION (gtk_ui_manager_get_action (parent_priv->ui_manager, "/ToolBar/FindInMessage"));
1568 gtk_toggle_action_set_active (toggle, FALSE);
1569 modest_msg_view_grab_focus (MODEST_MSG_VIEW (priv->msg_view));
1573 modest_msg_view_window_find_toolbar_search (GtkWidget *widget,
1574 ModestMsgViewWindow *obj)
1576 gchar *current_search;
1577 ModestMsgViewWindowPrivate *priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (obj);
1579 if (modest_mime_part_view_is_empty (MODEST_MIME_PART_VIEW (priv->msg_view))) {
1580 hildon_banner_show_information (NULL, NULL, _("mail_ib_nothing_to_find"));
1584 g_object_get (G_OBJECT (widget), "prefix", ¤t_search, NULL);
1586 if ((current_search == NULL) || (strcmp (current_search, "") == 0)) {
1587 g_free (current_search);
1588 hildon_banner_show_information (NULL, NULL, _CS("ecdg_ib_find_rep_enter_text"));
1592 if ((priv->last_search == NULL) || (strcmp (priv->last_search, current_search) != 0)) {
1594 g_free (priv->last_search);
1595 priv->last_search = g_strdup (current_search);
1596 result = modest_isearch_view_search (MODEST_ISEARCH_VIEW (priv->msg_view),
1599 hildon_banner_show_information (NULL, NULL, _HL("ckct_ib_find_no_matches"));
1600 g_free (priv->last_search);
1601 priv->last_search = NULL;
1603 modest_msg_view_grab_focus (MODEST_MSG_VIEW (priv->msg_view));
1604 hildon_find_toolbar_highlight_entry (HILDON_FIND_TOOLBAR (priv->find_toolbar), TRUE);
1607 if (!modest_isearch_view_search_next (MODEST_ISEARCH_VIEW (priv->msg_view))) {
1608 hildon_banner_show_information (NULL, NULL, _HL("ckct_ib_find_search_complete"));
1609 g_free (priv->last_search);
1610 priv->last_search = NULL;
1612 modest_msg_view_grab_focus (MODEST_MSG_VIEW (priv->msg_view));
1613 hildon_find_toolbar_highlight_entry (HILDON_FIND_TOOLBAR (priv->find_toolbar), TRUE);
1617 g_free (current_search);
1622 modest_msg_view_window_set_zoom (ModestWindow *window,
1625 ModestMsgViewWindowPrivate *priv;
1626 ModestWindowPrivate *parent_priv;
1627 GtkAction *action = NULL;
1628 gint int_zoom = (gint) rint (zoom*100.0+0.1);
1630 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window));
1632 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1633 parent_priv = MODEST_WINDOW_GET_PRIVATE (window);
1634 modest_zoomable_set_zoom (MODEST_ZOOMABLE (priv->msg_view), zoom);
1636 action = gtk_ui_manager_get_action (parent_priv->ui_manager,
1637 "/MenuBar/ViewMenu/ZoomMenu/Zoom50Menu");
1639 gtk_radio_action_set_current_value (GTK_RADIO_ACTION (action), int_zoom);
1643 modest_msg_view_window_get_zoom (ModestWindow *window)
1645 ModestMsgViewWindowPrivate *priv;
1647 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), 1.0);
1649 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1650 return modest_zoomable_get_zoom (MODEST_ZOOMABLE (priv->msg_view));
1654 modest_msg_view_window_zoom_plus (ModestWindow *window)
1656 ModestWindowPrivate *parent_priv;
1657 GtkRadioAction *zoom_radio_action;
1658 GSList *group, *node;
1660 parent_priv = MODEST_WINDOW_GET_PRIVATE (window);
1661 zoom_radio_action = GTK_RADIO_ACTION (gtk_ui_manager_get_action (parent_priv->ui_manager,
1662 "/MenuBar/ViewMenu/ZoomMenu/Zoom50Menu"));
1664 group = gtk_radio_action_get_group (zoom_radio_action);
1666 if (gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (group->data))) {
1667 hildon_banner_show_information (NULL, NULL, _CS("ckct_ib_max_zoom_level_reached"));
1671 for (node = group; node != NULL; node = g_slist_next (node)) {
1672 if ((node->next != NULL) && gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (node->next->data))) {
1673 gtk_toggle_action_set_active (GTK_TOGGLE_ACTION (node->data), TRUE);
1681 modest_msg_view_window_zoom_minus (ModestWindow *window)
1683 ModestWindowPrivate *parent_priv;
1684 GtkRadioAction *zoom_radio_action;
1685 GSList *group, *node;
1687 parent_priv = MODEST_WINDOW_GET_PRIVATE (window);
1688 zoom_radio_action = GTK_RADIO_ACTION (gtk_ui_manager_get_action (parent_priv->ui_manager,
1689 "/MenuBar/ViewMenu/ZoomMenu/Zoom50Menu"));
1691 group = gtk_radio_action_get_group (zoom_radio_action);
1693 for (node = group; node != NULL; node = g_slist_next (node)) {
1694 if (gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (node->data))) {
1695 if (node->next != NULL) {
1696 gtk_toggle_action_set_active (GTK_TOGGLE_ACTION (node->next->data), TRUE);
1699 hildon_banner_show_information (NULL, NULL,
1700 _CS("ckct_ib_min_zoom_level_reached"));
1710 modest_msg_view_window_key_event (GtkWidget *window,
1716 focus = gtk_window_get_focus (GTK_WINDOW (window));
1718 /* for the find toolbar case */
1719 if (focus && GTK_IS_ENTRY (focus)) {
1720 if (event->keyval == GDK_BackSpace) {
1722 copy = gdk_event_copy ((GdkEvent *) event);
1723 gtk_widget_event (focus, copy);
1724 gdk_event_free (copy);
1729 if (event->keyval == GDK_Up || event->keyval == GDK_KP_Up ||
1730 event->keyval == GDK_Down || event->keyval == GDK_KP_Down ||
1731 event->keyval == GDK_Page_Up || event->keyval == GDK_KP_Page_Up ||
1732 event->keyval == GDK_Page_Down || event->keyval == GDK_KP_Page_Down ||
1733 event->keyval == GDK_Home || event->keyval == GDK_KP_Home ||
1734 event->keyval == GDK_End || event->keyval == GDK_KP_End) {
1735 /* ModestMsgViewWindowPrivate *priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window); */
1736 /* gboolean return_value; */
1738 if (event->type == GDK_KEY_PRESS) {
1739 GtkScrollType scroll_type;
1741 switch (event->keyval) {
1744 scroll_type = GTK_SCROLL_STEP_UP; break;
1747 scroll_type = GTK_SCROLL_STEP_DOWN; break;
1749 case GDK_KP_Page_Up:
1750 scroll_type = GTK_SCROLL_PAGE_UP; break;
1752 case GDK_KP_Page_Down:
1753 scroll_type = GTK_SCROLL_PAGE_DOWN; break;
1756 scroll_type = GTK_SCROLL_START; break;
1759 scroll_type = GTK_SCROLL_END; break;
1760 default: scroll_type = GTK_SCROLL_NONE;
1763 /* g_signal_emit_by_name (G_OBJECT (priv->main_scroll), "scroll-child", */
1764 /* scroll_type, FALSE, &return_value); */
1775 modest_msg_view_window_last_message_selected (ModestMsgViewWindow *window)
1778 ModestMsgViewWindowPrivate *priv;
1779 GtkTreeIter tmp_iter;
1780 gboolean is_last_selected;
1782 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), TRUE);
1783 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1785 /*if no model (so no rows at all), then virtually we are the last*/
1786 if (!priv->header_model || !priv->row_reference)
1789 if (!gtk_tree_row_reference_valid (priv->row_reference))
1792 path = gtk_tree_row_reference_get_path (priv->row_reference);
1796 is_last_selected = TRUE;
1797 while (is_last_selected) {
1799 gtk_tree_path_next (path);
1800 if (!gtk_tree_model_get_iter (priv->header_model, &tmp_iter, path))
1802 gtk_tree_model_get (priv->header_model, &tmp_iter,
1803 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
1806 if (msg_is_visible (header, priv->is_outbox))
1807 is_last_selected = FALSE;
1808 g_object_unref(G_OBJECT(header));
1811 gtk_tree_path_free (path);
1812 return is_last_selected;
1816 modest_msg_view_window_has_headers_model (ModestMsgViewWindow *window)
1818 ModestMsgViewWindowPrivate *priv;
1820 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), TRUE);
1821 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1823 return priv->header_model != NULL;
1827 modest_msg_view_window_is_search_result (ModestMsgViewWindow *window)
1829 ModestMsgViewWindowPrivate *priv;
1831 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), TRUE);
1832 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1834 return priv->is_search_result;
1838 msg_is_visible (TnyHeader *header, gboolean check_outbox)
1840 if ((tny_header_get_flags(header) & TNY_HEADER_FLAG_DELETED))
1842 if (!check_outbox) {
1845 ModestTnySendQueueStatus status;
1846 status = modest_tny_all_send_queues_get_msg_status (header);
1847 return ((status != MODEST_TNY_SEND_QUEUE_FAILED) &&
1848 (status != MODEST_TNY_SEND_QUEUE_SENDING));
1853 modest_msg_view_window_first_message_selected (ModestMsgViewWindow *window)
1856 ModestMsgViewWindowPrivate *priv;
1857 gboolean is_first_selected;
1858 GtkTreeIter tmp_iter;
1860 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), TRUE);
1861 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1863 /*if no model (so no rows at all), then virtually we are the first*/
1864 if (!priv->header_model || !priv->row_reference)
1867 if (!gtk_tree_row_reference_valid (priv->row_reference))
1870 path = gtk_tree_row_reference_get_path (priv->row_reference);
1874 is_first_selected = TRUE;
1875 while (is_first_selected) {
1877 if(!gtk_tree_path_prev (path))
1879 /* Here the 'if' is needless for logic, but let make sure
1880 * iter is valid for gtk_tree_model_get. */
1881 if (!gtk_tree_model_get_iter (priv->header_model, &tmp_iter, path))
1883 gtk_tree_model_get (priv->header_model, &tmp_iter,
1884 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
1887 if (msg_is_visible (header, priv->is_outbox))
1888 is_first_selected = FALSE;
1889 g_object_unref(G_OBJECT(header));
1892 gtk_tree_path_free (path);
1893 return is_first_selected;
1900 GtkTreeRowReference *row_reference;
1904 message_reader_performer (gboolean canceled,
1906 GtkWindow *parent_window,
1907 TnyAccount *account,
1910 ModestMailOperation *mail_op = NULL;
1911 MsgReaderInfo *info;
1913 info = (MsgReaderInfo *) user_data;
1914 if (canceled || err) {
1918 /* Register the header - it'll be unregistered in the callback */
1920 modest_window_mgr_register_header (modest_runtime_get_window_mgr (), info->header, NULL);
1922 /* New mail operation */
1923 mail_op = modest_mail_operation_new_with_error_handling (G_OBJECT(parent_window),
1924 modest_ui_actions_disk_operations_error_handler,
1927 modest_mail_operation_queue_add (modest_runtime_get_mail_operation_queue (), mail_op);
1929 modest_mail_operation_get_msg (mail_op, info->header, TRUE, view_msg_cb, info->row_reference);
1931 modest_mail_operation_find_msg (mail_op, info->folder, info->uid, TRUE, view_msg_cb);
1932 g_object_unref (mail_op);
1934 /* Update dimming rules */
1935 modest_ui_actions_check_toolbar_dimming_rules (MODEST_WINDOW (parent_window));
1936 modest_ui_actions_check_menu_dimming_rules (MODEST_WINDOW (parent_window));
1939 /* Frees. The row_reference will be freed by the view_msg_cb callback */
1942 g_object_unref (info->header);
1944 g_object_unref (info->folder);
1945 g_slice_free (MsgReaderInfo, info);
1950 * Reads the message whose summary item is @header. It takes care of
1951 * several things, among others:
1953 * If the message was not previously downloaded then ask the user
1954 * before downloading. If there is no connection launch the connection
1955 * dialog. Update toolbar dimming rules.
1957 * Returns: TRUE if the mail operation was started, otherwise if the
1958 * user do not want to download the message, or if the user do not
1959 * want to connect, then the operation is not issued
1962 message_reader (ModestMsgViewWindow *window,
1963 ModestMsgViewWindowPrivate *priv,
1965 const gchar *msg_uid,
1967 GtkTreeRowReference *row_reference)
1969 gboolean already_showing = FALSE;
1970 ModestWindow *msg_window = NULL;
1971 ModestWindowMgr *mgr;
1972 TnyAccount *account;
1973 MsgReaderInfo *info;
1975 mgr = modest_runtime_get_window_mgr ();
1976 already_showing = header && modest_window_mgr_find_registered_header (mgr, header, &msg_window);
1977 if (already_showing && (msg_window != MODEST_WINDOW (window))) {
1980 gtk_window_present (GTK_WINDOW (msg_window));
1981 g_signal_emit_by_name (G_OBJECT (window), "delete-event", NULL, &retval);
1986 g_object_ref (folder);
1989 /* Msg download completed */
1990 if (!header || !(tny_header_get_flags (header) & TNY_HEADER_FLAG_CACHED)) {
1991 /* Ask the user if he wants to download the message if
1993 if (!tny_device_is_online (modest_runtime_get_device())) {
1994 GtkResponseType response;
1996 response = modest_platform_run_confirmation_dialog (GTK_WINDOW (window),
1997 _("mcen_nc_get_msg"));
1998 if (response == GTK_RESPONSE_CANCEL)
2002 if (folder) g_object_unref (folder);
2003 folder = tny_header_get_folder (header);
2005 info = g_slice_new (MsgReaderInfo);
2006 info->msg_uid = g_strdup (msg_uid);
2008 info->header = g_object_ref (header);
2010 info->header = NULL;
2012 info->row_reference = gtk_tree_row_reference_copy (row_reference);
2014 info->row_reference = NULL;
2016 /* Offer the connection dialog if necessary */
2017 modest_platform_connect_if_remote_and_perform ((GtkWindow *) window,
2019 TNY_FOLDER_STORE (folder),
2020 message_reader_performer,
2022 g_object_unref (folder);
2028 if (folder) g_object_unref (folder);
2029 folder = tny_header_get_folder (header);
2031 account = tny_folder_get_account (folder);
2032 info = g_slice_new (MsgReaderInfo);
2033 info->msg_uid = g_strdup (msg_uid);
2035 info->header = g_object_ref (header);
2037 info->header = NULL;
2039 info->row_reference = gtk_tree_row_reference_copy (row_reference);
2041 row_reference = NULL;
2043 message_reader_performer (FALSE, NULL, (GtkWindow *) window, account, info);
2044 g_object_unref (account);
2045 g_object_unref (folder);
2051 modest_msg_view_window_select_next_message (ModestMsgViewWindow *window)
2053 ModestMsgViewWindowPrivate *priv;
2054 GtkTreePath *path= NULL;
2055 GtkTreeIter tmp_iter;
2057 gboolean retval = TRUE;
2058 GtkTreeRowReference *row_reference = NULL;
2060 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), FALSE);
2061 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
2063 if (!priv->row_reference)
2066 /* Update the next row reference if it's not valid. This could
2067 happen if for example the header which it was pointing to,
2068 was deleted. The best place to do it is in the row-deleted
2069 handler but the tinymail model do not work like the glib
2070 tree models and reports the deletion when the row is still
2072 if (!gtk_tree_row_reference_valid (priv->next_row_reference)) {
2073 if (gtk_tree_row_reference_valid (priv->row_reference)) {
2074 priv->next_row_reference = gtk_tree_row_reference_copy (priv->row_reference);
2075 select_next_valid_row (priv->header_model, &(priv->next_row_reference), FALSE, priv->is_outbox);
2078 if (priv->next_row_reference)
2079 path = gtk_tree_row_reference_get_path (priv->next_row_reference);
2083 row_reference = gtk_tree_row_reference_copy (priv->next_row_reference);
2085 gtk_tree_model_get_iter (priv->header_model,
2088 gtk_tree_path_free (path);
2090 gtk_tree_model_get (priv->header_model, &tmp_iter,
2091 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
2094 /* Read the message & show it */
2095 if (!message_reader (window, priv, header, NULL, NULL, row_reference)) {
2098 gtk_tree_row_reference_free (row_reference);
2101 g_object_unref (header);
2107 modest_msg_view_window_select_previous_message (ModestMsgViewWindow *window)
2109 ModestMsgViewWindowPrivate *priv = NULL;
2111 gboolean finished = FALSE;
2112 gboolean retval = FALSE;
2114 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), FALSE);
2115 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
2117 /* Return inmediatly if there is no header model */
2118 if (!priv->header_model || !priv->row_reference)
2121 path = gtk_tree_row_reference_get_path (priv->row_reference);
2122 while (!finished && gtk_tree_path_prev (path)) {
2126 gtk_tree_model_get_iter (priv->header_model, &iter, path);
2127 gtk_tree_model_get (priv->header_model, &iter,
2128 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
2132 if (msg_is_visible (header, priv->is_outbox)) {
2133 GtkTreeRowReference *row_reference;
2134 row_reference = gtk_tree_row_reference_new (priv->header_model, path);
2135 /* Read the message & show it */
2136 retval = message_reader (window, priv, header, NULL, NULL, row_reference);
2137 gtk_tree_row_reference_free (row_reference);
2141 g_object_unref (header);
2145 gtk_tree_path_free (path);
2150 view_msg_cb (ModestMailOperation *mail_op,
2157 ModestMsgViewWindow *self = NULL;
2158 ModestMsgViewWindowPrivate *priv = NULL;
2159 GtkTreeRowReference *row_reference = NULL;
2161 /* Unregister the header (it was registered before creating the mail operation) */
2162 modest_window_mgr_unregister_header (modest_runtime_get_window_mgr (), header);
2164 row_reference = (GtkTreeRowReference *) user_data;
2167 gtk_tree_row_reference_free (row_reference);
2171 /* If there was any error */
2172 if (!modest_ui_actions_msg_retrieval_check (mail_op, header, msg)) {
2174 gtk_tree_row_reference_free (row_reference);
2178 /* Get the window */
2179 self = (ModestMsgViewWindow *) modest_mail_operation_get_source (mail_op);
2180 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self));
2181 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
2183 /* Update the row reference */
2184 if (priv->row_reference != NULL) {
2185 gtk_tree_row_reference_free (priv->row_reference);
2186 priv->row_reference = gtk_tree_row_reference_copy (row_reference);
2187 if (priv->next_row_reference != NULL) {
2188 gtk_tree_row_reference_free (priv->next_row_reference);
2190 priv->next_row_reference = gtk_tree_row_reference_copy (priv->row_reference);
2191 select_next_valid_row (priv->header_model, &(priv->next_row_reference), TRUE, priv->is_outbox);
2194 /* Mark header as read */
2195 if (!(tny_header_get_flags (header) & TNY_HEADER_FLAG_SEEN))
2196 tny_header_set_flag (header, TNY_HEADER_FLAG_SEEN);
2198 /* Set new message */
2199 if (priv->msg_view != NULL && TNY_IS_MSG_VIEW (priv->msg_view)) {
2200 tny_msg_view_set_msg (TNY_MSG_VIEW (priv->msg_view), msg);
2201 modest_msg_view_window_update_priority (self);
2202 update_window_title (MODEST_MSG_VIEW_WINDOW (self));
2203 modest_msg_view_grab_focus (MODEST_MSG_VIEW (priv->msg_view));
2206 /* Set the new message uid of the window */
2207 if (priv->msg_uid) {
2208 g_free (priv->msg_uid);
2209 priv->msg_uid = modest_tny_folder_get_header_unique_id (header);
2212 /* Notify the observers */
2213 g_signal_emit (G_OBJECT (self), signals[MSG_CHANGED_SIGNAL],
2214 0, priv->header_model, priv->row_reference);
2217 g_object_unref (self);
2219 gtk_tree_row_reference_free (row_reference);
2223 modest_msg_view_window_get_folder_type (ModestMsgViewWindow *window)
2225 ModestMsgViewWindowPrivate *priv;
2227 TnyFolderType folder_type;
2229 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
2231 folder_type = TNY_FOLDER_TYPE_UNKNOWN;
2233 msg = tny_msg_view_get_msg (TNY_MSG_VIEW (priv->msg_view));
2237 folder = tny_msg_get_folder (msg);
2239 folder_type = modest_tny_folder_guess_folder_type (folder);
2240 g_object_unref (folder);
2242 g_object_unref (msg);
2250 modest_msg_view_window_update_priority (ModestMsgViewWindow *window)
2252 ModestMsgViewWindowPrivate *priv;
2253 TnyHeader *header = NULL;
2254 TnyHeaderFlags flags = 0;
2256 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
2258 if (priv->header_model && priv->row_reference) {
2260 GtkTreePath *path = NULL;
2262 path = gtk_tree_row_reference_get_path (priv->row_reference);
2263 g_return_if_fail (path != NULL);
2264 gtk_tree_model_get_iter (priv->header_model,
2266 gtk_tree_row_reference_get_path (priv->row_reference));
2268 gtk_tree_model_get (priv->header_model, &iter, TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
2270 gtk_tree_path_free (path);
2273 msg = tny_msg_view_get_msg (TNY_MSG_VIEW (priv->msg_view));
2275 header = tny_msg_get_header (msg);
2276 g_object_unref (msg);
2281 flags = tny_header_get_flags (header);
2282 g_object_unref(G_OBJECT(header));
2285 modest_msg_view_set_priority (MODEST_MSG_VIEW(priv->msg_view), flags);
2290 toolbar_resize (ModestMsgViewWindow *self)
2292 ModestMsgViewWindowPrivate *priv = NULL;
2293 ModestWindowPrivate *parent_priv = NULL;
2295 gint static_button_size;
2296 ModestWindowMgr *mgr;
2298 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self));
2299 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
2300 parent_priv = MODEST_WINDOW_GET_PRIVATE(self);
2302 mgr = modest_runtime_get_window_mgr ();
2303 static_button_size = modest_window_mgr_get_fullscreen_mode (mgr)?118:108;
2305 if (parent_priv->toolbar) {
2306 /* left size buttons */
2307 widget = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/ToolbarMessageReply");
2308 gtk_tool_item_set_expand (GTK_TOOL_ITEM (widget), FALSE);
2309 gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (widget), FALSE);
2310 gtk_widget_set_size_request (GTK_WIDGET (widget), static_button_size, -1);
2311 widget = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/ToolbarMessageMoveTo");
2312 gtk_tool_item_set_expand (GTK_TOOL_ITEM (widget), FALSE);
2313 gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (widget), FALSE);
2314 gtk_widget_set_size_request (GTK_WIDGET (widget), static_button_size, -1);
2315 widget = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/ToolbarDeleteMessage");
2316 gtk_tool_item_set_expand (GTK_TOOL_ITEM (widget), FALSE);
2317 gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (widget), FALSE);
2318 gtk_widget_set_size_request (GTK_WIDGET (widget), static_button_size, -1);
2319 widget = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/FindInMessage");
2320 gtk_tool_item_set_expand (GTK_TOOL_ITEM (widget), FALSE);
2321 gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (widget), FALSE);
2322 gtk_widget_set_size_request (GTK_WIDGET (widget), static_button_size, -1);
2324 gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (priv->progress_toolitem), FALSE);
2325 gtk_tool_item_set_expand (GTK_TOOL_ITEM (priv->progress_toolitem), TRUE);
2326 gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (priv->cancel_toolitem), FALSE);
2327 gtk_tool_item_set_expand (GTK_TOOL_ITEM (priv->cancel_toolitem), FALSE);
2328 gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (priv->next_toolitem), TRUE);
2329 gtk_tool_item_set_expand (GTK_TOOL_ITEM (priv->next_toolitem), TRUE);
2330 gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (priv->prev_toolitem), TRUE);
2331 gtk_tool_item_set_expand (GTK_TOOL_ITEM (priv->prev_toolitem), TRUE);
2337 modest_msg_view_window_window_state_event (GtkWidget *widget, GdkEventWindowState *event, gpointer userdata)
2339 if (event->changed_mask & GDK_WINDOW_STATE_FULLSCREEN) {
2340 ModestWindowPrivate *parent_priv;
2341 ModestWindowMgr *mgr;
2342 gboolean is_fullscreen;
2343 GtkAction *fs_toggle_action;
2346 mgr = modest_runtime_get_window_mgr ();
2347 is_fullscreen = (modest_window_mgr_get_fullscreen_mode (mgr))?1:0;
2349 parent_priv = MODEST_WINDOW_GET_PRIVATE (widget);
2351 fs_toggle_action = gtk_ui_manager_get_action (parent_priv->ui_manager, "/MenuBar/ViewMenu/ViewToggleFullscreenMenu");
2352 active = (gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (fs_toggle_action)))?1:0;
2353 if (is_fullscreen != active) {
2354 gtk_toggle_action_set_active (GTK_TOGGLE_ACTION (fs_toggle_action), is_fullscreen);
2356 toolbar_resize (MODEST_MSG_VIEW_WINDOW (widget));
2364 modest_msg_view_window_show_toolbar (ModestWindow *self,
2365 gboolean show_toolbar)
2367 ModestMsgViewWindowPrivate *priv = NULL;
2368 ModestWindowPrivate *parent_priv;
2369 GtkWidget *reply_button = NULL, *menu = NULL;
2370 GtkWidget *placeholder = NULL;
2372 const gchar *action_name;
2375 parent_priv = MODEST_WINDOW_GET_PRIVATE(self);
2376 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(self);
2378 /* Set optimized view status */
2379 priv->optimized_view = !show_toolbar;
2381 if (!parent_priv->toolbar) {
2382 parent_priv->toolbar = gtk_ui_manager_get_widget (parent_priv->ui_manager,
2384 gtk_widget_set_no_show_all (parent_priv->toolbar, TRUE);
2386 priv->progress_toolitem = GTK_WIDGET (gtk_tool_item_new ());
2387 priv->cancel_toolitem = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/ToolbarCancel");
2388 priv->next_toolitem = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/ToolbarMessageNext");
2389 priv->prev_toolitem = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/ToolbarMessageBack");
2390 toolbar_resize (MODEST_MSG_VIEW_WINDOW (self));
2392 /* Add ProgressBar (Transfer toolbar) */
2393 priv->progress_bar = modest_progress_bar_new ();
2394 gtk_widget_set_no_show_all (priv->progress_bar, TRUE);
2395 placeholder = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/ProgressbarView");
2396 insert_index = gtk_toolbar_get_item_index(GTK_TOOLBAR (parent_priv->toolbar), GTK_TOOL_ITEM(placeholder));
2397 gtk_container_add (GTK_CONTAINER (priv->progress_toolitem), priv->progress_bar);
2398 gtk_toolbar_insert(GTK_TOOLBAR(parent_priv->toolbar), GTK_TOOL_ITEM (priv->progress_toolitem), insert_index);
2400 /* Connect cancel 'clicked' signal to abort progress mode */
2401 g_signal_connect(priv->cancel_toolitem, "clicked",
2402 G_CALLBACK(cancel_progressbar),
2405 /* Add it to the observers list */
2406 priv->progress_widgets = g_slist_prepend(priv->progress_widgets, priv->progress_bar);
2409 hildon_window_add_toolbar (HILDON_WINDOW (self),
2410 GTK_TOOLBAR (parent_priv->toolbar));
2412 /* Set reply button tap and hold menu */
2413 reply_button = gtk_ui_manager_get_widget (parent_priv->ui_manager,
2414 "/ToolBar/ToolbarMessageReply");
2415 menu = gtk_ui_manager_get_widget (parent_priv->ui_manager,
2416 "/ToolbarReplyCSM");
2417 gtk_widget_tap_and_hold_setup (GTK_WIDGET (reply_button), menu, NULL, 0);
2421 /* Quick hack: this prevents toolbar icons "dance" when progress bar show status is changed */
2422 /* TODO: resize mode migth be GTK_RESIZE_QUEUE, in order to avoid unneccesary shows */
2423 gtk_container_set_resize_mode (GTK_CONTAINER(parent_priv->toolbar), GTK_RESIZE_IMMEDIATE);
2425 gtk_widget_show (GTK_WIDGET (parent_priv->toolbar));
2426 if (modest_msg_view_window_transfer_mode_enabled (MODEST_MSG_VIEW_WINDOW (self)))
2427 set_toolbar_mode (MODEST_MSG_VIEW_WINDOW (self), TOOLBAR_MODE_TRANSFER);
2429 set_toolbar_mode (MODEST_MSG_VIEW_WINDOW (self), TOOLBAR_MODE_NORMAL);
2432 gtk_widget_set_no_show_all (parent_priv->toolbar, TRUE);
2433 gtk_widget_hide (GTK_WIDGET (parent_priv->toolbar));
2436 /* Update also the actions (to update the toggles in the
2437 menus), we have to do it manually because some other window
2438 of the same time could have changed it (remember that the
2439 toolbar fullscreen mode is shared by all the windows of the
2441 if (modest_window_mgr_get_fullscreen_mode (modest_runtime_get_window_mgr ()))
2442 action_name = "/MenuBar/ViewMenu/ViewShowToolbarMenu/ViewShowToolbarFullScreenMenu";
2444 action_name = "/MenuBar/ViewMenu/ViewShowToolbarMenu/ViewShowToolbarNormalScreenMenu";
2446 action = gtk_ui_manager_get_action (parent_priv->ui_manager, action_name);
2447 modest_utils_toggle_action_set_active_block_notify (GTK_TOGGLE_ACTION (action),
2452 modest_msg_view_window_clipboard_owner_change (GtkClipboard *clipboard,
2454 ModestMsgViewWindow *window)
2456 if (!GTK_WIDGET_VISIBLE (window))
2459 modest_window_check_dimming_rules_group (MODEST_WINDOW (window), MODEST_DIMMING_RULES_CLIPBOARD);
2463 modest_msg_view_window_transfer_mode_enabled (ModestMsgViewWindow *self)
2465 ModestMsgViewWindowPrivate *priv;
2467 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self), FALSE);
2468 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(self);
2470 return priv->current_toolbar_mode == TOOLBAR_MODE_TRANSFER;
2474 cancel_progressbar (GtkToolButton *toolbutton,
2475 ModestMsgViewWindow *self)
2478 ModestMsgViewWindowPrivate *priv;
2480 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(self);
2482 /* Get operation observers and cancel its current operation */
2483 tmp = priv->progress_widgets;
2485 modest_progress_object_cancel_current_operation (MODEST_PROGRESS_OBJECT(tmp->data));
2486 tmp=g_slist_next(tmp);
2490 observers_empty (ModestMsgViewWindow *self)
2493 ModestMsgViewWindowPrivate *priv;
2494 gboolean is_empty = TRUE;
2495 guint pending_ops = 0;
2497 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(self);
2498 tmp = priv->progress_widgets;
2500 /* Check all observers */
2501 while (tmp && is_empty) {
2502 pending_ops = modest_progress_object_num_pending_operations (MODEST_PROGRESS_OBJECT(tmp->data));
2503 is_empty = pending_ops == 0;
2505 tmp = g_slist_next(tmp);
2512 on_account_removed (TnyAccountStore *account_store,
2513 TnyAccount *account,
2516 /* Do nothing if it's a transport account, because we only
2517 show the messages of a store account */
2518 if (tny_account_get_account_type(account) == TNY_ACCOUNT_TYPE_STORE) {
2519 const gchar *parent_acc = NULL;
2520 const gchar *our_acc = NULL;
2522 our_acc = modest_window_get_active_account (MODEST_WINDOW (user_data));
2523 parent_acc = modest_tny_account_get_parent_modest_account_name_for_server_account (account);
2525 /* Close this window if I'm showing a message of the removed account */
2526 if (strcmp (parent_acc, our_acc) == 0)
2527 modest_ui_actions_on_close_window (NULL, MODEST_WINDOW (user_data));
2532 on_mail_operation_started (ModestMailOperation *mail_op,
2535 ModestMsgViewWindow *self;
2536 ModestMailOperationTypeOperation op_type;
2538 ModestMsgViewWindowPrivate *priv;
2539 GObject *source = NULL;
2541 self = MODEST_MSG_VIEW_WINDOW (user_data);
2542 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
2543 op_type = modest_mail_operation_get_type_operation (mail_op);
2544 tmp = priv->progress_widgets;
2545 source = modest_mail_operation_get_source(mail_op);
2546 if (G_OBJECT (self) == source) {
2547 if (op_type == MODEST_MAIL_OPERATION_TYPE_RECEIVE ) {
2548 set_toolbar_transfer_mode(self);
2550 modest_progress_object_add_operation (
2551 MODEST_PROGRESS_OBJECT (tmp->data),
2553 tmp = g_slist_next (tmp);
2557 g_object_unref (source);
2561 on_mail_operation_finished (ModestMailOperation *mail_op,
2564 ModestMsgViewWindow *self;
2565 ModestMailOperationTypeOperation op_type;
2567 ModestMsgViewWindowPrivate *priv;
2569 self = MODEST_MSG_VIEW_WINDOW (user_data);
2570 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
2571 op_type = modest_mail_operation_get_type_operation (mail_op);
2572 tmp = priv->progress_widgets;
2574 if (op_type == MODEST_MAIL_OPERATION_TYPE_RECEIVE ) {
2576 modest_progress_object_remove_operation (MODEST_PROGRESS_OBJECT (tmp->data),
2578 tmp = g_slist_next (tmp);
2581 /* If no more operations are being observed, NORMAL mode is enabled again */
2582 if (observers_empty (self)) {
2583 set_toolbar_mode (self, TOOLBAR_MODE_NORMAL);
2586 /* Update dimming rules. We have to do this right here
2587 and not in view_msg_cb because at that point the
2588 transfer mode is still enabled so the dimming rule
2589 won't let the user delete the message that has been
2590 readed for example */
2591 modest_ui_actions_check_toolbar_dimming_rules (MODEST_WINDOW (self));
2592 modest_ui_actions_check_menu_dimming_rules (MODEST_WINDOW (self));
2597 on_queue_changed (ModestMailOperationQueue *queue,
2598 ModestMailOperation *mail_op,
2599 ModestMailOperationQueueNotification type,
2600 ModestMsgViewWindow *self)
2602 ModestMsgViewWindowPrivate *priv;
2604 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
2606 /* If this operations was created by another window, do nothing */
2607 if (!modest_mail_operation_is_mine (mail_op, G_OBJECT(self)))
2610 if (type == MODEST_MAIL_OPERATION_QUEUE_OPERATION_ADDED) {
2611 priv->sighandlers = modest_signal_mgr_connect (priv->sighandlers,
2613 "operation-started",
2614 G_CALLBACK (on_mail_operation_started),
2616 priv->sighandlers = modest_signal_mgr_connect (priv->sighandlers,
2618 "operation-finished",
2619 G_CALLBACK (on_mail_operation_finished),
2621 } else if (type == MODEST_MAIL_OPERATION_QUEUE_OPERATION_REMOVED) {
2622 priv->sighandlers = modest_signal_mgr_disconnect (priv->sighandlers,
2624 "operation-started");
2625 priv->sighandlers = modest_signal_mgr_disconnect (priv->sighandlers,
2627 "operation-finished");
2632 modest_msg_view_window_get_attachments (ModestMsgViewWindow *win)
2634 ModestMsgViewWindowPrivate *priv;
2635 TnyList *selected_attachments = NULL;
2637 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (win), NULL);
2638 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (win);
2640 selected_attachments = modest_msg_view_get_selected_attachments (MODEST_MSG_VIEW (priv->msg_view));
2642 return selected_attachments;
2648 guint banner_idle_id;
2649 } DecodeAsyncHelper;
2652 decode_async_banner_idle (gpointer user_data)
2654 DecodeAsyncHelper *helper = (DecodeAsyncHelper *) user_data;
2656 helper->banner_idle_id = 0;
2657 helper->banner = hildon_banner_show_animation (NULL, NULL, _("mail_me_opening"));
2658 g_object_ref (helper->banner);
2664 on_decode_to_stream_async_handler (TnyMimePart *mime_part,
2670 DecodeAsyncHelper *helper = (DecodeAsyncHelper *) user_data;
2672 if (helper->banner_idle_id > 0) {
2673 g_source_remove (helper->banner_idle_id);
2674 helper->banner_idle_id = 0;
2676 if (helper->banner) {
2677 gtk_widget_destroy (helper->banner);
2679 if (cancelled || err) {
2680 modest_platform_information_banner (NULL, NULL,
2681 _("mail_ib_file_operation_failed"));
2685 /* make the file read-only */
2686 g_chmod(helper->filepath, 0444);
2688 /* Activate the file */
2689 modest_platform_activate_file (helper->filepath, modest_tny_mime_part_get_content_type (mime_part));
2693 g_free (helper->filepath);
2694 g_object_unref (helper->banner);
2695 g_slice_free (DecodeAsyncHelper, helper);
2699 modest_msg_view_window_view_attachment (ModestMsgViewWindow *window,
2700 TnyMimePart *mime_part)
2702 ModestMsgViewWindowPrivate *priv;
2703 const gchar *msg_uid;
2704 gchar *attachment_uid = NULL;
2705 gint attachment_index = 0;
2706 TnyList *attachments;
2707 TnyMimePart *window_msg;
2709 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window));
2710 g_return_if_fail (TNY_IS_MIME_PART (mime_part) || (mime_part == NULL));
2711 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
2713 msg_uid = modest_msg_view_window_get_message_uid (MODEST_MSG_VIEW_WINDOW (window));
2714 attachments = modest_msg_view_get_attachments (MODEST_MSG_VIEW (priv->msg_view));
2715 attachment_index = modest_list_index (attachments, (GObject *) mime_part);
2716 g_object_unref (attachments);
2718 if (msg_uid && attachment_index >= 0) {
2719 attachment_uid = g_strdup_printf ("%s/%d", msg_uid, attachment_index);
2722 if (mime_part == NULL) {
2723 gboolean error = FALSE;
2724 TnyList *selected_attachments = modest_msg_view_get_selected_attachments (MODEST_MSG_VIEW (priv->msg_view));
2725 if (selected_attachments == NULL || tny_list_get_length (selected_attachments) == 0) {
2727 } else if (tny_list_get_length (selected_attachments) > 1) {
2728 hildon_banner_show_information (NULL, NULL, _("mcen_ib_unable_to_display_more"));
2732 iter = tny_list_create_iterator (selected_attachments);
2733 mime_part = (TnyMimePart *) tny_iterator_get_current (iter);
2734 g_object_unref (iter);
2736 if (selected_attachments)
2737 g_object_unref (selected_attachments);
2742 g_object_ref (mime_part);
2745 if (tny_mime_part_is_purged (mime_part)) {
2746 g_object_unref (mime_part);
2750 /* we also check for mime_part == priv->msg, as this means it's a direct attachment
2751 * shown as attachment, so it should behave as a file */
2752 window_msg = TNY_MIME_PART (tny_msg_view_get_msg (TNY_MSG_VIEW (priv->msg_view)));
2753 if ((!modest_tny_mime_part_is_msg (mime_part) && tny_mime_part_get_filename (mime_part)) ||
2754 mime_part == window_msg) {
2755 gchar *filepath = NULL;
2756 const gchar *att_filename = tny_mime_part_get_filename (mime_part);
2757 gboolean show_error_banner = FALSE;
2758 TnyFsStream *temp_stream = NULL;
2759 temp_stream = modest_utils_create_temp_stream (att_filename, attachment_uid,
2762 if (temp_stream != NULL) {
2763 DecodeAsyncHelper *helper = g_slice_new (DecodeAsyncHelper);
2764 helper->filepath = g_strdup (filepath);
2765 helper->banner = NULL;
2766 helper->banner_idle_id = g_timeout_add (1000, decode_async_banner_idle, helper);
2767 tny_mime_part_decode_to_stream_async (mime_part, TNY_STREAM (temp_stream),
2768 on_decode_to_stream_async_handler,
2771 g_object_unref (temp_stream);
2772 /* NOTE: files in the temporary area will be automatically
2773 * cleaned after some time if they are no longer in use */
2776 const gchar *content_type;
2777 /* the file may already exist but it isn't writable,
2778 * let's try to open it anyway */
2779 content_type = modest_tny_mime_part_get_content_type (mime_part);
2780 modest_platform_activate_file (filepath, content_type);
2782 g_warning ("%s: modest_utils_create_temp_stream failed", __FUNCTION__);
2783 show_error_banner = TRUE;
2788 if (show_error_banner)
2789 modest_platform_information_banner (NULL, NULL, _("mail_ib_file_operation_failed"));
2790 } else if (!modest_tny_mime_part_is_msg (mime_part)) {
2791 ModestWindowMgr *mgr;
2792 ModestWindow *msg_win = NULL;
2793 TnyMsg *current_msg;
2797 current_msg = modest_msg_view_window_get_message (MODEST_MSG_VIEW_WINDOW (window));
2798 mgr = modest_runtime_get_window_mgr ();
2799 header = tny_msg_get_header (TNY_MSG (current_msg));
2800 found = modest_window_mgr_find_registered_message_uid (mgr,
2805 g_debug ("window for this body is already being created");
2808 /* it's not found, so create a new window for it */
2809 modest_window_mgr_register_header (mgr, header, attachment_uid); /* register the uid before building the window */
2810 gchar *account = g_strdup (modest_window_get_active_account (MODEST_WINDOW (window)));
2811 const gchar *mailbox = modest_window_get_active_mailbox (MODEST_WINDOW (window));
2813 account = modest_account_mgr_get_default_account (modest_runtime_get_account_mgr ());
2815 msg_win = modest_msg_view_window_new_with_other_body (TNY_MSG (current_msg), TNY_MIME_PART (mime_part),
2816 account, mailbox, attachment_uid);
2818 modest_window_set_zoom (MODEST_WINDOW (msg_win),
2819 modest_window_get_zoom (MODEST_WINDOW (window)));
2820 if (modest_window_mgr_register_window (mgr, msg_win, MODEST_WINDOW (window)))
2821 gtk_widget_show_all (GTK_WIDGET (msg_win));
2823 gtk_widget_destroy (GTK_WIDGET (msg_win));
2825 g_object_unref (current_msg);
2827 /* message attachment */
2828 TnyHeader *header = NULL;
2829 ModestWindowMgr *mgr;
2830 ModestWindow *msg_win = NULL;
2833 header = tny_msg_get_header (TNY_MSG (mime_part));
2834 mgr = modest_runtime_get_window_mgr ();
2835 found = modest_window_mgr_find_registered_header (mgr, header, &msg_win);
2838 if (msg_win) /* there is already a window for this uid; top it */
2839 gtk_window_present (GTK_WINDOW(msg_win));
2841 /* if it's found, but there is no msg_win, it's probably in the process of being created;
2842 * thus, we don't do anything */
2843 g_debug ("window for is already being created");
2845 /* it's not found, so create a new window for it */
2846 modest_window_mgr_register_header (mgr, header, attachment_uid); /* register the uid before building the window */
2847 gchar *account = g_strdup (modest_window_get_active_account (MODEST_WINDOW (window)));
2849 account = modest_account_mgr_get_default_account (modest_runtime_get_account_mgr ());
2850 msg_win = modest_msg_view_window_new_for_attachment (TNY_MSG (mime_part), account,
2851 NULL, attachment_uid);
2852 modest_window_set_zoom (MODEST_WINDOW (msg_win),
2853 modest_window_get_zoom (MODEST_WINDOW (window)));
2854 modest_window_mgr_register_window (mgr, msg_win, MODEST_WINDOW (window));
2855 gtk_widget_show_all (GTK_WIDGET (msg_win));
2858 g_object_unref (window_msg);
2859 g_object_unref (mime_part);
2872 GnomeVFSResult result;
2875 static void save_mime_part_info_free (SaveMimePartInfo *info, gboolean with_struct);
2876 static gboolean idle_save_mime_part_show_result (SaveMimePartInfo *info);
2877 static gpointer save_mime_part_to_file (SaveMimePartInfo *info);
2878 static void save_mime_parts_to_file_with_checks (GtkWindow *parent, SaveMimePartInfo *info);
2881 save_mime_part_info_free (SaveMimePartInfo *info, gboolean with_struct)
2885 for (node = info->pairs; node != NULL; node = g_list_next (node)) {
2886 SaveMimePartPair *pair = (SaveMimePartPair *) node->data;
2887 g_free (pair->filename);
2888 g_object_unref (pair->part);
2889 g_slice_free (SaveMimePartPair, pair);
2891 g_list_free (info->pairs);
2894 gtk_widget_destroy (info->banner);
2895 g_slice_free (SaveMimePartInfo, info);
2900 idle_save_mime_part_show_result (SaveMimePartInfo *info)
2902 if (info->pairs != NULL) {
2903 save_mime_part_to_file (info);
2905 /* This is a GDK lock because we are an idle callback and
2906 * hildon_banner_show_information is or does Gtk+ code */
2908 gdk_threads_enter (); /* CHECKED */
2909 save_mime_part_info_free (info, TRUE);
2910 if (info->result == GNOME_VFS_OK) {
2911 hildon_banner_show_information (NULL, NULL, _CS("sfil_ib_saved"));
2912 } else if (info->result == GNOME_VFS_ERROR_NO_SPACE) {
2913 hildon_banner_show_information (NULL, NULL,
2914 _KR("cerm_device_memory_full"));
2916 hildon_banner_show_information (NULL, NULL,
2917 _("mail_ib_file_operation_failed"));
2919 gdk_threads_leave (); /* CHECKED */
2926 save_mime_part_to_file (SaveMimePartInfo *info)
2928 GnomeVFSHandle *handle;
2930 SaveMimePartPair *pair = (SaveMimePartPair *) info->pairs->data;
2932 info->result = gnome_vfs_create (&handle, pair->filename, GNOME_VFS_OPEN_WRITE, FALSE, 0644);
2933 if (info->result == GNOME_VFS_OK) {
2934 GError *error = NULL;
2935 stream = tny_vfs_stream_new (handle);
2936 if (tny_mime_part_decode_to_stream (pair->part, stream, &error) < 0) {
2937 g_warning ("modest: could not save attachment %s: %d (%s)\n", pair->filename, error?error->code:-1, error?error->message:"Unknown error");
2939 info->result = GNOME_VFS_ERROR_IO;
2941 g_object_unref (G_OBJECT (stream));
2942 g_object_unref (pair->part);
2943 g_slice_free (SaveMimePartPair, pair);
2944 info->pairs = g_list_delete_link (info->pairs, info->pairs);
2946 g_warning ("modest: could not create save attachment %s: %s\n", pair->filename, gnome_vfs_result_to_string (info->result));
2947 save_mime_part_info_free (info, FALSE);
2950 g_idle_add ((GSourceFunc) idle_save_mime_part_show_result, info);
2955 save_mime_parts_to_file_with_checks (GtkWindow *parent, SaveMimePartInfo *info)
2957 gboolean is_ok = TRUE;
2958 gint replaced_files = 0;
2959 const GList *files = info->pairs;
2962 for (iter = files; (iter != NULL) && (replaced_files < 2); iter = g_list_next(iter)) {
2963 SaveMimePartPair *pair = iter->data;
2964 if (modest_utils_file_exists (pair->filename)) {
2968 if (replaced_files) {
2970 const gchar *message = (replaced_files == 1) ?
2971 _FM("docm_nc_replace_file") : _FM("docm_nc_replace_multiple");
2972 response = modest_platform_run_confirmation_dialog (parent, message);
2973 if (response != GTK_RESPONSE_OK)
2978 save_mime_part_info_free (info, TRUE);
2980 GtkWidget *banner = hildon_banner_show_animation (NULL, NULL,
2981 _CS("sfil_ib_saving"));
2982 info->banner = banner;
2983 g_thread_create ((GThreadFunc)save_mime_part_to_file, info, FALSE, NULL);
2989 save_attachments_response (GtkDialog *dialog,
2993 TnyList *mime_parts;
2995 GList *files_to_save = NULL;
2997 mime_parts = TNY_LIST (user_data);
2999 if (arg1 != GTK_RESPONSE_OK)
3002 chooser_uri = gtk_file_chooser_get_uri (GTK_FILE_CHOOSER (dialog));
3004 if (!modest_utils_folder_writable (chooser_uri)) {
3005 hildon_banner_show_information
3006 (NULL, NULL, _FM("sfil_ib_readonly_location"));
3010 iter = tny_list_create_iterator (mime_parts);
3011 while (!tny_iterator_is_done (iter)) {
3012 TnyMimePart *mime_part = (TnyMimePart *) tny_iterator_get_current (iter);
3014 if ((modest_tny_mime_part_is_attachment_for_modest (mime_part)) &&
3015 !tny_mime_part_is_purged (mime_part) &&
3016 (tny_mime_part_get_filename (mime_part) != NULL)) {
3017 SaveMimePartPair *pair;
3019 pair = g_slice_new0 (SaveMimePartPair);
3021 if (tny_list_get_length (mime_parts) > 1) {
3023 gnome_vfs_escape_slashes (tny_mime_part_get_filename (mime_part));
3024 pair->filename = g_build_filename (chooser_uri, escaped, NULL);
3027 pair->filename = g_strdup (chooser_uri);
3029 pair->part = mime_part;
3030 files_to_save = g_list_prepend (files_to_save, pair);
3032 tny_iterator_next (iter);
3034 g_object_unref (iter);
3036 g_free (chooser_uri);
3038 if (files_to_save != NULL) {
3039 SaveMimePartInfo *info = g_slice_new0 (SaveMimePartInfo);
3040 info->pairs = files_to_save;
3041 info->result = TRUE;
3042 save_mime_parts_to_file_with_checks ((GtkWindow*) dialog, info);
3046 /* Free and close the dialog */
3047 g_object_unref (mime_parts);
3048 gtk_widget_destroy (GTK_WIDGET (dialog));
3052 modest_msg_view_window_save_attachments (ModestMsgViewWindow *window, TnyList *mime_parts)
3054 ModestMsgViewWindowPrivate *priv;
3055 GtkWidget *save_dialog = NULL;
3056 gchar *folder = NULL;
3057 gchar *filename = NULL;
3058 gchar *save_multiple_str = NULL;
3061 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window));
3062 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
3064 if (mime_parts == NULL) {
3065 mime_parts = modest_msg_view_get_selected_attachments (MODEST_MSG_VIEW (priv->msg_view));
3066 if (mime_parts == NULL || tny_list_get_length (mime_parts) == 0)
3069 g_object_ref (mime_parts);
3072 window_msg = tny_msg_view_get_msg (TNY_MSG_VIEW (priv->msg_view));
3073 /* prepare dialog */
3074 if (tny_list_get_length (mime_parts) == 1) {
3076 /* only one attachment selected */
3077 iter = tny_list_create_iterator (mime_parts);
3078 TnyMimePart *mime_part = (TnyMimePart *) tny_iterator_get_current (iter);
3079 g_object_unref (iter);
3080 if (!modest_tny_mime_part_is_msg (mime_part) &&
3081 modest_tny_mime_part_is_attachment_for_modest (mime_part) &&
3082 !tny_mime_part_is_purged (mime_part)) {
3083 filename = g_strdup (tny_mime_part_get_filename (mime_part));
3085 /* TODO: show any error? */
3086 g_warning ("Tried to save a non-file attachment");
3087 g_object_unref (mime_parts);
3090 g_object_unref (mime_part);
3092 save_multiple_str = g_strdup_printf (_FM("sfil_va_number_of_objects_attachments"),
3093 tny_list_get_length (mime_parts));
3095 g_object_unref (window_msg);
3097 save_dialog = hildon_file_chooser_dialog_new (GTK_WINDOW (window),
3098 GTK_FILE_CHOOSER_ACTION_SAVE);
3101 folder = g_build_filename (g_get_home_dir (), DEFAULT_FOLDER, NULL);
3102 gtk_file_chooser_set_current_folder (GTK_FILE_CHOOSER (save_dialog), folder);
3107 gtk_file_chooser_set_current_name (GTK_FILE_CHOOSER (save_dialog),
3112 /* if multiple, set multiple string */
3113 if (save_multiple_str) {
3114 g_object_set (G_OBJECT (save_dialog), "save-multiple", save_multiple_str, NULL);
3115 gtk_window_set_title (GTK_WINDOW (save_dialog), _FM("sfil_ti_save_objects_files"));
3118 /* We must run this asynchronously, because the hildon dialog
3119 performs a gtk_dialog_run by itself which leads to gdk
3121 g_signal_connect (save_dialog, "response",
3122 G_CALLBACK (save_attachments_response), mime_parts);
3124 gtk_widget_show_all (save_dialog);
3128 show_remove_attachment_information (gpointer userdata)
3130 ModestMsgViewWindow *window = (ModestMsgViewWindow *) userdata;
3131 ModestMsgViewWindowPrivate *priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
3133 /* We're outside the main lock */
3134 gdk_threads_enter ();
3136 if (priv->remove_attachment_banner != NULL) {
3137 gtk_widget_destroy (priv->remove_attachment_banner);
3138 g_object_unref (priv->remove_attachment_banner);
3141 priv->remove_attachment_banner = g_object_ref (
3142 hildon_banner_show_animation (NULL, NULL, _("mcen_ib_removing_attachment")));
3144 gdk_threads_leave ();
3150 modest_msg_view_window_remove_attachments (ModestMsgViewWindow *window, gboolean get_all)
3152 ModestMsgViewWindowPrivate *priv;
3153 TnyList *mime_parts = NULL;
3154 gchar *confirmation_message;
3160 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window));
3161 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
3164 mime_parts = modest_msg_view_get_attachments (MODEST_MSG_VIEW (priv->msg_view));
3166 mime_parts = modest_msg_view_get_selected_attachments (MODEST_MSG_VIEW (priv->msg_view));
3168 /* Remove already purged messages from mime parts list */
3169 iter = tny_list_create_iterator (mime_parts);
3170 while (!tny_iterator_is_done (iter)) {
3171 TnyMimePart *part = TNY_MIME_PART (tny_iterator_get_current (iter));
3172 tny_iterator_next (iter);
3173 if (tny_mime_part_is_purged (part)) {
3174 tny_list_remove (mime_parts, (GObject *) part);
3176 g_object_unref (part);
3178 g_object_unref (iter);
3180 if (tny_list_get_length (mime_parts) == 0) {
3181 g_object_unref (mime_parts);
3185 n_attachments = tny_list_get_length (mime_parts);
3186 if (n_attachments == 1) {
3190 iter = tny_list_create_iterator (mime_parts);
3191 part = (TnyMimePart *) tny_iterator_get_current (iter);
3192 g_object_unref (iter);
3193 if (modest_tny_mime_part_is_msg (part)) {
3195 header = tny_msg_get_header (TNY_MSG (part));
3196 filename = tny_header_dup_subject (header);
3197 g_object_unref (header);
3198 if (filename == NULL)
3199 filename = g_strdup (_("mail_va_no_subject"));
3201 filename = g_strdup (tny_mime_part_get_filename (TNY_MIME_PART (part)));
3203 confirmation_message = g_strdup_printf (_("mcen_nc_purge_file_text"), filename);
3205 g_object_unref (part);
3207 confirmation_message = g_strdup_printf (ngettext("mcen_nc_purge_file_text",
3208 "mcen_nc_purge_files_text",
3209 n_attachments), n_attachments);
3211 response = modest_platform_run_confirmation_dialog (GTK_WINDOW (window),
3212 confirmation_message);
3213 g_free (confirmation_message);
3215 if (response != GTK_RESPONSE_OK) {
3216 g_object_unref (mime_parts);
3220 priv->purge_timeout = g_timeout_add (2000, show_remove_attachment_information, window);
3222 iter = tny_list_create_iterator (mime_parts);
3223 while (!tny_iterator_is_done (iter)) {
3226 part = (TnyMimePart *) tny_iterator_get_current (iter);
3227 tny_mime_part_set_purged (TNY_MIME_PART (part));
3228 g_object_unref (part);
3229 tny_iterator_next (iter);
3231 g_object_unref (iter);
3233 msg = tny_msg_view_get_msg (TNY_MSG_VIEW (priv->msg_view));
3234 tny_msg_view_clear (TNY_MSG_VIEW (priv->msg_view));
3235 tny_msg_rewrite_cache (msg);
3236 tny_msg_view_set_msg (TNY_MSG_VIEW (priv->msg_view), msg);
3237 g_object_unref (msg);
3239 g_object_unref (mime_parts);
3241 if (priv->purge_timeout > 0) {
3242 g_source_remove (priv->purge_timeout);
3243 priv->purge_timeout = 0;
3246 if (priv->remove_attachment_banner) {
3247 gtk_widget_destroy (priv->remove_attachment_banner);
3248 g_object_unref (priv->remove_attachment_banner);
3249 priv->remove_attachment_banner = NULL;
3257 update_window_title (ModestMsgViewWindow *window)
3259 ModestMsgViewWindowPrivate *priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
3261 TnyHeader *header = NULL;
3262 gchar *subject = NULL;
3264 msg = tny_msg_view_get_msg (TNY_MSG_VIEW (priv->msg_view));
3266 if (priv->other_body) {
3269 description = modest_tny_mime_part_get_header_value (priv->other_body, "Content-Description");
3271 g_strstrip (description);
3272 subject = description;
3274 } else if (msg != NULL) {
3275 header = tny_msg_get_header (msg);
3276 subject = tny_header_dup_subject (header);
3277 g_object_unref (header);
3278 g_object_unref (msg);
3281 if ((subject == NULL)||(subject[0] == '\0')) {
3283 subject = g_strdup (_("mail_va_no_subject"));
3286 gtk_window_set_title (GTK_WINDOW (window), subject);
3290 static void on_move_focus (GtkWidget *widget,
3291 GtkDirectionType direction,
3294 g_signal_stop_emission_by_name (G_OBJECT (widget), "move-focus");
3298 fetch_image_open_stream (TnyStreamCache *self, gint64 *expected_size, gchar *uri)
3300 GnomeVFSResult result;
3301 GnomeVFSHandle *handle = NULL;
3302 GnomeVFSFileInfo *info = NULL;
3305 result = gnome_vfs_open (&handle, uri, GNOME_VFS_OPEN_READ);
3306 if (result != GNOME_VFS_OK) {
3311 info = gnome_vfs_file_info_new ();
3312 result = gnome_vfs_get_file_info_from_handle (handle, info, GNOME_VFS_FILE_INFO_DEFAULT);
3313 if (result != GNOME_VFS_OK || ! (info->valid_fields & GNOME_VFS_FILE_INFO_FIELDS_SIZE)) {
3314 /* We put a "safe" default size for going to cache */
3315 *expected_size = (300*1024);
3317 *expected_size = info->size;
3319 gnome_vfs_file_info_unref (info);
3321 stream = tny_vfs_stream_new (handle);
3330 TnyStream *output_stream;
3331 GtkWidget *msg_view;
3335 on_fetch_image_idle_refresh_view (gpointer userdata)
3338 FetchImageData *fidata = (FetchImageData *) userdata;
3339 g_debug ("REFRESH VIEW");
3340 if (GTK_WIDGET_DRAWABLE (fidata->msg_view)) {
3341 g_debug ("QUEUING DRAW");
3342 gtk_widget_queue_draw (fidata->msg_view);
3344 g_object_unref (fidata->msg_view);
3345 g_slice_free (FetchImageData, fidata);
3350 on_fetch_image_thread (gpointer userdata)
3352 FetchImageData *fidata = (FetchImageData *) userdata;
3353 TnyStreamCache *cache;
3354 TnyStream *cache_stream;
3356 cache = modest_runtime_get_images_cache ();
3357 cache_stream = tny_stream_cache_get_stream (cache, fidata->cache_id, (TnyStreamCacheOpenStreamFetcher) fetch_image_open_stream, (gpointer) fidata->uri);
3358 g_free (fidata->cache_id);
3359 g_free (fidata->uri);
3361 if (cache_stream != NULL) {
3362 tny_stream_write_to_stream (cache_stream, fidata->output_stream);
3363 tny_stream_close (cache_stream);
3364 g_object_unref (cache_stream);
3367 tny_stream_close (fidata->output_stream);
3368 g_object_unref (fidata->output_stream);
3371 gdk_threads_enter ();
3372 g_idle_add (on_fetch_image_idle_refresh_view, fidata);
3373 gdk_threads_leave ();
3379 on_fetch_image (ModestMsgView *msgview,
3382 ModestMsgViewWindow *window)
3384 const gchar *current_account;
3385 ModestMsgViewWindowPrivate *priv;
3386 FetchImageData *fidata;
3388 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
3390 current_account = modest_window_get_active_account (MODEST_WINDOW (window));
3392 fidata = g_slice_new0 (FetchImageData);
3393 fidata->msg_view = g_object_ref (msgview);
3394 fidata->uri = g_strdup (uri);
3395 fidata->cache_id = modest_images_cache_get_id (current_account, uri);
3396 fidata->output_stream = g_object_ref (stream);
3398 if (g_thread_create (on_fetch_image_thread, fidata, FALSE, NULL) == NULL) {
3399 g_object_unref (fidata->output_stream);
3400 g_free (fidata->cache_id);
3401 g_free (fidata->uri);
3402 g_object_unref (fidata->msg_view);
3403 g_slice_free (FetchImageData, fidata);
3404 tny_stream_close (stream);
3412 modest_msg_view_window_add_to_contacts (ModestMsgViewWindow *self)
3414 modest_ui_actions_on_add_to_contacts (NULL, MODEST_WINDOW (self));
3419 modest_msg_view_window_fetch_images (ModestMsgViewWindow *self)
3421 ModestMsgViewWindowPrivate *priv;
3422 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
3424 modest_msg_view_request_fetch_images (MODEST_MSG_VIEW (priv->msg_view));
3428 modest_msg_view_window_has_blocked_external_images (ModestMsgViewWindow *self)
3430 ModestMsgViewWindowPrivate *priv;
3431 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
3433 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self), FALSE);
3435 return modest_msg_view_has_blocked_external_images (MODEST_MSG_VIEW (priv->msg_view));
3439 modest_msg_view_window_reload (ModestMsgViewWindow *self)
3441 ModestMsgViewWindowPrivate *priv;
3444 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self));
3446 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
3447 header = modest_msg_view_window_get_header (MODEST_MSG_VIEW_WINDOW (self));
3449 if (!message_reader (self, priv, header, NULL, NULL, priv->row_reference)) {
3450 g_warning ("Shouldn't happen, trying to reload a message failed");
3453 g_object_unref (header);