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 "hildon/hildon-pannable-area.h"
52 #include "modest-defs.h"
53 #include "modest-hildon-includes.h"
54 #include "modest-ui-dimming-manager.h"
55 #include <gdk/gdkkeysyms.h>
56 #include <modest-tny-account.h>
57 #include <modest-mime-part-view.h>
58 #include <modest-isearch-view.h>
59 #include <modest-tny-mime-part.h>
62 #include <glib/gstdio.h>
63 #include <modest-debug.h>
65 #define DEFAULT_FOLDER "MyDocs/.documents"
67 static void modest_msg_view_window_class_init (ModestMsgViewWindowClass *klass);
68 static void modest_msg_view_window_init (ModestMsgViewWindow *obj);
69 static void modest_header_view_observer_init(
70 ModestHeaderViewObserverIface *iface_class);
71 static void modest_msg_view_window_finalize (GObject *obj);
72 static void modest_msg_view_window_toggle_find_toolbar (GtkToggleAction *obj,
74 static void modest_msg_view_window_find_toolbar_close (GtkWidget *widget,
75 ModestMsgViewWindow *obj);
76 static void modest_msg_view_window_find_toolbar_search (GtkWidget *widget,
77 ModestMsgViewWindow *obj);
79 static void modest_msg_view_window_disconnect_signals (ModestWindow *self);
80 static void modest_msg_view_window_set_zoom (ModestWindow *window,
82 static gdouble modest_msg_view_window_get_zoom (ModestWindow *window);
83 static gboolean modest_msg_view_window_zoom_minus (ModestWindow *window);
84 static gboolean modest_msg_view_window_zoom_plus (ModestWindow *window);
85 static gboolean modest_msg_view_window_key_event (GtkWidget *window,
88 static gboolean modest_msg_view_window_window_state_event (GtkWidget *widget,
89 GdkEventWindowState *event,
91 static void modest_msg_view_window_update_priority (ModestMsgViewWindow *window);
93 static void modest_msg_view_window_show_toolbar (ModestWindow *window,
94 gboolean show_toolbar);
96 static void modest_msg_view_window_clipboard_owner_change (GtkClipboard *clipboard,
98 ModestMsgViewWindow *window);
100 static void modest_msg_view_window_on_row_changed (GtkTreeModel *header_model,
103 ModestMsgViewWindow *window);
105 static void modest_msg_view_window_on_row_deleted (GtkTreeModel *header_model,
107 ModestMsgViewWindow *window);
109 static void modest_msg_view_window_on_row_inserted (GtkTreeModel *header_model,
110 GtkTreePath *tree_path,
111 GtkTreeIter *tree_iter,
112 ModestMsgViewWindow *window);
114 static void modest_msg_view_window_on_row_reordered (GtkTreeModel *header_model,
118 ModestMsgViewWindow *window);
120 static void modest_msg_view_window_update_model_replaced (ModestHeaderViewObserver *window,
122 const gchar *tny_folder_id);
124 static void cancel_progressbar (GtkToolButton *toolbutton,
125 ModestMsgViewWindow *self);
127 static void on_queue_changed (ModestMailOperationQueue *queue,
128 ModestMailOperation *mail_op,
129 ModestMailOperationQueueNotification type,
130 ModestMsgViewWindow *self);
132 static void on_account_removed (TnyAccountStore *account_store,
136 static void on_move_focus (GtkWidget *widget,
137 GtkDirectionType direction,
140 static void view_msg_cb (ModestMailOperation *mail_op,
147 static void set_toolbar_mode (ModestMsgViewWindow *self,
148 ModestToolBarModes mode);
150 static void update_window_title (ModestMsgViewWindow *window);
152 static gboolean set_toolbar_transfer_mode (ModestMsgViewWindow *self);
153 static void init_window (ModestMsgViewWindow *obj);
155 static gboolean msg_is_visible (TnyHeader *header, gboolean check_outbox);
157 static void check_dimming_rules_after_change (ModestMsgViewWindow *window);
159 static gboolean on_fetch_image (ModestMsgView *msgview,
162 ModestMsgViewWindow *window);
164 static gboolean modest_msg_view_window_scroll_child (ModestMsgViewWindow *self,
165 GtkScrollType scroll_type,
169 /* list my signals */
176 static const GtkToggleActionEntry msg_view_toggle_action_entries [] = {
177 { "FindInMessage", MODEST_TOOLBAR_ICON_FIND, N_("qgn_toolb_gene_find"), NULL, NULL, G_CALLBACK (modest_msg_view_window_toggle_find_toolbar), FALSE },
178 { "ToolsFindInMessage", NULL, N_("mcen_me_viewer_find"), "<CTRL>F", NULL, G_CALLBACK (modest_msg_view_window_toggle_find_toolbar), FALSE },
181 static const GtkRadioActionEntry msg_view_zoom_action_entries [] = {
182 { "Zoom50", NULL, N_("mcen_me_viewer_50"), NULL, NULL, 50 },
183 { "Zoom80", NULL, N_("mcen_me_viewer_80"), NULL, NULL, 80 },
184 { "Zoom100", NULL, N_("mcen_me_viewer_100"), NULL, NULL, 100 },
185 { "Zoom120", NULL, N_("mcen_me_viewer_120"), NULL, NULL, 120 },
186 { "Zoom150", NULL, N_("mcen_me_viewer_150"), NULL, NULL, 150 },
187 { "Zoom200", NULL, N_("mcen_me_viewer_200"), NULL, NULL, 200 }
190 typedef struct _ModestMsgViewWindowPrivate ModestMsgViewWindowPrivate;
191 struct _ModestMsgViewWindowPrivate {
194 GtkWidget *main_scroll;
195 GtkWidget *find_toolbar;
198 /* Progress observers */
199 GtkWidget *progress_bar;
200 GSList *progress_widgets;
203 GtkWidget *progress_toolitem;
204 GtkWidget *cancel_toolitem;
205 GtkWidget *prev_toolitem;
206 GtkWidget *next_toolitem;
207 ModestToolBarModes current_toolbar_mode;
209 /* Optimized view enabled */
210 gboolean optimized_view;
212 /* Whether this was created via the *_new_for_search_result() function. */
213 gboolean is_search_result;
215 /* Whether the message is in outbox */
218 /* A reference to the @model of the header view
219 * to allow selecting previous/next messages,
220 * if the message is currently selected in the header view.
222 const gchar *header_folder_id;
223 GtkTreeModel *header_model;
224 GtkTreeRowReference *row_reference;
225 GtkTreeRowReference *next_row_reference;
227 gulong clipboard_change_handler;
228 gulong queue_change_handler;
229 gulong account_removed_handler;
230 gulong row_changed_handler;
231 gulong row_deleted_handler;
232 gulong row_inserted_handler;
233 gulong rows_reordered_handler;
236 GtkWidget *remove_attachment_banner;
238 guint progress_bar_timeout;
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;
483 priv->sighandlers = NULL;
486 init_window (MODEST_MSG_VIEW_WINDOW(obj));
488 /* Set window icon */
489 window_icon = modest_platform_get_icon (MODEST_APP_MSG_VIEW_ICON, MODEST_ICON_SIZE_BIG);
491 gtk_window_set_icon (GTK_WINDOW (obj), window_icon);
492 g_object_unref (window_icon);
495 hildon_program_add_window (hildon_program_get_instance(),
498 modest_window_mgr_register_help_id (modest_runtime_get_window_mgr(),
499 GTK_WINDOW(obj),"applications_email_viewer");
504 set_toolbar_transfer_mode (ModestMsgViewWindow *self)
506 ModestMsgViewWindowPrivate *priv = NULL;
508 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self), FALSE);
510 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(self);
512 set_toolbar_mode (self, TOOLBAR_MODE_TRANSFER);
514 if (priv->progress_bar_timeout > 0) {
515 g_source_remove (priv->progress_bar_timeout);
516 priv->progress_bar_timeout = 0;
523 set_toolbar_mode (ModestMsgViewWindow *self,
524 ModestToolBarModes mode)
526 ModestWindowPrivate *parent_priv;
527 ModestMsgViewWindowPrivate *priv;
528 /* GtkWidget *widget = NULL; */
530 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self));
532 parent_priv = MODEST_WINDOW_GET_PRIVATE(self);
533 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(self);
535 /* Sets current toolbar mode */
536 priv->current_toolbar_mode = mode;
538 /* Update toolbar dimming state */
539 modest_ui_actions_check_toolbar_dimming_rules (MODEST_WINDOW (self));
542 case TOOLBAR_MODE_NORMAL:
543 if (priv->progress_toolitem) {
544 gtk_tool_item_set_expand (GTK_TOOL_ITEM (priv->progress_toolitem), FALSE);
545 gtk_widget_hide (priv->progress_toolitem);
548 if (priv->progress_bar)
549 gtk_widget_hide (priv->progress_bar);
551 if (priv->cancel_toolitem)
552 gtk_widget_hide (priv->cancel_toolitem);
554 if (priv->prev_toolitem)
555 gtk_widget_show (priv->prev_toolitem);
557 if (priv->next_toolitem)
558 gtk_widget_show (priv->next_toolitem);
560 /* Hide toolbar if optimized view is enabled */
561 if (priv->optimized_view) {
562 gtk_widget_set_no_show_all (parent_priv->toolbar, TRUE);
563 gtk_widget_hide (GTK_WIDGET(parent_priv->toolbar));
567 case TOOLBAR_MODE_TRANSFER:
568 if (priv->prev_toolitem)
569 gtk_widget_hide (priv->prev_toolitem);
571 if (priv->next_toolitem)
572 gtk_widget_hide (priv->next_toolitem);
574 if (priv->progress_bar)
575 gtk_widget_show (priv->progress_bar);
577 if (priv->progress_toolitem) {
578 gtk_tool_item_set_expand (GTK_TOOL_ITEM (priv->progress_toolitem), TRUE);
579 gtk_widget_show (priv->progress_toolitem);
582 if (priv->cancel_toolitem)
583 gtk_widget_show (priv->cancel_toolitem);
585 /* Show toolbar if it's hiden (optimized view ) */
586 if (priv->optimized_view) {
587 gtk_widget_set_no_show_all (parent_priv->toolbar, FALSE);
588 gtk_widget_show (GTK_WIDGET (parent_priv->toolbar));
593 g_return_if_reached ();
600 init_window (ModestMsgViewWindow *obj)
602 GtkWidget *main_vbox;
603 ModestMsgViewWindowPrivate *priv;
605 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(obj);
607 priv->msg_view = GTK_WIDGET (tny_platform_factory_new_msg_view (modest_tny_platform_factory_get_instance ()));
608 modest_msg_view_set_shadow_type (MODEST_MSG_VIEW (priv->msg_view), GTK_SHADOW_NONE);
609 main_vbox = gtk_vbox_new (FALSE, 6);
610 #ifdef MODEST_TOOLKIT_HILDON2
611 priv->main_scroll = hildon_pannable_area_new ();
612 gtk_container_add (GTK_CONTAINER (priv->main_scroll), priv->msg_view);
614 #ifdef MODEST_USE_MOZEMBED
615 priv->main_scroll = priv->msg_view;
616 gtk_widget_set_size_request (priv->msg_view, -1, 1600);
618 priv->main_scroll = gtk_scrolled_window_new (NULL, NULL);
619 gtk_container_add (GTK_CONTAINER (priv->main_scroll), priv->msg_view);
621 gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (priv->main_scroll), GTK_POLICY_NEVER, GTK_POLICY_AUTOMATIC);
622 gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW (priv->main_scroll), GTK_SHADOW_NONE);
623 modest_maemo_set_thumbable_scrollbar (GTK_SCROLLED_WINDOW(priv->main_scroll), TRUE);
626 gtk_box_pack_start (GTK_BOX(main_vbox), priv->main_scroll, TRUE, TRUE, 0);
627 gtk_container_add (GTK_CONTAINER(obj), main_vbox);
629 priv->find_toolbar = hildon_find_toolbar_new (NULL);
630 hildon_window_add_toolbar (HILDON_WINDOW (obj), GTK_TOOLBAR (priv->find_toolbar));
631 gtk_widget_set_no_show_all (priv->find_toolbar, TRUE);
633 gtk_widget_show_all (GTK_WIDGET(main_vbox));
637 modest_msg_view_window_disconnect_signals (ModestWindow *self)
639 ModestMsgViewWindowPrivate *priv;
640 ModestHeaderView *header_view = NULL;
641 ModestWindow *main_window = NULL;
643 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
645 if (gtk_clipboard_get (GDK_SELECTION_PRIMARY) &&
646 g_signal_handler_is_connected (gtk_clipboard_get (GDK_SELECTION_PRIMARY),
647 priv->clipboard_change_handler))
648 g_signal_handler_disconnect (gtk_clipboard_get (GDK_SELECTION_PRIMARY),
649 priv->clipboard_change_handler);
651 if (g_signal_handler_is_connected (G_OBJECT (modest_runtime_get_mail_operation_queue ()),
652 priv->queue_change_handler))
653 g_signal_handler_disconnect (G_OBJECT (modest_runtime_get_mail_operation_queue ()),
654 priv->queue_change_handler);
656 if (g_signal_handler_is_connected (G_OBJECT (modest_runtime_get_account_store ()),
657 priv->account_removed_handler))
658 g_signal_handler_disconnect (G_OBJECT (modest_runtime_get_account_store ()),
659 priv->account_removed_handler);
661 if (priv->header_model) {
662 if (g_signal_handler_is_connected(G_OBJECT (priv->header_model),
663 priv->row_changed_handler))
664 g_signal_handler_disconnect(G_OBJECT (priv->header_model),
665 priv->row_changed_handler);
667 if (g_signal_handler_is_connected(G_OBJECT (priv->header_model),
668 priv->row_deleted_handler))
669 g_signal_handler_disconnect(G_OBJECT (priv->header_model),
670 priv->row_deleted_handler);
672 if (g_signal_handler_is_connected(G_OBJECT (priv->header_model),
673 priv->row_inserted_handler))
674 g_signal_handler_disconnect(G_OBJECT (priv->header_model),
675 priv->row_inserted_handler);
677 if (g_signal_handler_is_connected(G_OBJECT (priv->header_model),
678 priv->rows_reordered_handler))
679 g_signal_handler_disconnect(G_OBJECT (priv->header_model),
680 priv->rows_reordered_handler);
683 modest_signal_mgr_disconnect_all_and_destroy (priv->sighandlers);
684 priv->sighandlers = NULL;
686 main_window = modest_window_mgr_get_main_window (modest_runtime_get_window_mgr(),
687 FALSE); /* don't create */
691 header_view = MODEST_HEADER_VIEW(
692 modest_main_window_get_child_widget(
693 MODEST_MAIN_WINDOW(main_window),
694 MODEST_MAIN_WINDOW_WIDGET_TYPE_HEADER_VIEW));
695 if (header_view == NULL)
698 modest_header_view_remove_observer(header_view,
699 MODEST_HEADER_VIEW_OBSERVER(self));
703 modest_msg_view_window_finalize (GObject *obj)
705 ModestMsgViewWindowPrivate *priv;
707 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (obj);
709 /* Sanity check: shouldn't be needed, the window mgr should
710 call this function before */
711 modest_msg_view_window_disconnect_signals (MODEST_WINDOW (obj));
713 if (priv->header_model != NULL) {
714 g_object_unref (priv->header_model);
715 priv->header_model = NULL;
718 if (priv->progress_bar_timeout > 0) {
719 g_source_remove (priv->progress_bar_timeout);
720 priv->progress_bar_timeout = 0;
723 if (priv->remove_attachment_banner) {
724 gtk_widget_destroy (priv->remove_attachment_banner);
725 g_object_unref (priv->remove_attachment_banner);
726 priv->remove_attachment_banner = NULL;
729 if (priv->purge_timeout > 0) {
730 g_source_remove (priv->purge_timeout);
731 priv->purge_timeout = 0;
734 if (priv->row_reference) {
735 gtk_tree_row_reference_free (priv->row_reference);
736 priv->row_reference = NULL;
739 if (priv->next_row_reference) {
740 gtk_tree_row_reference_free (priv->next_row_reference);
741 priv->next_row_reference = NULL;
745 g_free (priv->msg_uid);
746 priv->msg_uid = NULL;
749 G_OBJECT_CLASS(parent_class)->finalize (obj);
753 select_next_valid_row (GtkTreeModel *model,
754 GtkTreeRowReference **row_reference,
758 GtkTreeIter tmp_iter;
760 GtkTreePath *next = NULL;
761 gboolean retval = FALSE, finished;
763 g_return_val_if_fail (gtk_tree_row_reference_valid (*row_reference), FALSE);
765 path = gtk_tree_row_reference_get_path (*row_reference);
766 gtk_tree_model_get_iter (model, &tmp_iter, path);
767 gtk_tree_row_reference_free (*row_reference);
768 *row_reference = NULL;
772 TnyHeader *header = NULL;
774 if (gtk_tree_model_iter_next (model, &tmp_iter)) {
775 gtk_tree_model_get (model, &tmp_iter,
776 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
780 if (msg_is_visible (header, is_outbox)) {
781 next = gtk_tree_model_get_path (model, &tmp_iter);
782 *row_reference = gtk_tree_row_reference_new (model, next);
786 g_object_unref (header);
789 } else if (cycle && gtk_tree_model_get_iter_first (model, &tmp_iter)) {
790 next = gtk_tree_model_get_path (model, &tmp_iter);
792 /* Ensure that we are not selecting the same */
793 if (gtk_tree_path_compare (path, next) != 0) {
794 gtk_tree_model_get (model, &tmp_iter,
795 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
798 if (msg_is_visible (header, is_outbox)) {
799 *row_reference = gtk_tree_row_reference_new (model, next);
803 g_object_unref (header);
807 /* If we ended up in the same message
808 then there is no valid next
813 /* If there are no more messages and we don't
814 want to start again in the first one then
815 there is no valid next message */
821 gtk_tree_path_free (path);
823 gtk_tree_path_free (next);
828 /* TODO: This should be in _init(), with the parameters as properties. */
830 modest_msg_view_window_construct (ModestMsgViewWindow *self,
831 const gchar *modest_account_name,
832 const gchar *msg_uid)
835 ModestMsgViewWindowPrivate *priv = NULL;
836 ModestWindowPrivate *parent_priv = NULL;
837 ModestDimmingRulesGroup *menu_rules_group = NULL;
838 ModestDimmingRulesGroup *toolbar_rules_group = NULL;
839 ModestDimmingRulesGroup *clipboard_rules_group = NULL;
841 obj = G_OBJECT (self);
842 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(obj);
843 parent_priv = MODEST_WINDOW_GET_PRIVATE(obj);
845 priv->msg_uid = g_strdup (msg_uid);
848 parent_priv->menubar = modest_maemo_utils_get_manager_menubar_as_menu (parent_priv->ui_manager, "/MenuBar");
849 hildon_window_set_menu (HILDON_WINDOW(obj), GTK_MENU(parent_priv->menubar));
850 gtk_widget_show (parent_priv->menubar);
851 parent_priv->ui_dimming_manager = modest_ui_dimming_manager_new();
853 menu_rules_group = modest_dimming_rules_group_new (MODEST_DIMMING_RULES_MENU, FALSE);
854 toolbar_rules_group = modest_dimming_rules_group_new (MODEST_DIMMING_RULES_TOOLBAR, TRUE);
855 clipboard_rules_group = modest_dimming_rules_group_new (MODEST_DIMMING_RULES_CLIPBOARD, FALSE);
857 /* Add common dimming rules */
858 modest_dimming_rules_group_add_rules (menu_rules_group,
859 modest_msg_view_menu_dimming_entries,
860 G_N_ELEMENTS (modest_msg_view_menu_dimming_entries),
861 MODEST_WINDOW (self));
862 modest_dimming_rules_group_add_rules (toolbar_rules_group,
863 modest_msg_view_toolbar_dimming_entries,
864 G_N_ELEMENTS (modest_msg_view_toolbar_dimming_entries),
865 MODEST_WINDOW (self));
866 modest_dimming_rules_group_add_rules (clipboard_rules_group,
867 modest_msg_view_clipboard_dimming_entries,
868 G_N_ELEMENTS (modest_msg_view_clipboard_dimming_entries),
869 MODEST_WINDOW (self));
871 /* Insert dimming rules group for this window */
872 modest_ui_dimming_manager_insert_rules_group (parent_priv->ui_dimming_manager, menu_rules_group);
873 modest_ui_dimming_manager_insert_rules_group (parent_priv->ui_dimming_manager, toolbar_rules_group);
874 modest_ui_dimming_manager_insert_rules_group (parent_priv->ui_dimming_manager, clipboard_rules_group);
875 g_object_unref (menu_rules_group);
876 g_object_unref (toolbar_rules_group);
877 g_object_unref (clipboard_rules_group);
879 restore_settings (MODEST_MSG_VIEW_WINDOW(obj));
881 /* g_signal_connect (G_OBJECT(obj), "delete-event", G_CALLBACK(on_delete_event), obj); */
883 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);
884 g_signal_connect (G_OBJECT(priv->msg_view), "activate_link",
885 G_CALLBACK (modest_ui_actions_on_msg_link_clicked), obj);
886 g_signal_connect (G_OBJECT(priv->msg_view), "link_hover",
887 G_CALLBACK (modest_ui_actions_on_msg_link_hover), obj);
888 g_signal_connect (G_OBJECT(priv->msg_view), "attachment_clicked",
889 G_CALLBACK (modest_ui_actions_on_msg_attachment_clicked), obj);
890 g_signal_connect (G_OBJECT(priv->msg_view), "recpt_activated",
891 G_CALLBACK (modest_ui_actions_on_msg_recpt_activated), obj);
892 g_signal_connect (G_OBJECT(priv->msg_view), "link_contextual",
893 G_CALLBACK (modest_ui_actions_on_msg_link_contextual), obj);
894 g_signal_connect (G_OBJECT (priv->msg_view), "fetch_image",
895 G_CALLBACK (on_fetch_image), obj);
897 g_signal_connect (G_OBJECT (obj), "key-release-event",
898 G_CALLBACK (modest_msg_view_window_key_event),
901 g_signal_connect (G_OBJECT (obj), "key-press-event",
902 G_CALLBACK (modest_msg_view_window_key_event),
905 g_signal_connect (G_OBJECT (obj), "window-state-event",
906 G_CALLBACK (modest_msg_view_window_window_state_event),
909 g_signal_connect (G_OBJECT (obj), "move-focus",
910 G_CALLBACK (on_move_focus), obj);
912 /* Mail Operation Queue */
913 priv->queue_change_handler = g_signal_connect (G_OBJECT (modest_runtime_get_mail_operation_queue ()),
915 G_CALLBACK (on_queue_changed),
918 /* Account manager */
919 priv->account_removed_handler = g_signal_connect (G_OBJECT (modest_runtime_get_account_store ()),
921 G_CALLBACK(on_account_removed),
924 modest_window_set_active_account (MODEST_WINDOW(obj), modest_account_name);
926 g_signal_connect (G_OBJECT (priv->find_toolbar), "close", G_CALLBACK (modest_msg_view_window_find_toolbar_close), obj);
927 g_signal_connect (G_OBJECT (priv->find_toolbar), "search", G_CALLBACK (modest_msg_view_window_find_toolbar_search), obj);
928 priv->last_search = NULL;
930 /* Init the clipboard actions dim status */
931 modest_msg_view_grab_focus(MODEST_MSG_VIEW (priv->msg_view));
933 update_window_title (MODEST_MSG_VIEW_WINDOW (obj));
938 /* FIXME: parameter checks */
940 modest_msg_view_window_new_with_header_model (TnyMsg *msg,
941 const gchar *modest_account_name,
942 const gchar *msg_uid,
944 GtkTreeRowReference *row_reference)
946 ModestMsgViewWindow *window = NULL;
947 ModestMsgViewWindowPrivate *priv = NULL;
948 TnyFolder *header_folder = NULL;
949 ModestHeaderView *header_view = NULL;
950 ModestWindow *main_window = NULL;
951 ModestWindowMgr *mgr = NULL;
954 modest_tny_mime_part_to_string (TNY_MIME_PART (msg), 0);
957 mgr = modest_runtime_get_window_mgr ();
958 window = MODEST_MSG_VIEW_WINDOW (modest_window_mgr_get_msg_view_window (mgr));
959 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), NULL);
961 modest_msg_view_window_construct (window, modest_account_name, msg_uid);
963 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
965 /* Remember the message list's TreeModel so we can detect changes
966 * and change the list selection when necessary: */
968 main_window = modest_window_mgr_get_main_window(mgr, FALSE); /* don't create */
970 header_view = MODEST_HEADER_VIEW(modest_main_window_get_child_widget(
971 MODEST_MAIN_WINDOW(main_window),
972 MODEST_MAIN_WINDOW_WIDGET_TYPE_HEADER_VIEW));
975 if (header_view != NULL){
976 header_folder = modest_header_view_get_folder(header_view);
977 /* This could happen if the header folder was
978 unseleted before opening this msg window (for
979 example if the user selects an account in the
980 folder view of the main window */
982 priv->is_outbox = (modest_tny_folder_guess_folder_type (header_folder) == TNY_FOLDER_TYPE_OUTBOX);
983 priv->header_folder_id = tny_folder_get_id(header_folder);
984 g_assert(priv->header_folder_id != NULL);
985 g_object_unref(header_folder);
989 /* Setup row references and connect signals */
990 priv->header_model = g_object_ref (model);
993 priv->row_reference = gtk_tree_row_reference_copy (row_reference);
994 priv->next_row_reference = gtk_tree_row_reference_copy (row_reference);
995 select_next_valid_row (model, &(priv->next_row_reference), TRUE, priv->is_outbox);
997 priv->row_reference = NULL;
998 priv->next_row_reference = NULL;
1001 /* Connect signals */
1002 priv->row_changed_handler =
1003 g_signal_connect (GTK_TREE_MODEL(model), "row-changed",
1004 G_CALLBACK(modest_msg_view_window_on_row_changed),
1006 priv->row_deleted_handler =
1007 g_signal_connect (GTK_TREE_MODEL(model), "row-deleted",
1008 G_CALLBACK(modest_msg_view_window_on_row_deleted),
1010 priv->row_inserted_handler =
1011 g_signal_connect (GTK_TREE_MODEL(model), "row-inserted",
1012 G_CALLBACK(modest_msg_view_window_on_row_inserted),
1014 priv->rows_reordered_handler =
1015 g_signal_connect(GTK_TREE_MODEL(model), "rows-reordered",
1016 G_CALLBACK(modest_msg_view_window_on_row_reordered),
1019 if (header_view != NULL){
1020 modest_header_view_add_observer(header_view,
1021 MODEST_HEADER_VIEW_OBSERVER(window));
1024 tny_msg_view_set_msg (TNY_MSG_VIEW (priv->msg_view), msg);
1025 update_window_title (MODEST_MSG_VIEW_WINDOW (window));
1026 gtk_widget_show_all (GTK_WIDGET (window));
1027 modest_msg_view_window_update_priority (window);
1029 /* Check dimming rules */
1030 modest_ui_actions_check_toolbar_dimming_rules (MODEST_WINDOW (window));
1031 modest_ui_actions_check_menu_dimming_rules (MODEST_WINDOW (window));
1032 modest_window_check_dimming_rules_group (MODEST_WINDOW (window), MODEST_DIMMING_RULES_CLIPBOARD);
1034 return MODEST_WINDOW(window);
1038 modest_msg_view_window_new_for_search_result (TnyMsg *msg,
1039 const gchar *modest_account_name,
1040 const gchar *msg_uid)
1042 ModestMsgViewWindow *window = NULL;
1043 ModestMsgViewWindowPrivate *priv = NULL;
1044 ModestWindowMgr *mgr = NULL;
1046 mgr = modest_runtime_get_window_mgr ();
1047 window = MODEST_MSG_VIEW_WINDOW (modest_window_mgr_get_msg_view_window (mgr));
1048 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), NULL);
1049 modest_msg_view_window_construct (window, modest_account_name, msg_uid);
1051 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1053 /* Remember that this is a search result,
1054 * so we can disable some UI appropriately: */
1055 priv->is_search_result = TRUE;
1057 tny_msg_view_set_msg (TNY_MSG_VIEW (priv->msg_view), msg);
1059 update_window_title (window);
1060 gtk_widget_show_all (GTK_WIDGET (window));
1061 modest_msg_view_window_update_priority (window);
1063 /* Check dimming rules */
1064 modest_ui_actions_check_toolbar_dimming_rules (MODEST_WINDOW (window));
1065 modest_ui_actions_check_menu_dimming_rules (MODEST_WINDOW (window));
1066 modest_window_check_dimming_rules_group (MODEST_WINDOW (window), MODEST_DIMMING_RULES_CLIPBOARD);
1068 return MODEST_WINDOW(window);
1072 modest_msg_view_window_new_for_attachment (TnyMsg *msg,
1073 const gchar *modest_account_name,
1074 const gchar *msg_uid)
1076 GObject *obj = NULL;
1077 ModestMsgViewWindowPrivate *priv;
1078 ModestWindowMgr *mgr = NULL;
1080 g_return_val_if_fail (msg, NULL);
1081 mgr = modest_runtime_get_window_mgr ();
1082 obj = G_OBJECT (modest_window_mgr_get_msg_view_window (mgr));
1083 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (obj);
1084 modest_msg_view_window_construct (MODEST_MSG_VIEW_WINDOW (obj),
1085 modest_account_name, msg_uid);
1087 tny_msg_view_set_msg (TNY_MSG_VIEW (priv->msg_view), msg);
1088 update_window_title (MODEST_MSG_VIEW_WINDOW (obj));
1090 gtk_widget_show_all (GTK_WIDGET (obj));
1092 /* Check dimming rules */
1093 modest_ui_actions_check_toolbar_dimming_rules (MODEST_WINDOW (obj));
1094 modest_ui_actions_check_menu_dimming_rules (MODEST_WINDOW (obj));
1095 modest_window_check_dimming_rules_group (MODEST_WINDOW (obj), MODEST_DIMMING_RULES_CLIPBOARD);
1097 return MODEST_WINDOW(obj);
1101 modest_msg_view_window_on_row_changed (GtkTreeModel *header_model,
1104 ModestMsgViewWindow *window)
1106 check_dimming_rules_after_change (window);
1110 modest_msg_view_window_on_row_deleted(GtkTreeModel *header_model,
1112 ModestMsgViewWindow *window)
1114 check_dimming_rules_after_change (window);
1116 /* The window could have dissapeared */
1119 check_dimming_rules_after_change (ModestMsgViewWindow *window)
1121 modest_ui_actions_check_menu_dimming_rules (MODEST_WINDOW (window));
1122 modest_ui_actions_check_toolbar_dimming_rules (MODEST_WINDOW (window));
1126 /* On insertions we check if the folder still has the message we are
1127 * showing or do not. If do not, we do nothing. Which means we are still
1128 * not attached to any header folder and thus next/prev buttons are
1129 * still dimmed. Once the message that is shown by msg-view is found, the
1130 * new model of header-view will be attached and the references will be set.
1131 * On each further insertions dimming rules will be checked. However
1132 * this requires extra CPU time at least works.
1133 * (An message might be deleted from TnyFolder and thus will not be
1134 * inserted into the model again for example if it is removed by the
1135 * imap server and the header view is refreshed.)
1138 modest_msg_view_window_on_row_inserted (GtkTreeModel *model,
1139 GtkTreePath *tree_path,
1140 GtkTreeIter *tree_iter,
1141 ModestMsgViewWindow *window)
1143 ModestMsgViewWindowPrivate *priv = NULL;
1144 TnyHeader *header = NULL;
1146 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window));
1147 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1149 g_assert (model == priv->header_model);
1151 /* Check if the newly inserted message is the same we are actually
1152 * showing. IF not, we should remain detached from the header model
1153 * and thus prev and next toolbar buttons should remain dimmed. */
1154 gtk_tree_model_get (model, tree_iter,
1155 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
1158 if (TNY_IS_HEADER (header)) {
1161 uid = modest_tny_folder_get_header_unique_id (header);
1162 if (!g_str_equal(priv->msg_uid, uid)) {
1163 check_dimming_rules_after_change (window);
1165 g_object_unref (G_OBJECT(header));
1169 g_object_unref(G_OBJECT(header));
1172 if (priv->row_reference) {
1173 gtk_tree_row_reference_free (priv->row_reference);
1176 /* Setup row_reference for the actual msg. */
1177 priv->row_reference = gtk_tree_row_reference_new (priv->header_model, tree_path);
1178 if (priv->row_reference == NULL) {
1179 g_warning("No reference for msg header item.");
1183 /* Now set up next_row_reference. */
1184 if (priv->next_row_reference) {
1185 gtk_tree_row_reference_free (priv->next_row_reference);
1188 priv->next_row_reference = gtk_tree_row_reference_copy (priv->row_reference);
1189 select_next_valid_row (priv->header_model,
1190 &(priv->next_row_reference), FALSE, priv->is_outbox);
1192 /* Connect the remaining callbacks to become able to detect
1193 * changes in header-view. */
1194 priv->row_changed_handler =
1195 g_signal_connect (priv->header_model, "row-changed",
1196 G_CALLBACK (modest_msg_view_window_on_row_changed),
1198 priv->row_deleted_handler =
1199 g_signal_connect (priv->header_model, "row-deleted",
1200 G_CALLBACK (modest_msg_view_window_on_row_deleted),
1202 priv->rows_reordered_handler =
1203 g_signal_connect (priv->header_model, "rows-reordered",
1204 G_CALLBACK (modest_msg_view_window_on_row_reordered),
1207 check_dimming_rules_after_change (window);
1211 modest_msg_view_window_on_row_reordered (GtkTreeModel *header_model,
1215 ModestMsgViewWindow *window)
1217 ModestMsgViewWindowPrivate *priv = NULL;
1218 gboolean already_changed = FALSE;
1220 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(window);
1222 /* If the current row was reordered select the proper next
1223 valid row. The same if the next row reference changes */
1224 if (priv->row_reference &&
1225 gtk_tree_row_reference_valid (priv->row_reference)) {
1227 path = gtk_tree_row_reference_get_path (priv->row_reference);
1228 if (gtk_tree_path_compare (path, arg1) == 0) {
1229 if (priv->next_row_reference) {
1230 gtk_tree_row_reference_free (priv->next_row_reference);
1232 priv->next_row_reference = gtk_tree_row_reference_copy (priv->row_reference);
1233 select_next_valid_row (header_model, &(priv->next_row_reference), FALSE, priv->is_outbox);
1234 already_changed = TRUE;
1236 gtk_tree_path_free (path);
1238 if (!already_changed &&
1239 priv->next_row_reference &&
1240 gtk_tree_row_reference_valid (priv->next_row_reference)) {
1242 path = gtk_tree_row_reference_get_path (priv->next_row_reference);
1243 if (gtk_tree_path_compare (path, arg1) == 0) {
1244 if (priv->next_row_reference) {
1245 gtk_tree_row_reference_free (priv->next_row_reference);
1247 priv->next_row_reference = gtk_tree_row_reference_copy (priv->row_reference);
1248 select_next_valid_row (header_model, &(priv->next_row_reference), FALSE, priv->is_outbox);
1250 gtk_tree_path_free (path);
1252 check_dimming_rules_after_change (window);
1255 /* The modest_msg_view_window_update_model_replaced implements update
1256 * function for ModestHeaderViewObserver. Checks whether the TnyFolder
1257 * actually belongs to the header-view is the same as the TnyFolder of
1258 * the message of msg-view or not. If they are different, there is
1259 * nothing to do. If they are the same, then the model has replaced and
1260 * the reference in msg-view shall be replaced from the old model to
1261 * the new model. In this case the view will be detached from it's
1262 * header folder. From this point the next/prev buttons are dimmed.
1265 modest_msg_view_window_update_model_replaced (ModestHeaderViewObserver *observer,
1266 GtkTreeModel *model,
1267 const gchar *tny_folder_id)
1269 ModestMsgViewWindowPrivate *priv = NULL;
1270 ModestMsgViewWindow *window = NULL;
1272 g_assert(MODEST_IS_HEADER_VIEW_OBSERVER(observer));
1273 g_assert(MODEST_IS_MSG_VIEW_WINDOW(observer));
1275 window = MODEST_MSG_VIEW_WINDOW(observer);
1276 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(window);
1278 /* If there is an other folder in the header-view then we do
1279 * not care about it's model (msg list). Else if the
1280 * header-view shows the folder the msg shown by us is in, we
1281 * shall replace our model reference and make some check. */
1282 if(model == NULL || tny_folder_id == NULL ||
1283 (priv->header_folder_id && !g_str_equal(tny_folder_id, priv->header_folder_id)))
1286 /* Model is changed(replaced), so we should forget the old
1287 * one. Because there might be other references and there
1288 * might be some change on the model even if we unreferenced
1289 * it, we need to disconnect our signals here. */
1290 if (priv->header_model) {
1291 if (g_signal_handler_is_connected(G_OBJECT (priv->header_model),
1292 priv->row_changed_handler))
1293 g_signal_handler_disconnect(G_OBJECT (priv->header_model),
1294 priv->row_changed_handler);
1295 if (g_signal_handler_is_connected(G_OBJECT (priv->header_model),
1296 priv->row_deleted_handler))
1297 g_signal_handler_disconnect(G_OBJECT (priv->header_model),
1298 priv->row_deleted_handler);
1299 if (g_signal_handler_is_connected(G_OBJECT (priv->header_model),
1300 priv->row_inserted_handler))
1301 g_signal_handler_disconnect(G_OBJECT (priv->header_model),
1302 priv->row_inserted_handler);
1303 if (g_signal_handler_is_connected(G_OBJECT (priv->header_model),
1304 priv->rows_reordered_handler))
1305 g_signal_handler_disconnect(G_OBJECT (priv->header_model),
1306 priv->rows_reordered_handler);
1309 if (priv->row_reference)
1310 gtk_tree_row_reference_free (priv->row_reference);
1311 if (priv->next_row_reference)
1312 gtk_tree_row_reference_free (priv->next_row_reference);
1313 g_object_unref(priv->header_model);
1316 priv->row_changed_handler = 0;
1317 priv->row_deleted_handler = 0;
1318 priv->row_inserted_handler = 0;
1319 priv->rows_reordered_handler = 0;
1320 priv->next_row_reference = NULL;
1321 priv->row_reference = NULL;
1322 priv->header_model = NULL;
1325 priv->header_model = g_object_ref (model);
1327 /* Also we must connect to the new model for row insertions.
1328 * Only for insertions now. We will need other ones only after
1329 * the msg is show by msg-view is added to the new model. */
1330 priv->row_inserted_handler =
1331 g_signal_connect (priv->header_model, "row-inserted",
1332 G_CALLBACK(modest_msg_view_window_on_row_inserted),
1335 modest_ui_actions_check_menu_dimming_rules(MODEST_WINDOW(window));
1336 modest_ui_actions_check_toolbar_dimming_rules(MODEST_WINDOW(window));
1340 modest_msg_view_window_toolbar_on_transfer_mode (ModestMsgViewWindow *self)
1342 ModestMsgViewWindowPrivate *priv= NULL;
1344 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self), FALSE);
1345 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
1347 return priv->current_toolbar_mode == TOOLBAR_MODE_TRANSFER;
1351 modest_msg_view_window_get_header (ModestMsgViewWindow *self)
1353 ModestMsgViewWindowPrivate *priv= NULL;
1355 TnyHeader *header = NULL;
1356 GtkTreePath *path = NULL;
1359 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self), NULL);
1360 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
1362 /* If the message was not obtained from a treemodel,
1363 * for instance if it was opened directly by the search UI:
1365 if (priv->header_model == NULL ||
1366 priv->row_reference == NULL ||
1367 !gtk_tree_row_reference_valid (priv->row_reference)) {
1368 msg = modest_msg_view_window_get_message (self);
1370 header = tny_msg_get_header (msg);
1371 g_object_unref (msg);
1376 /* Get iter of the currently selected message in the header view: */
1377 path = gtk_tree_row_reference_get_path (priv->row_reference);
1378 g_return_val_if_fail (path != NULL, NULL);
1379 gtk_tree_model_get_iter (priv->header_model,
1383 /* Get current message header */
1384 gtk_tree_model_get (priv->header_model, &iter,
1385 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
1388 gtk_tree_path_free (path);
1393 modest_msg_view_window_get_message (ModestMsgViewWindow *self)
1395 ModestMsgViewWindowPrivate *priv;
1397 g_return_val_if_fail (self, NULL);
1399 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(self);
1401 return tny_msg_view_get_msg (TNY_MSG_VIEW (priv->msg_view));
1405 modest_msg_view_window_get_message_uid (ModestMsgViewWindow *self)
1407 ModestMsgViewWindowPrivate *priv;
1409 g_return_val_if_fail (self, NULL);
1411 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
1413 return (const gchar*) priv->msg_uid;
1417 modest_msg_view_window_toggle_find_toolbar (GtkToggleAction *toggle,
1420 ModestMsgViewWindow *window = MODEST_MSG_VIEW_WINDOW (data);
1421 ModestMsgViewWindowPrivate *priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1422 ModestWindowPrivate *parent_priv = MODEST_WINDOW_GET_PRIVATE (window);
1426 is_active = gtk_toggle_action_get_active (toggle);
1429 gtk_widget_show (priv->find_toolbar);
1430 hildon_find_toolbar_highlight_entry (HILDON_FIND_TOOLBAR (priv->find_toolbar), TRUE);
1432 gtk_widget_hide (priv->find_toolbar);
1435 /* update the toggle buttons status */
1436 action = gtk_ui_manager_get_action (parent_priv->ui_manager, "/ToolBar/FindInMessage");
1437 modest_utils_toggle_action_set_active_block_notify (GTK_TOGGLE_ACTION (action), is_active);
1438 action = gtk_ui_manager_get_action (parent_priv->ui_manager, "/MenuBar/ToolsMenu/ToolsFindInMessageMenu");
1439 modest_utils_toggle_action_set_active_block_notify (GTK_TOGGLE_ACTION (action), is_active);
1444 modest_msg_view_window_find_toolbar_close (GtkWidget *widget,
1445 ModestMsgViewWindow *obj)
1447 GtkToggleAction *toggle;
1448 ModestWindowPrivate *parent_priv;
1449 ModestMsgViewWindowPrivate *priv;
1451 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (obj);
1452 parent_priv = MODEST_WINDOW_GET_PRIVATE (obj);
1454 toggle = GTK_TOGGLE_ACTION (gtk_ui_manager_get_action (parent_priv->ui_manager, "/ToolBar/FindInMessage"));
1455 gtk_toggle_action_set_active (toggle, FALSE);
1456 modest_msg_view_grab_focus (MODEST_MSG_VIEW (priv->msg_view));
1460 modest_msg_view_window_find_toolbar_search (GtkWidget *widget,
1461 ModestMsgViewWindow *obj)
1463 gchar *current_search;
1464 ModestMsgViewWindowPrivate *priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (obj);
1466 if (modest_mime_part_view_is_empty (MODEST_MIME_PART_VIEW (priv->msg_view))) {
1467 hildon_banner_show_information (NULL, NULL, _("mail_ib_nothing_to_find"));
1471 g_object_get (G_OBJECT (widget), "prefix", ¤t_search, NULL);
1473 if ((current_search == NULL) || (strcmp (current_search, "") == 0)) {
1474 g_free (current_search);
1475 hildon_banner_show_information (NULL, NULL, dgettext("hildon-common-strings", "ecdg_ib_find_rep_enter_text"));
1479 if ((priv->last_search == NULL) || (strcmp (priv->last_search, current_search) != 0)) {
1481 g_free (priv->last_search);
1482 priv->last_search = g_strdup (current_search);
1483 result = modest_isearch_view_search (MODEST_ISEARCH_VIEW (priv->msg_view),
1486 hildon_banner_show_information (NULL, NULL, dgettext("hildon-libs", "ckct_ib_find_no_matches"));
1487 g_free (priv->last_search);
1488 priv->last_search = NULL;
1490 modest_msg_view_grab_focus (MODEST_MSG_VIEW (priv->msg_view));
1491 hildon_find_toolbar_highlight_entry (HILDON_FIND_TOOLBAR (priv->find_toolbar), TRUE);
1494 if (!modest_isearch_view_search_next (MODEST_ISEARCH_VIEW (priv->msg_view))) {
1495 hildon_banner_show_information (NULL, NULL, dgettext("hildon-libs", "ckct_ib_find_search_complete"));
1496 g_free (priv->last_search);
1497 priv->last_search = NULL;
1499 modest_msg_view_grab_focus (MODEST_MSG_VIEW (priv->msg_view));
1500 hildon_find_toolbar_highlight_entry (HILDON_FIND_TOOLBAR (priv->find_toolbar), TRUE);
1504 g_free (current_search);
1509 modest_msg_view_window_set_zoom (ModestWindow *window,
1512 ModestMsgViewWindowPrivate *priv;
1513 ModestWindowPrivate *parent_priv;
1514 GtkAction *action = NULL;
1515 gint int_zoom = (gint) rint (zoom*100.0+0.1);
1517 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window));
1519 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1520 parent_priv = MODEST_WINDOW_GET_PRIVATE (window);
1521 modest_zoomable_set_zoom (MODEST_ZOOMABLE (priv->msg_view), zoom);
1523 action = gtk_ui_manager_get_action (parent_priv->ui_manager,
1524 "/MenuBar/ViewMenu/ZoomMenu/Zoom50Menu");
1526 gtk_radio_action_set_current_value (GTK_RADIO_ACTION (action), int_zoom);
1530 modest_msg_view_window_get_zoom (ModestWindow *window)
1532 ModestMsgViewWindowPrivate *priv;
1534 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), 1.0);
1536 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1537 return modest_zoomable_get_zoom (MODEST_ZOOMABLE (priv->msg_view));
1541 modest_msg_view_window_zoom_plus (ModestWindow *window)
1543 ModestWindowPrivate *parent_priv;
1544 GtkRadioAction *zoom_radio_action;
1545 GSList *group, *node;
1547 parent_priv = MODEST_WINDOW_GET_PRIVATE (window);
1548 zoom_radio_action = GTK_RADIO_ACTION (gtk_ui_manager_get_action (parent_priv->ui_manager,
1549 "/MenuBar/ViewMenu/ZoomMenu/Zoom50Menu"));
1551 group = gtk_radio_action_get_group (zoom_radio_action);
1553 if (gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (group->data))) {
1554 hildon_banner_show_information (NULL, NULL, dgettext("hildon-common-strings", "ckct_ib_max_zoom_level_reached"));
1558 for (node = group; node != NULL; node = g_slist_next (node)) {
1559 if ((node->next != NULL) && gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (node->next->data))) {
1560 gtk_toggle_action_set_active (GTK_TOGGLE_ACTION (node->data), TRUE);
1568 modest_msg_view_window_zoom_minus (ModestWindow *window)
1570 ModestWindowPrivate *parent_priv;
1571 GtkRadioAction *zoom_radio_action;
1572 GSList *group, *node;
1574 parent_priv = MODEST_WINDOW_GET_PRIVATE (window);
1575 zoom_radio_action = GTK_RADIO_ACTION (gtk_ui_manager_get_action (parent_priv->ui_manager,
1576 "/MenuBar/ViewMenu/ZoomMenu/Zoom50Menu"));
1578 group = gtk_radio_action_get_group (zoom_radio_action);
1580 for (node = group; node != NULL; node = g_slist_next (node)) {
1581 if (gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (node->data))) {
1582 if (node->next != NULL) {
1583 gtk_toggle_action_set_active (GTK_TOGGLE_ACTION (node->next->data), TRUE);
1586 hildon_banner_show_information (NULL, NULL, dgettext("hildon-common-strings", "ckct_ib_min_zoom_level_reached"));
1596 modest_msg_view_window_key_event (GtkWidget *window,
1602 focus = gtk_window_get_focus (GTK_WINDOW (window));
1604 /* for the find toolbar case */
1605 if (focus && GTK_IS_ENTRY (focus)) {
1606 if (event->keyval == GDK_BackSpace) {
1608 copy = gdk_event_copy ((GdkEvent *) event);
1609 gtk_widget_event (focus, copy);
1610 gdk_event_free (copy);
1615 if (event->keyval == GDK_Up || event->keyval == GDK_KP_Up ||
1616 event->keyval == GDK_Down || event->keyval == GDK_KP_Down ||
1617 event->keyval == GDK_Page_Up || event->keyval == GDK_KP_Page_Up ||
1618 event->keyval == GDK_Page_Down || event->keyval == GDK_KP_Page_Down ||
1619 event->keyval == GDK_Home || event->keyval == GDK_KP_Home ||
1620 event->keyval == GDK_End || event->keyval == GDK_KP_End) {
1621 /* ModestMsgViewWindowPrivate *priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window); */
1622 /* gboolean return_value; */
1624 if (event->type == GDK_KEY_PRESS) {
1625 GtkScrollType scroll_type;
1627 switch (event->keyval) {
1630 scroll_type = GTK_SCROLL_STEP_UP; break;
1633 scroll_type = GTK_SCROLL_STEP_DOWN; break;
1635 case GDK_KP_Page_Up:
1636 scroll_type = GTK_SCROLL_PAGE_UP; break;
1638 case GDK_KP_Page_Down:
1639 scroll_type = GTK_SCROLL_PAGE_DOWN; break;
1642 scroll_type = GTK_SCROLL_START; break;
1645 scroll_type = GTK_SCROLL_END; break;
1646 default: scroll_type = GTK_SCROLL_NONE;
1649 /* g_signal_emit_by_name (G_OBJECT (priv->main_scroll), "scroll-child", */
1650 /* scroll_type, FALSE, &return_value); */
1661 modest_msg_view_window_last_message_selected (ModestMsgViewWindow *window)
1664 ModestMsgViewWindowPrivate *priv;
1665 GtkTreeIter tmp_iter;
1666 gboolean is_last_selected;
1668 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), TRUE);
1669 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1671 /*if no model (so no rows at all), then virtually we are the last*/
1672 if (!priv->header_model || !priv->row_reference)
1675 if (!gtk_tree_row_reference_valid (priv->row_reference))
1678 path = gtk_tree_row_reference_get_path (priv->row_reference);
1682 is_last_selected = TRUE;
1683 while (is_last_selected) {
1685 gtk_tree_path_next (path);
1686 if (!gtk_tree_model_get_iter (priv->header_model, &tmp_iter, path))
1688 gtk_tree_model_get (priv->header_model, &tmp_iter,
1689 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
1692 if (msg_is_visible (header, priv->is_outbox))
1693 is_last_selected = FALSE;
1694 g_object_unref(G_OBJECT(header));
1697 gtk_tree_path_free (path);
1698 return is_last_selected;
1702 modest_msg_view_window_has_headers_model (ModestMsgViewWindow *window)
1704 ModestMsgViewWindowPrivate *priv;
1706 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), TRUE);
1707 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1709 return priv->header_model != NULL;
1713 modest_msg_view_window_is_search_result (ModestMsgViewWindow *window)
1715 ModestMsgViewWindowPrivate *priv;
1717 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), TRUE);
1718 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1720 return priv->is_search_result;
1724 msg_is_visible (TnyHeader *header, gboolean check_outbox)
1726 if ((tny_header_get_flags(header) & TNY_HEADER_FLAG_DELETED))
1728 if (!check_outbox) {
1731 ModestTnySendQueueStatus status;
1732 status = modest_tny_all_send_queues_get_msg_status (header);
1733 return ((status != MODEST_TNY_SEND_QUEUE_FAILED) &&
1734 (status != MODEST_TNY_SEND_QUEUE_SENDING));
1739 modest_msg_view_window_first_message_selected (ModestMsgViewWindow *window)
1742 ModestMsgViewWindowPrivate *priv;
1743 gboolean is_first_selected;
1744 GtkTreeIter tmp_iter;
1746 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), TRUE);
1747 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1749 /*if no model (so no rows at all), then virtually we are the first*/
1750 if (!priv->header_model || !priv->row_reference)
1753 if (!gtk_tree_row_reference_valid (priv->row_reference))
1756 path = gtk_tree_row_reference_get_path (priv->row_reference);
1760 is_first_selected = TRUE;
1761 while (is_first_selected) {
1763 if(!gtk_tree_path_prev (path))
1765 /* Here the 'if' is needless for logic, but let make sure
1766 * iter is valid for gtk_tree_model_get. */
1767 if (!gtk_tree_model_get_iter (priv->header_model, &tmp_iter, path))
1769 gtk_tree_model_get (priv->header_model, &tmp_iter,
1770 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
1773 if (msg_is_visible (header, priv->is_outbox))
1774 is_first_selected = FALSE;
1775 g_object_unref(G_OBJECT(header));
1778 gtk_tree_path_free (path);
1779 return is_first_selected;
1784 GtkTreeRowReference *row_reference;
1788 message_reader_performer (gboolean canceled,
1790 GtkWindow *parent_window,
1791 TnyAccount *account,
1794 ModestMailOperation *mail_op = NULL;
1795 MsgReaderInfo *info;
1797 info = (MsgReaderInfo *) user_data;
1798 if (canceled || err) {
1802 /* Register the header - it'll be unregistered in the callback */
1803 modest_window_mgr_register_header (modest_runtime_get_window_mgr (), info->header, NULL);
1805 /* New mail operation */
1806 mail_op = modest_mail_operation_new_with_error_handling (G_OBJECT(parent_window),
1807 modest_ui_actions_disk_operations_error_handler,
1810 modest_mail_operation_queue_add (modest_runtime_get_mail_operation_queue (), mail_op);
1811 modest_mail_operation_get_msg (mail_op, info->header, TRUE, view_msg_cb, info->row_reference);
1812 g_object_unref (mail_op);
1814 /* Update dimming rules */
1815 modest_ui_actions_check_toolbar_dimming_rules (MODEST_WINDOW (parent_window));
1816 modest_ui_actions_check_menu_dimming_rules (MODEST_WINDOW (parent_window));
1819 /* Frees. The row_reference will be freed by the view_msg_cb callback */
1820 g_object_unref (info->header);
1821 g_slice_free (MsgReaderInfo, info);
1826 * Reads the message whose summary item is @header. It takes care of
1827 * several things, among others:
1829 * If the message was not previously downloaded then ask the user
1830 * before downloading. If there is no connection launch the connection
1831 * dialog. Update toolbar dimming rules.
1833 * Returns: TRUE if the mail operation was started, otherwise if the
1834 * user do not want to download the message, or if the user do not
1835 * want to connect, then the operation is not issued
1838 message_reader (ModestMsgViewWindow *window,
1839 ModestMsgViewWindowPrivate *priv,
1841 GtkTreeRowReference *row_reference)
1843 gboolean already_showing = FALSE;
1844 ModestWindow *msg_window = NULL;
1845 ModestWindowMgr *mgr;
1846 TnyAccount *account;
1848 MsgReaderInfo *info;
1850 g_return_val_if_fail (row_reference != NULL, FALSE);
1852 mgr = modest_runtime_get_window_mgr ();
1853 already_showing = modest_window_mgr_find_registered_header (mgr, header, &msg_window);
1854 if (already_showing && (msg_window != MODEST_WINDOW (window))) {
1857 gtk_window_present (GTK_WINDOW (msg_window));
1858 g_signal_emit_by_name (G_OBJECT (window), "delete-event", NULL, &retval);
1862 /* Msg download completed */
1863 if (!(tny_header_get_flags (header) & TNY_HEADER_FLAG_CACHED)) {
1864 /* Ask the user if he wants to download the message if
1866 if (!tny_device_is_online (modest_runtime_get_device())) {
1867 GtkResponseType response;
1869 response = modest_platform_run_confirmation_dialog (GTK_WINDOW (window),
1870 _("mcen_nc_get_msg"));
1871 if (response == GTK_RESPONSE_CANCEL)
1874 folder = tny_header_get_folder (header);
1875 info = g_slice_new (MsgReaderInfo);
1876 info->header = g_object_ref (header);
1877 info->row_reference = gtk_tree_row_reference_copy (row_reference);
1879 /* Offer the connection dialog if necessary */
1880 modest_platform_connect_if_remote_and_perform ((GtkWindow *) window,
1882 TNY_FOLDER_STORE (folder),
1883 message_reader_performer,
1885 g_object_unref (folder);
1890 folder = tny_header_get_folder (header);
1891 account = tny_folder_get_account (folder);
1892 info = g_slice_new (MsgReaderInfo);
1893 info->header = g_object_ref (header);
1894 info->row_reference = gtk_tree_row_reference_copy (row_reference);
1896 message_reader_performer (FALSE, NULL, (GtkWindow *) window, account, info);
1897 g_object_unref (account);
1898 g_object_unref (folder);
1904 modest_msg_view_window_select_next_message (ModestMsgViewWindow *window)
1906 ModestMsgViewWindowPrivate *priv;
1907 GtkTreePath *path= NULL;
1908 GtkTreeIter tmp_iter;
1910 gboolean retval = TRUE;
1911 GtkTreeRowReference *row_reference = NULL;
1913 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), FALSE);
1914 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1916 if (!priv->row_reference)
1919 /* Update the next row reference if it's not valid. This could
1920 happen if for example the header which it was pointing to,
1921 was deleted. The best place to do it is in the row-deleted
1922 handler but the tinymail model do not work like the glib
1923 tree models and reports the deletion when the row is still
1925 if (!gtk_tree_row_reference_valid (priv->next_row_reference)) {
1926 if (gtk_tree_row_reference_valid (priv->row_reference)) {
1927 priv->next_row_reference = gtk_tree_row_reference_copy (priv->row_reference);
1928 select_next_valid_row (priv->header_model, &(priv->next_row_reference), FALSE, priv->is_outbox);
1931 if (priv->next_row_reference)
1932 path = gtk_tree_row_reference_get_path (priv->next_row_reference);
1936 row_reference = gtk_tree_row_reference_copy (priv->next_row_reference);
1938 gtk_tree_model_get_iter (priv->header_model,
1941 gtk_tree_path_free (path);
1943 gtk_tree_model_get (priv->header_model, &tmp_iter,
1944 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
1947 /* Read the message & show it */
1948 if (!message_reader (window, priv, header, row_reference)) {
1951 gtk_tree_row_reference_free (row_reference);
1954 g_object_unref (header);
1960 modest_msg_view_window_select_previous_message (ModestMsgViewWindow *window)
1962 ModestMsgViewWindowPrivate *priv = NULL;
1964 gboolean finished = FALSE;
1965 gboolean retval = FALSE;
1967 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), FALSE);
1968 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1970 /* Return inmediatly if there is no header model */
1971 if (!priv->header_model || !priv->row_reference)
1974 path = gtk_tree_row_reference_get_path (priv->row_reference);
1975 while (!finished && gtk_tree_path_prev (path)) {
1979 gtk_tree_model_get_iter (priv->header_model, &iter, path);
1980 gtk_tree_model_get (priv->header_model, &iter,
1981 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
1985 if (msg_is_visible (header, priv->is_outbox)) {
1986 GtkTreeRowReference *row_reference;
1987 row_reference = gtk_tree_row_reference_new (priv->header_model, path);
1988 /* Read the message & show it */
1989 retval = message_reader (window, priv, header, row_reference);
1990 gtk_tree_row_reference_free (row_reference);
1994 g_object_unref (header);
1998 gtk_tree_path_free (path);
2003 view_msg_cb (ModestMailOperation *mail_op,
2010 ModestMsgViewWindow *self = NULL;
2011 ModestMsgViewWindowPrivate *priv = NULL;
2012 GtkTreeRowReference *row_reference = NULL;
2014 /* Unregister the header (it was registered before creating the mail operation) */
2015 modest_window_mgr_unregister_header (modest_runtime_get_window_mgr (), header);
2017 row_reference = (GtkTreeRowReference *) user_data;
2019 gtk_tree_row_reference_free (row_reference);
2023 /* If there was any error */
2024 if (!modest_ui_actions_msg_retrieval_check (mail_op, header, msg)) {
2025 gtk_tree_row_reference_free (row_reference);
2029 /* Get the window */
2030 self = (ModestMsgViewWindow *) modest_mail_operation_get_source (mail_op);
2031 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self));
2032 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
2034 /* Update the row reference */
2035 if (priv->row_reference != NULL) {
2036 gtk_tree_row_reference_free (priv->row_reference);
2037 priv->row_reference = gtk_tree_row_reference_copy (row_reference);
2038 if (priv->next_row_reference != NULL) {
2039 gtk_tree_row_reference_free (priv->next_row_reference);
2041 priv->next_row_reference = gtk_tree_row_reference_copy (priv->row_reference);
2042 select_next_valid_row (priv->header_model, &(priv->next_row_reference), TRUE, priv->is_outbox);
2045 /* Mark header as read */
2046 if (!(tny_header_get_flags (header) & TNY_HEADER_FLAG_SEEN))
2047 tny_header_set_flag (header, TNY_HEADER_FLAG_SEEN);
2049 /* Set new message */
2050 if (priv->msg_view != NULL && TNY_IS_MSG_VIEW (priv->msg_view)) {
2051 tny_msg_view_set_msg (TNY_MSG_VIEW (priv->msg_view), msg);
2052 modest_msg_view_window_update_priority (self);
2053 update_window_title (MODEST_MSG_VIEW_WINDOW (self));
2054 modest_msg_view_grab_focus (MODEST_MSG_VIEW (priv->msg_view));
2057 /* Set the new message uid of the window */
2058 if (priv->msg_uid) {
2059 g_free (priv->msg_uid);
2060 priv->msg_uid = modest_tny_folder_get_header_unique_id (header);
2063 /* Notify the observers */
2064 g_signal_emit (G_OBJECT (self), signals[MSG_CHANGED_SIGNAL],
2065 0, priv->header_model, priv->row_reference);
2068 g_object_unref (self);
2069 gtk_tree_row_reference_free (row_reference);
2073 modest_msg_view_window_get_folder_type (ModestMsgViewWindow *window)
2075 ModestMsgViewWindowPrivate *priv;
2077 TnyFolderType folder_type;
2079 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
2081 folder_type = TNY_FOLDER_TYPE_UNKNOWN;
2083 msg = tny_msg_view_get_msg (TNY_MSG_VIEW (priv->msg_view));
2087 folder = tny_msg_get_folder (msg);
2089 folder_type = modest_tny_folder_guess_folder_type (folder);
2090 g_object_unref (folder);
2092 g_object_unref (msg);
2100 modest_msg_view_window_update_priority (ModestMsgViewWindow *window)
2102 ModestMsgViewWindowPrivate *priv;
2103 TnyHeader *header = NULL;
2104 TnyHeaderFlags flags = 0;
2106 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
2108 if (priv->header_model && priv->row_reference) {
2110 GtkTreePath *path = NULL;
2112 path = gtk_tree_row_reference_get_path (priv->row_reference);
2113 g_return_if_fail (path != NULL);
2114 gtk_tree_model_get_iter (priv->header_model,
2116 gtk_tree_row_reference_get_path (priv->row_reference));
2118 gtk_tree_model_get (priv->header_model, &iter, TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
2120 gtk_tree_path_free (path);
2123 msg = tny_msg_view_get_msg (TNY_MSG_VIEW (priv->msg_view));
2125 header = tny_msg_get_header (msg);
2126 g_object_unref (msg);
2131 flags = tny_header_get_flags (header);
2132 g_object_unref(G_OBJECT(header));
2135 modest_msg_view_set_priority (MODEST_MSG_VIEW(priv->msg_view), flags);
2140 toolbar_resize (ModestMsgViewWindow *self)
2142 ModestMsgViewWindowPrivate *priv = NULL;
2143 ModestWindowPrivate *parent_priv = NULL;
2145 gint static_button_size;
2146 ModestWindowMgr *mgr;
2148 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self));
2149 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
2150 parent_priv = MODEST_WINDOW_GET_PRIVATE(self);
2152 mgr = modest_runtime_get_window_mgr ();
2153 static_button_size = modest_window_mgr_get_fullscreen_mode (mgr)?118:108;
2155 if (parent_priv->toolbar) {
2156 /* left size buttons */
2157 widget = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/ToolbarMessageReply");
2158 gtk_tool_item_set_expand (GTK_TOOL_ITEM (widget), FALSE);
2159 gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (widget), FALSE);
2160 gtk_widget_set_size_request (GTK_WIDGET (widget), static_button_size, -1);
2161 widget = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/ToolbarMessageMoveTo");
2162 gtk_tool_item_set_expand (GTK_TOOL_ITEM (widget), FALSE);
2163 gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (widget), FALSE);
2164 gtk_widget_set_size_request (GTK_WIDGET (widget), static_button_size, -1);
2165 widget = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/ToolbarDeleteMessage");
2166 gtk_tool_item_set_expand (GTK_TOOL_ITEM (widget), FALSE);
2167 gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (widget), FALSE);
2168 gtk_widget_set_size_request (GTK_WIDGET (widget), static_button_size, -1);
2169 widget = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/FindInMessage");
2170 gtk_tool_item_set_expand (GTK_TOOL_ITEM (widget), FALSE);
2171 gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (widget), FALSE);
2172 gtk_widget_set_size_request (GTK_WIDGET (widget), static_button_size, -1);
2174 gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (priv->progress_toolitem), FALSE);
2175 gtk_tool_item_set_expand (GTK_TOOL_ITEM (priv->progress_toolitem), TRUE);
2176 gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (priv->cancel_toolitem), FALSE);
2177 gtk_tool_item_set_expand (GTK_TOOL_ITEM (priv->cancel_toolitem), FALSE);
2178 gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (priv->next_toolitem), TRUE);
2179 gtk_tool_item_set_expand (GTK_TOOL_ITEM (priv->next_toolitem), TRUE);
2180 gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (priv->prev_toolitem), TRUE);
2181 gtk_tool_item_set_expand (GTK_TOOL_ITEM (priv->prev_toolitem), TRUE);
2187 modest_msg_view_window_window_state_event (GtkWidget *widget, GdkEventWindowState *event, gpointer userdata)
2189 if (event->changed_mask & GDK_WINDOW_STATE_FULLSCREEN) {
2190 ModestWindowPrivate *parent_priv;
2191 ModestWindowMgr *mgr;
2192 gboolean is_fullscreen;
2193 GtkAction *fs_toggle_action;
2196 mgr = modest_runtime_get_window_mgr ();
2197 is_fullscreen = (modest_window_mgr_get_fullscreen_mode (mgr))?1:0;
2199 parent_priv = MODEST_WINDOW_GET_PRIVATE (widget);
2201 fs_toggle_action = gtk_ui_manager_get_action (parent_priv->ui_manager, "/MenuBar/ViewMenu/ViewToggleFullscreenMenu");
2202 active = (gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (fs_toggle_action)))?1:0;
2203 if (is_fullscreen != active) {
2204 gtk_toggle_action_set_active (GTK_TOGGLE_ACTION (fs_toggle_action), is_fullscreen);
2206 toolbar_resize (MODEST_MSG_VIEW_WINDOW (widget));
2214 modest_msg_view_window_show_toolbar (ModestWindow *self,
2215 gboolean show_toolbar)
2217 ModestMsgViewWindowPrivate *priv = NULL;
2218 ModestWindowPrivate *parent_priv;
2219 GtkWidget *reply_button = NULL, *menu = NULL;
2220 GtkWidget *placeholder = NULL;
2222 const gchar *action_name;
2225 parent_priv = MODEST_WINDOW_GET_PRIVATE(self);
2226 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(self);
2228 /* Set optimized view status */
2229 priv->optimized_view = !show_toolbar;
2231 if (!parent_priv->toolbar) {
2232 parent_priv->toolbar = gtk_ui_manager_get_widget (parent_priv->ui_manager,
2234 gtk_widget_set_no_show_all (parent_priv->toolbar, TRUE);
2236 priv->progress_toolitem = GTK_WIDGET (gtk_tool_item_new ());
2237 priv->cancel_toolitem = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/ToolbarCancel");
2238 priv->next_toolitem = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/ToolbarMessageNext");
2239 priv->prev_toolitem = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/ToolbarMessageBack");
2240 toolbar_resize (MODEST_MSG_VIEW_WINDOW (self));
2242 /* Add ProgressBar (Transfer toolbar) */
2243 priv->progress_bar = modest_progress_bar_new ();
2244 gtk_widget_set_no_show_all (priv->progress_bar, TRUE);
2245 placeholder = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/ProgressbarView");
2246 insert_index = gtk_toolbar_get_item_index(GTK_TOOLBAR (parent_priv->toolbar), GTK_TOOL_ITEM(placeholder));
2247 gtk_container_add (GTK_CONTAINER (priv->progress_toolitem), priv->progress_bar);
2248 gtk_toolbar_insert(GTK_TOOLBAR(parent_priv->toolbar), GTK_TOOL_ITEM (priv->progress_toolitem), insert_index);
2250 /* Connect cancel 'clicked' signal to abort progress mode */
2251 g_signal_connect(priv->cancel_toolitem, "clicked",
2252 G_CALLBACK(cancel_progressbar),
2255 /* Add it to the observers list */
2256 priv->progress_widgets = g_slist_prepend(priv->progress_widgets, priv->progress_bar);
2259 hildon_window_add_toolbar (HILDON_WINDOW (self),
2260 GTK_TOOLBAR (parent_priv->toolbar));
2262 /* Set reply button tap and hold menu */
2263 reply_button = gtk_ui_manager_get_widget (parent_priv->ui_manager,
2264 "/ToolBar/ToolbarMessageReply");
2265 menu = gtk_ui_manager_get_widget (parent_priv->ui_manager,
2266 "/ToolbarReplyCSM");
2267 gtk_widget_tap_and_hold_setup (GTK_WIDGET (reply_button), menu, NULL, 0);
2271 /* Quick hack: this prevents toolbar icons "dance" when progress bar show status is changed */
2272 /* TODO: resize mode migth be GTK_RESIZE_QUEUE, in order to avoid unneccesary shows */
2273 gtk_container_set_resize_mode (GTK_CONTAINER(parent_priv->toolbar), GTK_RESIZE_IMMEDIATE);
2275 gtk_widget_show (GTK_WIDGET (parent_priv->toolbar));
2276 if (modest_msg_view_window_transfer_mode_enabled (MODEST_MSG_VIEW_WINDOW (self)))
2277 set_toolbar_mode (MODEST_MSG_VIEW_WINDOW (self), TOOLBAR_MODE_TRANSFER);
2279 set_toolbar_mode (MODEST_MSG_VIEW_WINDOW (self), TOOLBAR_MODE_NORMAL);
2282 gtk_widget_set_no_show_all (parent_priv->toolbar, TRUE);
2283 gtk_widget_hide (GTK_WIDGET (parent_priv->toolbar));
2286 /* Update also the actions (to update the toggles in the
2287 menus), we have to do it manually because some other window
2288 of the same time could have changed it (remember that the
2289 toolbar fullscreen mode is shared by all the windows of the
2291 if (modest_window_mgr_get_fullscreen_mode (modest_runtime_get_window_mgr ()))
2292 action_name = "/MenuBar/ViewMenu/ViewShowToolbarMenu/ViewShowToolbarFullScreenMenu";
2294 action_name = "/MenuBar/ViewMenu/ViewShowToolbarMenu/ViewShowToolbarNormalScreenMenu";
2296 action = gtk_ui_manager_get_action (parent_priv->ui_manager, action_name);
2297 modest_utils_toggle_action_set_active_block_notify (GTK_TOGGLE_ACTION (action),
2302 modest_msg_view_window_clipboard_owner_change (GtkClipboard *clipboard,
2304 ModestMsgViewWindow *window)
2306 if (!GTK_WIDGET_VISIBLE (window))
2309 modest_window_check_dimming_rules_group (MODEST_WINDOW (window), MODEST_DIMMING_RULES_CLIPBOARD);
2313 modest_msg_view_window_transfer_mode_enabled (ModestMsgViewWindow *self)
2315 ModestMsgViewWindowPrivate *priv;
2317 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self), FALSE);
2318 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(self);
2320 return priv->current_toolbar_mode == TOOLBAR_MODE_TRANSFER;
2324 cancel_progressbar (GtkToolButton *toolbutton,
2325 ModestMsgViewWindow *self)
2328 ModestMsgViewWindowPrivate *priv;
2330 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(self);
2332 /* Get operation observers and cancel its current operation */
2333 tmp = priv->progress_widgets;
2335 modest_progress_object_cancel_current_operation (MODEST_PROGRESS_OBJECT(tmp->data));
2336 tmp=g_slist_next(tmp);
2340 observers_empty (ModestMsgViewWindow *self)
2343 ModestMsgViewWindowPrivate *priv;
2344 gboolean is_empty = TRUE;
2345 guint pending_ops = 0;
2347 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(self);
2348 tmp = priv->progress_widgets;
2350 /* Check all observers */
2351 while (tmp && is_empty) {
2352 pending_ops = modest_progress_object_num_pending_operations (MODEST_PROGRESS_OBJECT(tmp->data));
2353 is_empty = pending_ops == 0;
2355 tmp = g_slist_next(tmp);
2362 on_account_removed (TnyAccountStore *account_store,
2363 TnyAccount *account,
2366 /* Do nothing if it's a transport account, because we only
2367 show the messages of a store account */
2368 if (tny_account_get_account_type(account) == TNY_ACCOUNT_TYPE_STORE) {
2369 const gchar *parent_acc = NULL;
2370 const gchar *our_acc = NULL;
2372 our_acc = modest_window_get_active_account (MODEST_WINDOW (user_data));
2373 parent_acc = modest_tny_account_get_parent_modest_account_name_for_server_account (account);
2375 /* Close this window if I'm showing a message of the removed account */
2376 if (strcmp (parent_acc, our_acc) == 0)
2377 modest_ui_actions_on_close_window (NULL, MODEST_WINDOW (user_data));
2382 on_mail_operation_started (ModestMailOperation *mail_op,
2385 ModestMsgViewWindow *self;
2386 ModestMailOperationTypeOperation op_type;
2388 ModestMsgViewWindowPrivate *priv;
2389 GObject *source = NULL;
2391 self = MODEST_MSG_VIEW_WINDOW (user_data);
2392 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
2393 op_type = modest_mail_operation_get_type_operation (mail_op);
2394 tmp = priv->progress_widgets;
2395 source = modest_mail_operation_get_source(mail_op);
2396 if (G_OBJECT (self) == source) {
2397 if (op_type == MODEST_MAIL_OPERATION_TYPE_RECEIVE ) {
2398 set_toolbar_transfer_mode(self);
2400 modest_progress_object_add_operation (
2401 MODEST_PROGRESS_OBJECT (tmp->data),
2403 tmp = g_slist_next (tmp);
2407 g_object_unref (source);
2411 on_mail_operation_finished (ModestMailOperation *mail_op,
2414 ModestMsgViewWindow *self;
2415 ModestMailOperationTypeOperation op_type;
2417 ModestMsgViewWindowPrivate *priv;
2419 self = MODEST_MSG_VIEW_WINDOW (user_data);
2420 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
2421 op_type = modest_mail_operation_get_type_operation (mail_op);
2422 tmp = priv->progress_widgets;
2424 if (op_type == MODEST_MAIL_OPERATION_TYPE_RECEIVE ) {
2426 modest_progress_object_remove_operation (MODEST_PROGRESS_OBJECT (tmp->data),
2428 tmp = g_slist_next (tmp);
2431 /* If no more operations are being observed, NORMAL mode is enabled again */
2432 if (observers_empty (self)) {
2433 set_toolbar_mode (self, TOOLBAR_MODE_NORMAL);
2436 /* Update dimming rules. We have to do this right here
2437 and not in view_msg_cb because at that point the
2438 transfer mode is still enabled so the dimming rule
2439 won't let the user delete the message that has been
2440 readed for example */
2441 modest_ui_actions_check_toolbar_dimming_rules (MODEST_WINDOW (self));
2442 modest_ui_actions_check_menu_dimming_rules (MODEST_WINDOW (self));
2447 on_queue_changed (ModestMailOperationQueue *queue,
2448 ModestMailOperation *mail_op,
2449 ModestMailOperationQueueNotification type,
2450 ModestMsgViewWindow *self)
2452 ModestMsgViewWindowPrivate *priv;
2454 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
2456 /* If this operations was created by another window, do nothing */
2457 if (!modest_mail_operation_is_mine (mail_op, G_OBJECT(self)))
2460 if (type == MODEST_MAIL_OPERATION_QUEUE_OPERATION_ADDED) {
2461 priv->sighandlers = modest_signal_mgr_connect (priv->sighandlers,
2463 "operation-started",
2464 G_CALLBACK (on_mail_operation_started),
2466 priv->sighandlers = modest_signal_mgr_connect (priv->sighandlers,
2468 "operation-finished",
2469 G_CALLBACK (on_mail_operation_finished),
2471 } else if (type == MODEST_MAIL_OPERATION_QUEUE_OPERATION_REMOVED) {
2472 priv->sighandlers = modest_signal_mgr_disconnect (priv->sighandlers,
2474 "operation-started");
2475 priv->sighandlers = modest_signal_mgr_disconnect (priv->sighandlers,
2477 "operation-finished");
2482 modest_msg_view_window_get_attachments (ModestMsgViewWindow *win)
2484 ModestMsgViewWindowPrivate *priv;
2485 TnyList *selected_attachments = NULL;
2487 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (win), NULL);
2488 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (win);
2490 selected_attachments = modest_msg_view_get_selected_attachments (MODEST_MSG_VIEW (priv->msg_view));
2492 return selected_attachments;
2498 guint banner_idle_id;
2499 } DecodeAsyncHelper;
2502 decode_async_banner_idle (gpointer user_data)
2504 DecodeAsyncHelper *helper = (DecodeAsyncHelper *) user_data;
2506 helper->banner_idle_id = 0;
2507 helper->banner = hildon_banner_show_animation (NULL, NULL, _("mail_me_opening"));
2508 g_object_ref (helper->banner);
2514 on_decode_to_stream_async_handler (TnyMimePart *mime_part,
2520 DecodeAsyncHelper *helper = (DecodeAsyncHelper *) user_data;
2522 if (helper->banner_idle_id > 0) {
2523 g_source_remove (helper->banner_idle_id);
2524 helper->banner_idle_id = 0;
2526 if (helper->banner) {
2527 gtk_widget_destroy (helper->banner);
2529 if (cancelled || err) {
2530 modest_platform_information_banner (NULL, NULL,
2531 _("mail_ib_file_operation_failed"));
2535 /* make the file read-only */
2536 g_chmod(helper->filepath, 0444);
2538 /* Activate the file */
2539 modest_platform_activate_file (helper->filepath, tny_mime_part_get_content_type (mime_part));
2543 g_free (helper->filepath);
2544 g_object_unref (helper->banner);
2545 g_slice_free (DecodeAsyncHelper, helper);
2549 modest_msg_view_window_view_attachment (ModestMsgViewWindow *window,
2550 TnyMimePart *mime_part)
2552 ModestMsgViewWindowPrivate *priv;
2553 const gchar *msg_uid;
2554 gchar *attachment_uid = NULL;
2555 gint attachment_index = 0;
2556 TnyList *attachments;
2558 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window));
2559 g_return_if_fail (TNY_IS_MIME_PART (mime_part) || (mime_part == NULL));
2560 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
2562 msg_uid = modest_msg_view_window_get_message_uid (MODEST_MSG_VIEW_WINDOW (window));
2563 attachments = modest_msg_view_get_attachments (MODEST_MSG_VIEW (priv->msg_view));
2564 attachment_index = modest_list_index (attachments, (GObject *) mime_part);
2565 g_object_unref (attachments);
2567 if (msg_uid && attachment_index >= 0) {
2568 attachment_uid = g_strdup_printf ("%s/%d", msg_uid, attachment_index);
2571 if (mime_part == NULL) {
2572 gboolean error = FALSE;
2573 TnyList *selected_attachments = modest_msg_view_get_selected_attachments (MODEST_MSG_VIEW (priv->msg_view));
2574 if (selected_attachments == NULL || tny_list_get_length (selected_attachments) == 0) {
2576 } else if (tny_list_get_length (selected_attachments) > 1) {
2577 hildon_banner_show_information (NULL, NULL, _("mcen_ib_unable_to_display_more"));
2581 iter = tny_list_create_iterator (selected_attachments);
2582 mime_part = (TnyMimePart *) tny_iterator_get_current (iter);
2583 g_object_unref (iter);
2585 g_object_unref (selected_attachments);
2590 g_object_ref (mime_part);
2593 if (tny_mime_part_is_purged (mime_part)) {
2594 g_object_unref (mime_part);
2598 if (!modest_tny_mime_part_is_msg (mime_part)) {
2599 gchar *filepath = NULL;
2600 const gchar *att_filename = tny_mime_part_get_filename (mime_part);
2601 gboolean show_error_banner = FALSE;
2602 TnyFsStream *temp_stream = NULL;
2603 temp_stream = modest_utils_create_temp_stream (att_filename, attachment_uid,
2606 if (temp_stream != NULL) {
2607 DecodeAsyncHelper *helper = g_slice_new (DecodeAsyncHelper);
2608 helper->filepath = g_strdup (filepath);
2609 helper->banner = NULL;
2610 helper->banner_idle_id = g_timeout_add (1000, decode_async_banner_idle, helper);
2611 tny_mime_part_decode_to_stream_async (mime_part, TNY_STREAM (temp_stream),
2612 on_decode_to_stream_async_handler,
2615 g_object_unref (temp_stream);
2616 /* NOTE: files in the temporary area will be automatically
2617 * cleaned after some time if they are no longer in use */
2620 const gchar *content_type;
2621 /* the file may already exist but it isn't writable,
2622 * let's try to open it anyway */
2623 content_type = tny_mime_part_get_content_type (mime_part);
2624 modest_platform_activate_file (filepath, content_type);
2626 g_warning ("%s: modest_utils_create_temp_stream failed", __FUNCTION__);
2627 show_error_banner = TRUE;
2632 if (show_error_banner)
2633 modest_platform_information_banner (NULL, NULL, _("mail_ib_file_operation_failed"));
2635 /* message attachment */
2636 TnyHeader *header = NULL;
2637 ModestWindowMgr *mgr;
2638 ModestWindow *msg_win = NULL;
2641 header = tny_msg_get_header (TNY_MSG (mime_part));
2642 mgr = modest_runtime_get_window_mgr ();
2643 found = modest_window_mgr_find_registered_header (mgr, header, &msg_win);
2646 if (msg_win) /* there is already a window for this uid; top it */
2647 gtk_window_present (GTK_WINDOW(msg_win));
2649 /* if it's found, but there is no msg_win, it's probably in the process of being created;
2650 * thus, we don't do anything */
2651 g_warning ("window for is already being created");
2653 /* it's not found, so create a new window for it */
2654 modest_window_mgr_register_header (mgr, header, attachment_uid); /* register the uid before building the window */
2655 gchar *account = g_strdup (modest_window_get_active_account (MODEST_WINDOW (window)));
2657 account = modest_account_mgr_get_default_account (modest_runtime_get_account_mgr ());
2658 msg_win = modest_msg_view_window_new_for_attachment (TNY_MSG (mime_part), account, attachment_uid);
2659 modest_window_set_zoom (MODEST_WINDOW (msg_win),
2660 modest_window_get_zoom (MODEST_WINDOW (window)));
2661 modest_window_mgr_register_window (mgr, msg_win);
2662 gtk_widget_show_all (GTK_WIDGET (msg_win));
2665 g_object_unref (mime_part);
2678 GnomeVFSResult result;
2681 static void save_mime_part_info_free (SaveMimePartInfo *info, gboolean with_struct);
2682 static gboolean idle_save_mime_part_show_result (SaveMimePartInfo *info);
2683 static gpointer save_mime_part_to_file (SaveMimePartInfo *info);
2684 static void save_mime_parts_to_file_with_checks (SaveMimePartInfo *info);
2687 save_mime_part_info_free (SaveMimePartInfo *info, gboolean with_struct)
2691 for (node = info->pairs; node != NULL; node = g_list_next (node)) {
2692 SaveMimePartPair *pair = (SaveMimePartPair *) node->data;
2693 g_free (pair->filename);
2694 g_object_unref (pair->part);
2695 g_slice_free (SaveMimePartPair, pair);
2697 g_list_free (info->pairs);
2700 gtk_widget_destroy (info->banner);
2701 g_slice_free (SaveMimePartInfo, info);
2706 idle_save_mime_part_show_result (SaveMimePartInfo *info)
2708 if (info->pairs != NULL) {
2709 save_mime_part_to_file (info);
2711 /* This is a GDK lock because we are an idle callback and
2712 * hildon_banner_show_information is or does Gtk+ code */
2714 gdk_threads_enter (); /* CHECKED */
2715 save_mime_part_info_free (info, TRUE);
2716 if (info->result == GNOME_VFS_OK) {
2717 hildon_banner_show_information (NULL, NULL, _CS("sfil_ib_saved"));
2718 } else if (info->result == GNOME_VFS_ERROR_NO_SPACE) {
2719 hildon_banner_show_information (NULL, NULL, dgettext("ke-recv",
2720 "cerm_device_memory_full"));
2722 hildon_banner_show_information (NULL, NULL, _("mail_ib_file_operation_failed"));
2724 gdk_threads_leave (); /* CHECKED */
2731 save_mime_part_to_file (SaveMimePartInfo *info)
2733 GnomeVFSHandle *handle;
2735 SaveMimePartPair *pair = (SaveMimePartPair *) info->pairs->data;
2737 info->result = gnome_vfs_create (&handle, pair->filename, GNOME_VFS_OPEN_WRITE, FALSE, 0644);
2738 if (info->result == GNOME_VFS_OK) {
2739 GError *error = NULL;
2740 stream = tny_vfs_stream_new (handle);
2741 if (tny_mime_part_decode_to_stream (pair->part, stream, &error) < 0) {
2742 g_warning ("modest: could not save attachment %s: %d (%s)\n", pair->filename, error?error->code:-1, error?error->message:"Unknown error");
2744 info->result = GNOME_VFS_ERROR_IO;
2746 g_object_unref (G_OBJECT (stream));
2747 g_object_unref (pair->part);
2748 g_slice_free (SaveMimePartPair, pair);
2749 info->pairs = g_list_delete_link (info->pairs, info->pairs);
2751 g_warning ("modest: could not create save attachment %s: %s\n", pair->filename, gnome_vfs_result_to_string (info->result));
2752 save_mime_part_info_free (info, FALSE);
2755 g_idle_add ((GSourceFunc) idle_save_mime_part_show_result, info);
2760 save_mime_parts_to_file_with_checks (SaveMimePartInfo *info)
2762 gboolean is_ok = TRUE;
2763 gint replaced_files = 0;
2764 const GList *files = info->pairs;
2767 for (iter = files; (iter != NULL) && (replaced_files < 2); iter = g_list_next(iter)) {
2768 SaveMimePartPair *pair = iter->data;
2769 if (modest_utils_file_exists (pair->filename)) {
2773 if (replaced_files) {
2774 GtkWidget *confirm_overwrite_dialog;
2775 const gchar *message = (replaced_files == 1) ?
2776 _FM("docm_nc_replace_file") : _FM("docm_nc_replace_multiple");
2777 confirm_overwrite_dialog = hildon_note_new_confirmation (NULL, message);
2778 if (gtk_dialog_run (GTK_DIALOG (confirm_overwrite_dialog)) != GTK_RESPONSE_OK) {
2781 gtk_widget_destroy (confirm_overwrite_dialog);
2785 save_mime_part_info_free (info, TRUE);
2787 GtkWidget *banner = hildon_banner_show_animation (NULL, NULL,
2788 _CS("sfil_ib_saving"));
2789 info->banner = banner;
2790 g_thread_create ((GThreadFunc)save_mime_part_to_file, info, FALSE, NULL);
2796 save_attachments_response (GtkDialog *dialog,
2800 TnyList *mime_parts;
2802 GList *files_to_save = NULL;
2804 mime_parts = TNY_LIST (user_data);
2806 if (arg1 != GTK_RESPONSE_OK)
2809 chooser_uri = gtk_file_chooser_get_uri (GTK_FILE_CHOOSER (dialog));
2811 if (!modest_utils_folder_writable (chooser_uri)) {
2812 hildon_banner_show_information
2813 (NULL, NULL, dgettext("hildon-fm", "sfil_ib_readonly_location"));
2817 iter = tny_list_create_iterator (mime_parts);
2818 while (!tny_iterator_is_done (iter)) {
2819 TnyMimePart *mime_part = (TnyMimePart *) tny_iterator_get_current (iter);
2821 if ((modest_tny_mime_part_is_attachment_for_modest (mime_part)) &&
2822 !tny_mime_part_is_purged (mime_part) &&
2823 (tny_mime_part_get_filename (mime_part) != NULL)) {
2824 SaveMimePartPair *pair;
2826 pair = g_slice_new0 (SaveMimePartPair);
2828 if (tny_list_get_length (mime_parts) > 1) {
2830 gnome_vfs_escape_slashes (tny_mime_part_get_filename (mime_part));
2831 pair->filename = g_build_filename (chooser_uri, escaped, NULL);
2834 pair->filename = g_strdup (chooser_uri);
2836 pair->part = mime_part;
2837 files_to_save = g_list_prepend (files_to_save, pair);
2839 tny_iterator_next (iter);
2841 g_object_unref (iter);
2843 g_free (chooser_uri);
2845 if (files_to_save != NULL) {
2846 SaveMimePartInfo *info = g_slice_new0 (SaveMimePartInfo);
2847 info->pairs = files_to_save;
2848 info->result = TRUE;
2849 save_mime_parts_to_file_with_checks (info);
2853 /* Free and close the dialog */
2854 g_object_unref (mime_parts);
2855 gtk_widget_destroy (GTK_WIDGET (dialog));
2859 modest_msg_view_window_save_attachments (ModestMsgViewWindow *window, TnyList *mime_parts)
2861 ModestMsgViewWindowPrivate *priv;
2862 GtkWidget *save_dialog = NULL;
2863 gchar *folder = NULL;
2864 gchar *filename = NULL;
2865 gchar *save_multiple_str = NULL;
2867 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window));
2868 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
2870 if (mime_parts == NULL) {
2871 mime_parts = modest_msg_view_get_selected_attachments (MODEST_MSG_VIEW (priv->msg_view));
2872 if (mime_parts == NULL || tny_list_get_length (mime_parts) == 0)
2875 g_object_ref (mime_parts);
2878 /* prepare dialog */
2879 if (tny_list_get_length (mime_parts) == 1) {
2881 /* only one attachment selected */
2882 iter = tny_list_create_iterator (mime_parts);
2883 TnyMimePart *mime_part = (TnyMimePart *) tny_iterator_get_current (iter);
2884 g_object_unref (iter);
2885 if (!modest_tny_mime_part_is_msg (mime_part) &&
2886 modest_tny_mime_part_is_attachment_for_modest (mime_part) &&
2887 !tny_mime_part_is_purged (mime_part)) {
2888 filename = g_strdup (tny_mime_part_get_filename (mime_part));
2890 /* TODO: show any error? */
2891 g_warning ("Tried to save a non-file attachment");
2892 g_object_unref (mime_parts);
2895 g_object_unref (mime_part);
2897 save_multiple_str = g_strdup_printf (_FM("sfil_va_number_of_objects_attachments"),
2898 tny_list_get_length (mime_parts));
2901 save_dialog = hildon_file_chooser_dialog_new (GTK_WINDOW (window),
2902 GTK_FILE_CHOOSER_ACTION_SAVE);
2905 folder = g_build_filename (g_get_home_dir (), DEFAULT_FOLDER, NULL);
2906 gtk_file_chooser_set_current_folder (GTK_FILE_CHOOSER (save_dialog), folder);
2911 gtk_file_chooser_set_current_name (GTK_FILE_CHOOSER (save_dialog),
2916 /* if multiple, set multiple string */
2917 if (save_multiple_str) {
2918 g_object_set (G_OBJECT (save_dialog), "save-multiple", save_multiple_str, NULL);
2919 gtk_window_set_title (GTK_WINDOW (save_dialog), _FM("sfil_ti_save_objects_files"));
2922 /* We must run this asynchronously, because the hildon dialog
2923 performs a gtk_dialog_run by itself which leads to gdk
2925 g_signal_connect (save_dialog, "response",
2926 G_CALLBACK (save_attachments_response), mime_parts);
2928 gtk_widget_show_all (save_dialog);
2932 show_remove_attachment_information (gpointer userdata)
2934 ModestMsgViewWindow *window = (ModestMsgViewWindow *) userdata;
2935 ModestMsgViewWindowPrivate *priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
2937 /* We're outside the main lock */
2938 gdk_threads_enter ();
2940 if (priv->remove_attachment_banner != NULL) {
2941 gtk_widget_destroy (priv->remove_attachment_banner);
2942 g_object_unref (priv->remove_attachment_banner);
2945 priv->remove_attachment_banner = g_object_ref (
2946 hildon_banner_show_animation (NULL, NULL, _("mcen_ib_removing_attachment")));
2948 gdk_threads_leave ();
2954 modest_msg_view_window_remove_attachments (ModestMsgViewWindow *window, gboolean get_all)
2956 ModestMsgViewWindowPrivate *priv;
2957 TnyList *mime_parts = NULL;
2958 gchar *confirmation_message;
2964 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window));
2965 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
2968 mime_parts = modest_msg_view_get_attachments (MODEST_MSG_VIEW (priv->msg_view));
2970 mime_parts = modest_msg_view_get_selected_attachments (MODEST_MSG_VIEW (priv->msg_view));
2972 /* Remove already purged messages from mime parts list */
2973 iter = tny_list_create_iterator (mime_parts);
2974 while (!tny_iterator_is_done (iter)) {
2975 TnyMimePart *part = TNY_MIME_PART (tny_iterator_get_current (iter));
2976 tny_iterator_next (iter);
2977 if (tny_mime_part_is_purged (part)) {
2978 tny_list_remove (mime_parts, (GObject *) part);
2980 g_object_unref (part);
2982 g_object_unref (iter);
2984 if (tny_list_get_length (mime_parts) == 0) {
2985 g_object_unref (mime_parts);
2989 n_attachments = tny_list_get_length (mime_parts);
2990 if (n_attachments == 1) {
2994 iter = tny_list_create_iterator (mime_parts);
2995 part = (TnyMimePart *) tny_iterator_get_current (iter);
2996 g_object_unref (iter);
2997 if (modest_tny_mime_part_is_msg (part)) {
2999 header = tny_msg_get_header (TNY_MSG (part));
3000 filename = tny_header_dup_subject (header);
3001 g_object_unref (header);
3002 if (filename == NULL)
3003 filename = g_strdup (_("mail_va_no_subject"));
3005 filename = g_strdup (tny_mime_part_get_filename (TNY_MIME_PART (part)));
3007 confirmation_message = g_strdup_printf (_("mcen_nc_purge_file_text"), filename);
3009 g_object_unref (part);
3011 confirmation_message = g_strdup_printf (ngettext("mcen_nc_purge_file_text",
3012 "mcen_nc_purge_files_text",
3013 n_attachments), n_attachments);
3015 response = modest_platform_run_confirmation_dialog (GTK_WINDOW (window),
3016 confirmation_message);
3017 g_free (confirmation_message);
3019 if (response != GTK_RESPONSE_OK) {
3020 g_object_unref (mime_parts);
3024 priv->purge_timeout = g_timeout_add (2000, show_remove_attachment_information, window);
3026 iter = tny_list_create_iterator (mime_parts);
3027 while (!tny_iterator_is_done (iter)) {
3030 part = (TnyMimePart *) tny_iterator_get_current (iter);
3031 tny_mime_part_set_purged (TNY_MIME_PART (part));
3032 g_object_unref (part);
3033 tny_iterator_next (iter);
3035 g_object_unref (iter);
3037 msg = tny_msg_view_get_msg (TNY_MSG_VIEW (priv->msg_view));
3038 tny_msg_view_clear (TNY_MSG_VIEW (priv->msg_view));
3039 tny_msg_rewrite_cache (msg);
3040 tny_msg_view_set_msg (TNY_MSG_VIEW (priv->msg_view), msg);
3041 g_object_unref (msg);
3043 g_object_unref (mime_parts);
3045 if (priv->purge_timeout > 0) {
3046 g_source_remove (priv->purge_timeout);
3047 priv->purge_timeout = 0;
3050 if (priv->remove_attachment_banner) {
3051 gtk_widget_destroy (priv->remove_attachment_banner);
3052 g_object_unref (priv->remove_attachment_banner);
3053 priv->remove_attachment_banner = NULL;
3061 update_window_title (ModestMsgViewWindow *window)
3063 ModestMsgViewWindowPrivate *priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
3065 TnyHeader *header = NULL;
3066 gchar *subject = NULL;
3068 msg = tny_msg_view_get_msg (TNY_MSG_VIEW (priv->msg_view));
3071 header = tny_msg_get_header (msg);
3072 subject = tny_header_dup_subject (header);
3073 g_object_unref (header);
3074 g_object_unref (msg);
3077 if ((subject == NULL)||(subject[0] == '\0')) {
3079 subject = g_strdup (_("mail_va_no_subject"));
3082 gtk_window_set_title (GTK_WINDOW (window), subject);
3086 static void on_move_focus (GtkWidget *widget,
3087 GtkDirectionType direction,
3090 g_signal_stop_emission_by_name (G_OBJECT (widget), "move-focus");
3094 fetch_image_open_stream (TnyStreamCache *self, gint64 *expected_size, gchar *uri)
3096 GnomeVFSResult result;
3097 GnomeVFSHandle *handle = NULL;
3098 GnomeVFSFileInfo *info = NULL;
3101 result = gnome_vfs_open (&handle, uri, GNOME_VFS_OPEN_READ);
3102 if (result != GNOME_VFS_OK) {
3107 info = gnome_vfs_file_info_new ();
3108 result = gnome_vfs_get_file_info_from_handle (handle, info, GNOME_VFS_FILE_INFO_DEFAULT);
3109 if (result != GNOME_VFS_OK || ! (info->valid_fields & GNOME_VFS_FILE_INFO_FIELDS_SIZE)) {
3110 /* We put a "safe" default size for going to cache */
3111 *expected_size = (300*1024);
3113 *expected_size = info->size;
3115 gnome_vfs_file_info_unref (info);
3117 stream = tny_vfs_stream_new (handle);
3126 TnyStream *output_stream;
3127 GtkWidget *msg_view;
3131 on_fetch_image_idle_refresh_view (gpointer userdata)
3134 FetchImageData *fidata = (FetchImageData *) userdata;
3135 g_message ("REFRESH VIEW");
3136 if (GTK_WIDGET_DRAWABLE (fidata->msg_view)) {
3137 g_message ("QUEUING DRAW");
3138 gtk_widget_queue_draw (fidata->msg_view);
3140 g_object_unref (fidata->msg_view);
3141 g_slice_free (FetchImageData, fidata);
3146 on_fetch_image_thread (gpointer userdata)
3148 FetchImageData *fidata = (FetchImageData *) userdata;
3149 TnyStreamCache *cache;
3150 TnyStream *cache_stream;
3152 cache = modest_runtime_get_images_cache ();
3153 cache_stream = tny_stream_cache_get_stream (cache, fidata->cache_id, (TnyStreamCacheOpenStreamFetcher) fetch_image_open_stream, (gpointer) fidata->uri);
3154 g_free (fidata->cache_id);
3155 g_free (fidata->uri);
3157 if (cache_stream != NULL) {
3158 tny_stream_write_to_stream (cache_stream, fidata->output_stream);
3159 tny_stream_close (cache_stream);
3160 g_object_unref (cache_stream);
3163 tny_stream_close (fidata->output_stream);
3164 g_object_unref (fidata->output_stream);
3167 gdk_threads_enter ();
3168 g_idle_add (on_fetch_image_idle_refresh_view, fidata);
3169 gdk_threads_leave ();
3175 on_fetch_image (ModestMsgView *msgview,
3178 ModestMsgViewWindow *window)
3180 const gchar *current_account;
3181 ModestMsgViewWindowPrivate *priv;
3182 FetchImageData *fidata;
3184 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
3186 current_account = modest_window_get_active_account (MODEST_WINDOW (window));
3188 fidata = g_slice_new0 (FetchImageData);
3189 fidata->msg_view = g_object_ref (msgview);
3190 fidata->uri = g_strdup (uri);
3191 fidata->cache_id = modest_images_cache_get_id (current_account, uri);
3192 fidata->output_stream = g_object_ref (stream);
3194 if (g_thread_create (on_fetch_image_thread, fidata, FALSE, NULL) == NULL) {
3195 g_object_unref (fidata->output_stream);
3196 g_free (fidata->cache_id);
3197 g_free (fidata->uri);
3198 g_object_unref (fidata->msg_view);
3199 g_slice_free (FetchImageData, fidata);
3200 tny_stream_close (stream);