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);
1433 modest_msg_view_grab_focus (MODEST_MSG_VIEW (priv->msg_view));
1436 /* update the toggle buttons status */
1437 action = gtk_ui_manager_get_action (parent_priv->ui_manager, "/ToolBar/FindInMessage");
1438 modest_utils_toggle_action_set_active_block_notify (GTK_TOGGLE_ACTION (action), is_active);
1439 action = gtk_ui_manager_get_action (parent_priv->ui_manager, "/MenuBar/ToolsMenu/ToolsFindInMessageMenu");
1440 modest_utils_toggle_action_set_active_block_notify (GTK_TOGGLE_ACTION (action), is_active);
1445 modest_msg_view_window_find_toolbar_close (GtkWidget *widget,
1446 ModestMsgViewWindow *obj)
1448 GtkToggleAction *toggle;
1449 ModestWindowPrivate *parent_priv;
1450 ModestMsgViewWindowPrivate *priv;
1452 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (obj);
1453 parent_priv = MODEST_WINDOW_GET_PRIVATE (obj);
1455 toggle = GTK_TOGGLE_ACTION (gtk_ui_manager_get_action (parent_priv->ui_manager, "/ToolBar/FindInMessage"));
1456 gtk_toggle_action_set_active (toggle, FALSE);
1457 modest_msg_view_grab_focus (MODEST_MSG_VIEW (priv->msg_view));
1461 modest_msg_view_window_find_toolbar_search (GtkWidget *widget,
1462 ModestMsgViewWindow *obj)
1464 gchar *current_search;
1465 ModestMsgViewWindowPrivate *priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (obj);
1467 if (modest_mime_part_view_is_empty (MODEST_MIME_PART_VIEW (priv->msg_view))) {
1468 hildon_banner_show_information (NULL, NULL, _("mail_ib_nothing_to_find"));
1472 g_object_get (G_OBJECT (widget), "prefix", ¤t_search, NULL);
1474 if ((current_search == NULL) || (strcmp (current_search, "") == 0)) {
1475 g_free (current_search);
1476 hildon_banner_show_information (NULL, NULL, dgettext("hildon-common-strings", "ecdg_ib_find_rep_enter_text"));
1480 if ((priv->last_search == NULL) || (strcmp (priv->last_search, current_search) != 0)) {
1482 g_free (priv->last_search);
1483 priv->last_search = g_strdup (current_search);
1484 result = modest_isearch_view_search (MODEST_ISEARCH_VIEW (priv->msg_view),
1487 hildon_banner_show_information (NULL, NULL, dgettext("hildon-libs", "ckct_ib_find_no_matches"));
1488 g_free (priv->last_search);
1489 priv->last_search = NULL;
1491 modest_msg_view_grab_focus (MODEST_MSG_VIEW (priv->msg_view));
1492 hildon_find_toolbar_highlight_entry (HILDON_FIND_TOOLBAR (priv->find_toolbar), TRUE);
1495 if (!modest_isearch_view_search_next (MODEST_ISEARCH_VIEW (priv->msg_view))) {
1496 hildon_banner_show_information (NULL, NULL, dgettext("hildon-libs", "ckct_ib_find_search_complete"));
1497 g_free (priv->last_search);
1498 priv->last_search = NULL;
1500 modest_msg_view_grab_focus (MODEST_MSG_VIEW (priv->msg_view));
1501 hildon_find_toolbar_highlight_entry (HILDON_FIND_TOOLBAR (priv->find_toolbar), TRUE);
1505 g_free (current_search);
1510 modest_msg_view_window_set_zoom (ModestWindow *window,
1513 ModestMsgViewWindowPrivate *priv;
1514 ModestWindowPrivate *parent_priv;
1515 GtkAction *action = NULL;
1516 gint int_zoom = (gint) rint (zoom*100.0+0.1);
1518 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window));
1520 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1521 parent_priv = MODEST_WINDOW_GET_PRIVATE (window);
1522 modest_zoomable_set_zoom (MODEST_ZOOMABLE (priv->msg_view), zoom);
1524 action = gtk_ui_manager_get_action (parent_priv->ui_manager,
1525 "/MenuBar/ViewMenu/ZoomMenu/Zoom50Menu");
1527 gtk_radio_action_set_current_value (GTK_RADIO_ACTION (action), int_zoom);
1531 modest_msg_view_window_get_zoom (ModestWindow *window)
1533 ModestMsgViewWindowPrivate *priv;
1535 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), 1.0);
1537 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1538 return modest_zoomable_get_zoom (MODEST_ZOOMABLE (priv->msg_view));
1542 modest_msg_view_window_zoom_plus (ModestWindow *window)
1544 ModestWindowPrivate *parent_priv;
1545 GtkRadioAction *zoom_radio_action;
1546 GSList *group, *node;
1548 parent_priv = MODEST_WINDOW_GET_PRIVATE (window);
1549 zoom_radio_action = GTK_RADIO_ACTION (gtk_ui_manager_get_action (parent_priv->ui_manager,
1550 "/MenuBar/ViewMenu/ZoomMenu/Zoom50Menu"));
1552 group = gtk_radio_action_get_group (zoom_radio_action);
1554 if (gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (group->data))) {
1555 hildon_banner_show_information (NULL, NULL, dgettext("hildon-common-strings", "ckct_ib_max_zoom_level_reached"));
1559 for (node = group; node != NULL; node = g_slist_next (node)) {
1560 if ((node->next != NULL) && gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (node->next->data))) {
1561 gtk_toggle_action_set_active (GTK_TOGGLE_ACTION (node->data), TRUE);
1569 modest_msg_view_window_zoom_minus (ModestWindow *window)
1571 ModestWindowPrivate *parent_priv;
1572 GtkRadioAction *zoom_radio_action;
1573 GSList *group, *node;
1575 parent_priv = MODEST_WINDOW_GET_PRIVATE (window);
1576 zoom_radio_action = GTK_RADIO_ACTION (gtk_ui_manager_get_action (parent_priv->ui_manager,
1577 "/MenuBar/ViewMenu/ZoomMenu/Zoom50Menu"));
1579 group = gtk_radio_action_get_group (zoom_radio_action);
1581 for (node = group; node != NULL; node = g_slist_next (node)) {
1582 if (gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (node->data))) {
1583 if (node->next != NULL) {
1584 gtk_toggle_action_set_active (GTK_TOGGLE_ACTION (node->next->data), TRUE);
1587 hildon_banner_show_information (NULL, NULL, dgettext("hildon-common-strings", "ckct_ib_min_zoom_level_reached"));
1597 modest_msg_view_window_key_event (GtkWidget *window,
1603 focus = gtk_window_get_focus (GTK_WINDOW (window));
1605 /* for the find toolbar case */
1606 if (focus && GTK_IS_ENTRY (focus)) {
1607 if (event->keyval == GDK_BackSpace) {
1609 copy = gdk_event_copy ((GdkEvent *) event);
1610 gtk_widget_event (focus, copy);
1611 gdk_event_free (copy);
1616 if (event->keyval == GDK_Up || event->keyval == GDK_KP_Up ||
1617 event->keyval == GDK_Down || event->keyval == GDK_KP_Down ||
1618 event->keyval == GDK_Page_Up || event->keyval == GDK_KP_Page_Up ||
1619 event->keyval == GDK_Page_Down || event->keyval == GDK_KP_Page_Down ||
1620 event->keyval == GDK_Home || event->keyval == GDK_KP_Home ||
1621 event->keyval == GDK_End || event->keyval == GDK_KP_End) {
1622 /* ModestMsgViewWindowPrivate *priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window); */
1623 /* gboolean return_value; */
1625 if (event->type == GDK_KEY_PRESS) {
1626 GtkScrollType scroll_type;
1628 switch (event->keyval) {
1631 scroll_type = GTK_SCROLL_STEP_UP; break;
1634 scroll_type = GTK_SCROLL_STEP_DOWN; break;
1636 case GDK_KP_Page_Up:
1637 scroll_type = GTK_SCROLL_PAGE_UP; break;
1639 case GDK_KP_Page_Down:
1640 scroll_type = GTK_SCROLL_PAGE_DOWN; break;
1643 scroll_type = GTK_SCROLL_START; break;
1646 scroll_type = GTK_SCROLL_END; break;
1647 default: scroll_type = GTK_SCROLL_NONE;
1650 /* g_signal_emit_by_name (G_OBJECT (priv->main_scroll), "scroll-child", */
1651 /* scroll_type, FALSE, &return_value); */
1662 modest_msg_view_window_last_message_selected (ModestMsgViewWindow *window)
1665 ModestMsgViewWindowPrivate *priv;
1666 GtkTreeIter tmp_iter;
1667 gboolean is_last_selected;
1669 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), TRUE);
1670 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1672 /*if no model (so no rows at all), then virtually we are the last*/
1673 if (!priv->header_model || !priv->row_reference)
1676 if (!gtk_tree_row_reference_valid (priv->row_reference))
1679 path = gtk_tree_row_reference_get_path (priv->row_reference);
1683 is_last_selected = TRUE;
1684 while (is_last_selected) {
1686 gtk_tree_path_next (path);
1687 if (!gtk_tree_model_get_iter (priv->header_model, &tmp_iter, path))
1689 gtk_tree_model_get (priv->header_model, &tmp_iter,
1690 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
1693 if (msg_is_visible (header, priv->is_outbox))
1694 is_last_selected = FALSE;
1695 g_object_unref(G_OBJECT(header));
1698 gtk_tree_path_free (path);
1699 return is_last_selected;
1703 modest_msg_view_window_has_headers_model (ModestMsgViewWindow *window)
1705 ModestMsgViewWindowPrivate *priv;
1707 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), TRUE);
1708 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1710 return priv->header_model != NULL;
1714 modest_msg_view_window_is_search_result (ModestMsgViewWindow *window)
1716 ModestMsgViewWindowPrivate *priv;
1718 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), TRUE);
1719 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1721 return priv->is_search_result;
1725 msg_is_visible (TnyHeader *header, gboolean check_outbox)
1727 if ((tny_header_get_flags(header) & TNY_HEADER_FLAG_DELETED))
1729 if (!check_outbox) {
1732 ModestTnySendQueueStatus status;
1733 status = modest_tny_all_send_queues_get_msg_status (header);
1734 return ((status != MODEST_TNY_SEND_QUEUE_FAILED) &&
1735 (status != MODEST_TNY_SEND_QUEUE_SENDING));
1740 modest_msg_view_window_first_message_selected (ModestMsgViewWindow *window)
1743 ModestMsgViewWindowPrivate *priv;
1744 gboolean is_first_selected;
1745 GtkTreeIter tmp_iter;
1747 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), TRUE);
1748 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1750 /*if no model (so no rows at all), then virtually we are the first*/
1751 if (!priv->header_model || !priv->row_reference)
1754 if (!gtk_tree_row_reference_valid (priv->row_reference))
1757 path = gtk_tree_row_reference_get_path (priv->row_reference);
1761 is_first_selected = TRUE;
1762 while (is_first_selected) {
1764 if(!gtk_tree_path_prev (path))
1766 /* Here the 'if' is needless for logic, but let make sure
1767 * iter is valid for gtk_tree_model_get. */
1768 if (!gtk_tree_model_get_iter (priv->header_model, &tmp_iter, path))
1770 gtk_tree_model_get (priv->header_model, &tmp_iter,
1771 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
1774 if (msg_is_visible (header, priv->is_outbox))
1775 is_first_selected = FALSE;
1776 g_object_unref(G_OBJECT(header));
1779 gtk_tree_path_free (path);
1780 return is_first_selected;
1785 GtkTreeRowReference *row_reference;
1789 message_reader_performer (gboolean canceled,
1791 GtkWindow *parent_window,
1792 TnyAccount *account,
1795 ModestMailOperation *mail_op = NULL;
1796 MsgReaderInfo *info;
1798 info = (MsgReaderInfo *) user_data;
1799 if (canceled || err) {
1803 /* Register the header - it'll be unregistered in the callback */
1804 modest_window_mgr_register_header (modest_runtime_get_window_mgr (), info->header, NULL);
1806 /* New mail operation */
1807 mail_op = modest_mail_operation_new_with_error_handling (G_OBJECT(parent_window),
1808 modest_ui_actions_disk_operations_error_handler,
1811 modest_mail_operation_queue_add (modest_runtime_get_mail_operation_queue (), mail_op);
1812 modest_mail_operation_get_msg (mail_op, info->header, TRUE, view_msg_cb, info->row_reference);
1813 g_object_unref (mail_op);
1815 /* Update dimming rules */
1816 modest_ui_actions_check_toolbar_dimming_rules (MODEST_WINDOW (parent_window));
1817 modest_ui_actions_check_menu_dimming_rules (MODEST_WINDOW (parent_window));
1820 /* Frees. The row_reference will be freed by the view_msg_cb callback */
1821 g_object_unref (info->header);
1822 g_slice_free (MsgReaderInfo, info);
1827 * Reads the message whose summary item is @header. It takes care of
1828 * several things, among others:
1830 * If the message was not previously downloaded then ask the user
1831 * before downloading. If there is no connection launch the connection
1832 * dialog. Update toolbar dimming rules.
1834 * Returns: TRUE if the mail operation was started, otherwise if the
1835 * user do not want to download the message, or if the user do not
1836 * want to connect, then the operation is not issued
1839 message_reader (ModestMsgViewWindow *window,
1840 ModestMsgViewWindowPrivate *priv,
1842 GtkTreeRowReference *row_reference)
1844 ModestWindowMgr *mgr;
1845 TnyAccount *account;
1847 MsgReaderInfo *info;
1849 g_return_val_if_fail (row_reference != NULL, FALSE);
1851 mgr = modest_runtime_get_window_mgr ();
1852 /* Msg download completed */
1853 if (!(tny_header_get_flags (header) & TNY_HEADER_FLAG_CACHED)) {
1854 /* Ask the user if he wants to download the message if
1856 if (!tny_device_is_online (modest_runtime_get_device())) {
1857 GtkResponseType response;
1859 response = modest_platform_run_confirmation_dialog (GTK_WINDOW (window),
1860 _("mcen_nc_get_msg"));
1861 if (response == GTK_RESPONSE_CANCEL)
1864 folder = tny_header_get_folder (header);
1865 info = g_slice_new (MsgReaderInfo);
1866 info->header = g_object_ref (header);
1867 info->row_reference = gtk_tree_row_reference_copy (row_reference);
1869 /* Offer the connection dialog if necessary */
1870 modest_platform_connect_if_remote_and_perform ((GtkWindow *) window,
1872 TNY_FOLDER_STORE (folder),
1873 message_reader_performer,
1875 g_object_unref (folder);
1880 folder = tny_header_get_folder (header);
1881 account = tny_folder_get_account (folder);
1882 info = g_slice_new (MsgReaderInfo);
1883 info->header = g_object_ref (header);
1884 info->row_reference = gtk_tree_row_reference_copy (row_reference);
1886 message_reader_performer (FALSE, NULL, (GtkWindow *) window, account, info);
1887 g_object_unref (account);
1888 g_object_unref (folder);
1894 modest_msg_view_window_select_next_message (ModestMsgViewWindow *window)
1896 ModestMsgViewWindowPrivate *priv;
1897 GtkTreePath *path= NULL;
1898 GtkTreeIter tmp_iter;
1900 gboolean retval = TRUE;
1901 GtkTreeRowReference *row_reference = NULL;
1903 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), FALSE);
1904 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1906 if (!priv->row_reference)
1909 /* Update the next row reference if it's not valid. This could
1910 happen if for example the header which it was pointing to,
1911 was deleted. The best place to do it is in the row-deleted
1912 handler but the tinymail model do not work like the glib
1913 tree models and reports the deletion when the row is still
1915 if (!gtk_tree_row_reference_valid (priv->next_row_reference)) {
1916 if (gtk_tree_row_reference_valid (priv->row_reference)) {
1917 priv->next_row_reference = gtk_tree_row_reference_copy (priv->row_reference);
1918 select_next_valid_row (priv->header_model, &(priv->next_row_reference), FALSE, priv->is_outbox);
1921 if (priv->next_row_reference)
1922 path = gtk_tree_row_reference_get_path (priv->next_row_reference);
1926 row_reference = gtk_tree_row_reference_copy (priv->next_row_reference);
1928 gtk_tree_model_get_iter (priv->header_model,
1931 gtk_tree_path_free (path);
1933 gtk_tree_model_get (priv->header_model, &tmp_iter,
1934 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
1937 /* Read the message & show it */
1938 if (!message_reader (window, priv, header, row_reference)) {
1941 gtk_tree_row_reference_free (row_reference);
1944 g_object_unref (header);
1950 modest_msg_view_window_select_previous_message (ModestMsgViewWindow *window)
1952 ModestMsgViewWindowPrivate *priv = NULL;
1954 gboolean finished = FALSE;
1955 gboolean retval = FALSE;
1957 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), FALSE);
1958 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1960 /* Return inmediatly if there is no header model */
1961 if (!priv->header_model || !priv->row_reference)
1964 path = gtk_tree_row_reference_get_path (priv->row_reference);
1965 while (!finished && gtk_tree_path_prev (path)) {
1969 gtk_tree_model_get_iter (priv->header_model, &iter, path);
1970 gtk_tree_model_get (priv->header_model, &iter,
1971 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
1975 if (msg_is_visible (header, priv->is_outbox)) {
1976 GtkTreeRowReference *row_reference;
1977 row_reference = gtk_tree_row_reference_new (priv->header_model, path);
1978 /* Read the message & show it */
1979 retval = message_reader (window, priv, header, row_reference);
1980 gtk_tree_row_reference_free (row_reference);
1984 g_object_unref (header);
1988 gtk_tree_path_free (path);
1993 view_msg_cb (ModestMailOperation *mail_op,
2000 ModestMsgViewWindow *self = NULL;
2001 ModestMsgViewWindowPrivate *priv = NULL;
2002 GtkTreeRowReference *row_reference = NULL;
2004 /* Unregister the header (it was registered before creating the mail operation) */
2005 modest_window_mgr_unregister_header (modest_runtime_get_window_mgr (), header);
2007 row_reference = (GtkTreeRowReference *) user_data;
2009 gtk_tree_row_reference_free (row_reference);
2013 /* If there was any error */
2014 if (!modest_ui_actions_msg_retrieval_check (mail_op, header, msg)) {
2015 gtk_tree_row_reference_free (row_reference);
2019 /* Get the window */
2020 self = (ModestMsgViewWindow *) modest_mail_operation_get_source (mail_op);
2021 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self));
2022 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
2024 /* Update the row reference */
2025 if (priv->row_reference != NULL) {
2026 gtk_tree_row_reference_free (priv->row_reference);
2027 priv->row_reference = gtk_tree_row_reference_copy (row_reference);
2028 if (priv->next_row_reference != NULL) {
2029 gtk_tree_row_reference_free (priv->next_row_reference);
2031 priv->next_row_reference = gtk_tree_row_reference_copy (priv->row_reference);
2032 select_next_valid_row (priv->header_model, &(priv->next_row_reference), TRUE, priv->is_outbox);
2035 /* Mark header as read */
2036 if (!(tny_header_get_flags (header) & TNY_HEADER_FLAG_SEEN))
2037 tny_header_set_flag (header, TNY_HEADER_FLAG_SEEN);
2039 /* Set new message */
2040 if (priv->msg_view != NULL && TNY_IS_MSG_VIEW (priv->msg_view)) {
2041 tny_msg_view_set_msg (TNY_MSG_VIEW (priv->msg_view), msg);
2042 modest_msg_view_window_update_priority (self);
2043 update_window_title (MODEST_MSG_VIEW_WINDOW (self));
2044 modest_msg_view_grab_focus (MODEST_MSG_VIEW (priv->msg_view));
2047 /* Set the new message uid of the window */
2048 if (priv->msg_uid) {
2049 g_free (priv->msg_uid);
2050 priv->msg_uid = modest_tny_folder_get_header_unique_id (header);
2053 /* Notify the observers */
2054 g_signal_emit (G_OBJECT (self), signals[MSG_CHANGED_SIGNAL],
2055 0, priv->header_model, priv->row_reference);
2058 g_object_unref (self);
2059 gtk_tree_row_reference_free (row_reference);
2063 modest_msg_view_window_get_folder_type (ModestMsgViewWindow *window)
2065 ModestMsgViewWindowPrivate *priv;
2067 TnyFolderType folder_type;
2069 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
2071 folder_type = TNY_FOLDER_TYPE_UNKNOWN;
2073 msg = tny_msg_view_get_msg (TNY_MSG_VIEW (priv->msg_view));
2077 folder = tny_msg_get_folder (msg);
2079 folder_type = modest_tny_folder_guess_folder_type (folder);
2080 g_object_unref (folder);
2082 g_object_unref (msg);
2090 modest_msg_view_window_update_priority (ModestMsgViewWindow *window)
2092 ModestMsgViewWindowPrivate *priv;
2093 TnyHeader *header = NULL;
2094 TnyHeaderFlags flags = 0;
2096 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
2098 if (priv->header_model && priv->row_reference) {
2100 GtkTreePath *path = NULL;
2102 path = gtk_tree_row_reference_get_path (priv->row_reference);
2103 g_return_if_fail (path != NULL);
2104 gtk_tree_model_get_iter (priv->header_model,
2106 gtk_tree_row_reference_get_path (priv->row_reference));
2108 gtk_tree_model_get (priv->header_model, &iter, TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
2110 gtk_tree_path_free (path);
2113 msg = tny_msg_view_get_msg (TNY_MSG_VIEW (priv->msg_view));
2115 header = tny_msg_get_header (msg);
2116 g_object_unref (msg);
2121 flags = tny_header_get_flags (header);
2122 g_object_unref(G_OBJECT(header));
2125 modest_msg_view_set_priority (MODEST_MSG_VIEW(priv->msg_view), flags);
2130 toolbar_resize (ModestMsgViewWindow *self)
2132 ModestMsgViewWindowPrivate *priv = NULL;
2133 ModestWindowPrivate *parent_priv = NULL;
2135 gint static_button_size;
2136 ModestWindowMgr *mgr;
2138 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self));
2139 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
2140 parent_priv = MODEST_WINDOW_GET_PRIVATE(self);
2142 mgr = modest_runtime_get_window_mgr ();
2143 static_button_size = modest_window_mgr_get_fullscreen_mode (mgr)?118:108;
2145 if (parent_priv->toolbar) {
2146 /* left size buttons */
2147 widget = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/ToolbarMessageReply");
2148 gtk_tool_item_set_expand (GTK_TOOL_ITEM (widget), FALSE);
2149 gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (widget), FALSE);
2150 gtk_widget_set_size_request (GTK_WIDGET (widget), static_button_size, -1);
2151 widget = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/ToolbarMessageMoveTo");
2152 gtk_tool_item_set_expand (GTK_TOOL_ITEM (widget), FALSE);
2153 gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (widget), FALSE);
2154 gtk_widget_set_size_request (GTK_WIDGET (widget), static_button_size, -1);
2155 widget = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/ToolbarDeleteMessage");
2156 gtk_tool_item_set_expand (GTK_TOOL_ITEM (widget), FALSE);
2157 gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (widget), FALSE);
2158 gtk_widget_set_size_request (GTK_WIDGET (widget), static_button_size, -1);
2159 widget = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/FindInMessage");
2160 gtk_tool_item_set_expand (GTK_TOOL_ITEM (widget), FALSE);
2161 gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (widget), FALSE);
2162 gtk_widget_set_size_request (GTK_WIDGET (widget), static_button_size, -1);
2164 gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (priv->progress_toolitem), FALSE);
2165 gtk_tool_item_set_expand (GTK_TOOL_ITEM (priv->progress_toolitem), TRUE);
2166 gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (priv->cancel_toolitem), FALSE);
2167 gtk_tool_item_set_expand (GTK_TOOL_ITEM (priv->cancel_toolitem), FALSE);
2168 gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (priv->next_toolitem), TRUE);
2169 gtk_tool_item_set_expand (GTK_TOOL_ITEM (priv->next_toolitem), TRUE);
2170 gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (priv->prev_toolitem), TRUE);
2171 gtk_tool_item_set_expand (GTK_TOOL_ITEM (priv->prev_toolitem), TRUE);
2177 modest_msg_view_window_window_state_event (GtkWidget *widget, GdkEventWindowState *event, gpointer userdata)
2179 if (event->changed_mask & GDK_WINDOW_STATE_FULLSCREEN) {
2180 ModestWindowPrivate *parent_priv;
2181 ModestWindowMgr *mgr;
2182 gboolean is_fullscreen;
2183 GtkAction *fs_toggle_action;
2186 mgr = modest_runtime_get_window_mgr ();
2187 is_fullscreen = (modest_window_mgr_get_fullscreen_mode (mgr))?1:0;
2189 parent_priv = MODEST_WINDOW_GET_PRIVATE (widget);
2191 fs_toggle_action = gtk_ui_manager_get_action (parent_priv->ui_manager, "/MenuBar/ViewMenu/ViewToggleFullscreenMenu");
2192 active = (gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (fs_toggle_action)))?1:0;
2193 if (is_fullscreen != active) {
2194 gtk_toggle_action_set_active (GTK_TOGGLE_ACTION (fs_toggle_action), is_fullscreen);
2196 toolbar_resize (MODEST_MSG_VIEW_WINDOW (widget));
2204 modest_msg_view_window_show_toolbar (ModestWindow *self,
2205 gboolean show_toolbar)
2207 ModestMsgViewWindowPrivate *priv = NULL;
2208 ModestWindowPrivate *parent_priv;
2209 GtkWidget *reply_button = NULL, *menu = NULL;
2210 GtkWidget *placeholder = NULL;
2212 const gchar *action_name;
2215 parent_priv = MODEST_WINDOW_GET_PRIVATE(self);
2216 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(self);
2218 /* Set optimized view status */
2219 priv->optimized_view = !show_toolbar;
2221 if (!parent_priv->toolbar) {
2222 parent_priv->toolbar = gtk_ui_manager_get_widget (parent_priv->ui_manager,
2224 gtk_widget_set_no_show_all (parent_priv->toolbar, TRUE);
2226 priv->progress_toolitem = GTK_WIDGET (gtk_tool_item_new ());
2227 priv->cancel_toolitem = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/ToolbarCancel");
2228 priv->next_toolitem = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/ToolbarMessageNext");
2229 priv->prev_toolitem = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/ToolbarMessageBack");
2230 toolbar_resize (MODEST_MSG_VIEW_WINDOW (self));
2232 /* Add ProgressBar (Transfer toolbar) */
2233 priv->progress_bar = modest_progress_bar_new ();
2234 gtk_widget_set_no_show_all (priv->progress_bar, TRUE);
2235 placeholder = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/ProgressbarView");
2236 insert_index = gtk_toolbar_get_item_index(GTK_TOOLBAR (parent_priv->toolbar), GTK_TOOL_ITEM(placeholder));
2237 gtk_container_add (GTK_CONTAINER (priv->progress_toolitem), priv->progress_bar);
2238 gtk_toolbar_insert(GTK_TOOLBAR(parent_priv->toolbar), GTK_TOOL_ITEM (priv->progress_toolitem), insert_index);
2240 /* Connect cancel 'clicked' signal to abort progress mode */
2241 g_signal_connect(priv->cancel_toolitem, "clicked",
2242 G_CALLBACK(cancel_progressbar),
2245 /* Add it to the observers list */
2246 priv->progress_widgets = g_slist_prepend(priv->progress_widgets, priv->progress_bar);
2249 hildon_window_add_toolbar (HILDON_WINDOW (self),
2250 GTK_TOOLBAR (parent_priv->toolbar));
2252 /* Set reply button tap and hold menu */
2253 reply_button = gtk_ui_manager_get_widget (parent_priv->ui_manager,
2254 "/ToolBar/ToolbarMessageReply");
2255 menu = gtk_ui_manager_get_widget (parent_priv->ui_manager,
2256 "/ToolbarReplyCSM");
2257 gtk_widget_tap_and_hold_setup (GTK_WIDGET (reply_button), menu, NULL, 0);
2261 /* Quick hack: this prevents toolbar icons "dance" when progress bar show status is changed */
2262 /* TODO: resize mode migth be GTK_RESIZE_QUEUE, in order to avoid unneccesary shows */
2263 gtk_container_set_resize_mode (GTK_CONTAINER(parent_priv->toolbar), GTK_RESIZE_IMMEDIATE);
2265 gtk_widget_show (GTK_WIDGET (parent_priv->toolbar));
2266 if (modest_msg_view_window_transfer_mode_enabled (MODEST_MSG_VIEW_WINDOW (self)))
2267 set_toolbar_mode (MODEST_MSG_VIEW_WINDOW (self), TOOLBAR_MODE_TRANSFER);
2269 set_toolbar_mode (MODEST_MSG_VIEW_WINDOW (self), TOOLBAR_MODE_NORMAL);
2272 gtk_widget_set_no_show_all (parent_priv->toolbar, TRUE);
2273 gtk_widget_hide (GTK_WIDGET (parent_priv->toolbar));
2276 /* Update also the actions (to update the toggles in the
2277 menus), we have to do it manually because some other window
2278 of the same time could have changed it (remember that the
2279 toolbar fullscreen mode is shared by all the windows of the
2281 if (modest_window_mgr_get_fullscreen_mode (modest_runtime_get_window_mgr ()))
2282 action_name = "/MenuBar/ViewMenu/ViewShowToolbarMenu/ViewShowToolbarFullScreenMenu";
2284 action_name = "/MenuBar/ViewMenu/ViewShowToolbarMenu/ViewShowToolbarNormalScreenMenu";
2286 action = gtk_ui_manager_get_action (parent_priv->ui_manager, action_name);
2287 modest_utils_toggle_action_set_active_block_notify (GTK_TOGGLE_ACTION (action),
2292 modest_msg_view_window_clipboard_owner_change (GtkClipboard *clipboard,
2294 ModestMsgViewWindow *window)
2296 if (!GTK_WIDGET_VISIBLE (window))
2299 modest_window_check_dimming_rules_group (MODEST_WINDOW (window), MODEST_DIMMING_RULES_CLIPBOARD);
2303 modest_msg_view_window_transfer_mode_enabled (ModestMsgViewWindow *self)
2305 ModestMsgViewWindowPrivate *priv;
2307 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self), FALSE);
2308 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(self);
2310 return priv->current_toolbar_mode == TOOLBAR_MODE_TRANSFER;
2314 cancel_progressbar (GtkToolButton *toolbutton,
2315 ModestMsgViewWindow *self)
2318 ModestMsgViewWindowPrivate *priv;
2320 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(self);
2322 /* Get operation observers and cancel its current operation */
2323 tmp = priv->progress_widgets;
2325 modest_progress_object_cancel_current_operation (MODEST_PROGRESS_OBJECT(tmp->data));
2326 tmp=g_slist_next(tmp);
2330 observers_empty (ModestMsgViewWindow *self)
2333 ModestMsgViewWindowPrivate *priv;
2334 gboolean is_empty = TRUE;
2335 guint pending_ops = 0;
2337 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(self);
2338 tmp = priv->progress_widgets;
2340 /* Check all observers */
2341 while (tmp && is_empty) {
2342 pending_ops = modest_progress_object_num_pending_operations (MODEST_PROGRESS_OBJECT(tmp->data));
2343 is_empty = pending_ops == 0;
2345 tmp = g_slist_next(tmp);
2352 on_account_removed (TnyAccountStore *account_store,
2353 TnyAccount *account,
2356 /* Do nothing if it's a transport account, because we only
2357 show the messages of a store account */
2358 if (tny_account_get_account_type(account) == TNY_ACCOUNT_TYPE_STORE) {
2359 const gchar *parent_acc = NULL;
2360 const gchar *our_acc = NULL;
2362 our_acc = modest_window_get_active_account (MODEST_WINDOW (user_data));
2363 parent_acc = modest_tny_account_get_parent_modest_account_name_for_server_account (account);
2365 /* Close this window if I'm showing a message of the removed account */
2366 if (strcmp (parent_acc, our_acc) == 0)
2367 modest_ui_actions_on_close_window (NULL, MODEST_WINDOW (user_data));
2372 on_mail_operation_started (ModestMailOperation *mail_op,
2375 ModestMsgViewWindow *self;
2376 ModestMailOperationTypeOperation op_type;
2378 ModestMsgViewWindowPrivate *priv;
2379 GObject *source = NULL;
2381 self = MODEST_MSG_VIEW_WINDOW (user_data);
2382 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
2383 op_type = modest_mail_operation_get_type_operation (mail_op);
2384 tmp = priv->progress_widgets;
2385 source = modest_mail_operation_get_source(mail_op);
2386 if (G_OBJECT (self) == source) {
2387 if (op_type == MODEST_MAIL_OPERATION_TYPE_RECEIVE ) {
2388 set_toolbar_transfer_mode(self);
2390 modest_progress_object_add_operation (
2391 MODEST_PROGRESS_OBJECT (tmp->data),
2393 tmp = g_slist_next (tmp);
2397 g_object_unref (source);
2401 on_mail_operation_finished (ModestMailOperation *mail_op,
2404 ModestMsgViewWindow *self;
2405 ModestMailOperationTypeOperation op_type;
2407 ModestMsgViewWindowPrivate *priv;
2409 self = MODEST_MSG_VIEW_WINDOW (user_data);
2410 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
2411 op_type = modest_mail_operation_get_type_operation (mail_op);
2412 tmp = priv->progress_widgets;
2414 if (op_type == MODEST_MAIL_OPERATION_TYPE_RECEIVE ) {
2416 modest_progress_object_remove_operation (MODEST_PROGRESS_OBJECT (tmp->data),
2418 tmp = g_slist_next (tmp);
2421 /* If no more operations are being observed, NORMAL mode is enabled again */
2422 if (observers_empty (self)) {
2423 set_toolbar_mode (self, TOOLBAR_MODE_NORMAL);
2426 /* Update dimming rules. We have to do this right here
2427 and not in view_msg_cb because at that point the
2428 transfer mode is still enabled so the dimming rule
2429 won't let the user delete the message that has been
2430 readed for example */
2431 modest_ui_actions_check_toolbar_dimming_rules (MODEST_WINDOW (self));
2432 modest_ui_actions_check_menu_dimming_rules (MODEST_WINDOW (self));
2437 on_queue_changed (ModestMailOperationQueue *queue,
2438 ModestMailOperation *mail_op,
2439 ModestMailOperationQueueNotification type,
2440 ModestMsgViewWindow *self)
2442 ModestMsgViewWindowPrivate *priv;
2444 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
2446 /* If this operations was created by another window, do nothing */
2447 if (!modest_mail_operation_is_mine (mail_op, G_OBJECT(self)))
2450 if (type == MODEST_MAIL_OPERATION_QUEUE_OPERATION_ADDED) {
2451 priv->sighandlers = modest_signal_mgr_connect (priv->sighandlers,
2453 "operation-started",
2454 G_CALLBACK (on_mail_operation_started),
2456 priv->sighandlers = modest_signal_mgr_connect (priv->sighandlers,
2458 "operation-finished",
2459 G_CALLBACK (on_mail_operation_finished),
2461 } else if (type == MODEST_MAIL_OPERATION_QUEUE_OPERATION_REMOVED) {
2462 priv->sighandlers = modest_signal_mgr_disconnect (priv->sighandlers,
2464 "operation-started");
2465 priv->sighandlers = modest_signal_mgr_disconnect (priv->sighandlers,
2467 "operation-finished");
2472 modest_msg_view_window_get_attachments (ModestMsgViewWindow *win)
2474 ModestMsgViewWindowPrivate *priv;
2475 TnyList *selected_attachments = NULL;
2477 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (win), NULL);
2478 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (win);
2480 selected_attachments = modest_msg_view_get_selected_attachments (MODEST_MSG_VIEW (priv->msg_view));
2482 return selected_attachments;
2488 guint banner_idle_id;
2489 } DecodeAsyncHelper;
2492 decode_async_banner_idle (gpointer user_data)
2494 DecodeAsyncHelper *helper = (DecodeAsyncHelper *) user_data;
2496 helper->banner_idle_id = 0;
2497 helper->banner = hildon_banner_show_animation (NULL, NULL, _("mail_me_opening"));
2498 g_object_ref (helper->banner);
2504 on_decode_to_stream_async_handler (TnyMimePart *mime_part,
2510 DecodeAsyncHelper *helper = (DecodeAsyncHelper *) user_data;
2512 if (helper->banner_idle_id > 0) {
2513 g_source_remove (helper->banner_idle_id);
2514 helper->banner_idle_id = 0;
2516 if (helper->banner) {
2517 gtk_widget_destroy (helper->banner);
2519 if (cancelled || err) {
2520 modest_platform_information_banner (NULL, NULL,
2521 _("mail_ib_file_operation_failed"));
2525 /* make the file read-only */
2526 g_chmod(helper->filepath, 0444);
2528 /* Activate the file */
2529 modest_platform_activate_file (helper->filepath, tny_mime_part_get_content_type (mime_part));
2533 g_free (helper->filepath);
2534 g_object_unref (helper->banner);
2535 g_slice_free (DecodeAsyncHelper, helper);
2539 modest_msg_view_window_view_attachment (ModestMsgViewWindow *window,
2540 TnyMimePart *mime_part)
2542 ModestMsgViewWindowPrivate *priv;
2543 const gchar *msg_uid;
2544 gchar *attachment_uid = NULL;
2545 gint attachment_index = 0;
2546 TnyList *attachments;
2548 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window));
2549 g_return_if_fail (TNY_IS_MIME_PART (mime_part) || (mime_part == NULL));
2550 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
2552 msg_uid = modest_msg_view_window_get_message_uid (MODEST_MSG_VIEW_WINDOW (window));
2553 attachments = modest_msg_view_get_attachments (MODEST_MSG_VIEW (priv->msg_view));
2554 attachment_index = modest_list_index (attachments, (GObject *) mime_part);
2555 g_object_unref (attachments);
2557 if (msg_uid && attachment_index >= 0) {
2558 attachment_uid = g_strdup_printf ("%s/%d", msg_uid, attachment_index);
2561 if (mime_part == NULL) {
2562 gboolean error = FALSE;
2563 TnyList *selected_attachments = modest_msg_view_get_selected_attachments (MODEST_MSG_VIEW (priv->msg_view));
2564 if (selected_attachments == NULL || tny_list_get_length (selected_attachments) == 0) {
2566 } else if (tny_list_get_length (selected_attachments) > 1) {
2567 hildon_banner_show_information (NULL, NULL, _("mcen_ib_unable_to_display_more"));
2571 iter = tny_list_create_iterator (selected_attachments);
2572 mime_part = (TnyMimePart *) tny_iterator_get_current (iter);
2573 g_object_unref (iter);
2575 g_object_unref (selected_attachments);
2580 g_object_ref (mime_part);
2583 if (tny_mime_part_is_purged (mime_part)) {
2584 g_object_unref (mime_part);
2588 if (!modest_tny_mime_part_is_msg (mime_part)) {
2589 gchar *filepath = NULL;
2590 const gchar *att_filename = tny_mime_part_get_filename (mime_part);
2591 gboolean show_error_banner = FALSE;
2592 TnyFsStream *temp_stream = NULL;
2593 temp_stream = modest_utils_create_temp_stream (att_filename, attachment_uid,
2596 if (temp_stream != NULL) {
2597 DecodeAsyncHelper *helper = g_slice_new (DecodeAsyncHelper);
2598 helper->filepath = g_strdup (filepath);
2599 helper->banner = NULL;
2600 helper->banner_idle_id = g_timeout_add (1000, decode_async_banner_idle, helper);
2601 tny_mime_part_decode_to_stream_async (mime_part, TNY_STREAM (temp_stream),
2602 on_decode_to_stream_async_handler,
2605 g_object_unref (temp_stream);
2606 /* NOTE: files in the temporary area will be automatically
2607 * cleaned after some time if they are no longer in use */
2610 const gchar *content_type;
2611 /* the file may already exist but it isn't writable,
2612 * let's try to open it anyway */
2613 content_type = tny_mime_part_get_content_type (mime_part);
2614 modest_platform_activate_file (filepath, content_type);
2616 g_warning ("%s: modest_utils_create_temp_stream failed", __FUNCTION__);
2617 show_error_banner = TRUE;
2622 if (show_error_banner)
2623 modest_platform_information_banner (NULL, NULL, _("mail_ib_file_operation_failed"));
2625 /* message attachment */
2626 TnyHeader *header = NULL;
2627 ModestWindowMgr *mgr;
2628 ModestWindow *msg_win = NULL;
2631 header = tny_msg_get_header (TNY_MSG (mime_part));
2632 mgr = modest_runtime_get_window_mgr ();
2633 found = modest_window_mgr_find_registered_header (mgr, header, &msg_win);
2636 /* if it's found, but there is no msg_win, it's probably in the process of being created;
2637 * thus, we don't do anything */
2638 g_warning ("window for is already being created");
2640 /* it's not found, so create a new window for it */
2641 modest_window_mgr_register_header (mgr, header, attachment_uid); /* register the uid before building the window */
2642 gchar *account = g_strdup (modest_window_get_active_account (MODEST_WINDOW (window)));
2644 account = modest_account_mgr_get_default_account (modest_runtime_get_account_mgr ());
2645 msg_win = modest_msg_view_window_new_for_attachment (TNY_MSG (mime_part), account, attachment_uid);
2646 modest_window_set_zoom (MODEST_WINDOW (msg_win),
2647 modest_window_get_zoom (MODEST_WINDOW (window)));
2648 modest_window_mgr_register_window (mgr, msg_win, MODEST_WINDOW (window));
2649 /* gtk_widget_show_all (GTK_WIDGET (msg_win));*/
2652 g_object_unref (mime_part);
2665 GnomeVFSResult result;
2668 static void save_mime_part_info_free (SaveMimePartInfo *info, gboolean with_struct);
2669 static gboolean idle_save_mime_part_show_result (SaveMimePartInfo *info);
2670 static gpointer save_mime_part_to_file (SaveMimePartInfo *info);
2671 static void save_mime_parts_to_file_with_checks (SaveMimePartInfo *info);
2674 save_mime_part_info_free (SaveMimePartInfo *info, gboolean with_struct)
2678 for (node = info->pairs; node != NULL; node = g_list_next (node)) {
2679 SaveMimePartPair *pair = (SaveMimePartPair *) node->data;
2680 g_free (pair->filename);
2681 g_object_unref (pair->part);
2682 g_slice_free (SaveMimePartPair, pair);
2684 g_list_free (info->pairs);
2687 gtk_widget_destroy (info->banner);
2688 g_slice_free (SaveMimePartInfo, info);
2693 idle_save_mime_part_show_result (SaveMimePartInfo *info)
2695 if (info->pairs != NULL) {
2696 save_mime_part_to_file (info);
2698 /* This is a GDK lock because we are an idle callback and
2699 * hildon_banner_show_information is or does Gtk+ code */
2701 gdk_threads_enter (); /* CHECKED */
2702 save_mime_part_info_free (info, TRUE);
2703 if (info->result == GNOME_VFS_OK) {
2704 hildon_banner_show_information (NULL, NULL, _CS("sfil_ib_saved"));
2705 } else if (info->result == GNOME_VFS_ERROR_NO_SPACE) {
2706 hildon_banner_show_information (NULL, NULL, dgettext("ke-recv",
2707 "cerm_device_memory_full"));
2709 hildon_banner_show_information (NULL, NULL, _("mail_ib_file_operation_failed"));
2711 gdk_threads_leave (); /* CHECKED */
2718 save_mime_part_to_file (SaveMimePartInfo *info)
2720 GnomeVFSHandle *handle;
2722 SaveMimePartPair *pair = (SaveMimePartPair *) info->pairs->data;
2724 info->result = gnome_vfs_create (&handle, pair->filename, GNOME_VFS_OPEN_WRITE, FALSE, 0644);
2725 if (info->result == GNOME_VFS_OK) {
2726 GError *error = NULL;
2727 stream = tny_vfs_stream_new (handle);
2728 if (tny_mime_part_decode_to_stream (pair->part, stream, &error) < 0) {
2729 g_warning ("modest: could not save attachment %s: %d (%s)\n", pair->filename, error?error->code:-1, error?error->message:"Unknown error");
2731 info->result = GNOME_VFS_ERROR_IO;
2733 g_object_unref (G_OBJECT (stream));
2734 g_object_unref (pair->part);
2735 g_slice_free (SaveMimePartPair, pair);
2736 info->pairs = g_list_delete_link (info->pairs, info->pairs);
2738 g_warning ("modest: could not create save attachment %s: %s\n", pair->filename, gnome_vfs_result_to_string (info->result));
2739 save_mime_part_info_free (info, FALSE);
2742 g_idle_add ((GSourceFunc) idle_save_mime_part_show_result, info);
2747 save_mime_parts_to_file_with_checks (SaveMimePartInfo *info)
2749 gboolean is_ok = TRUE;
2750 gint replaced_files = 0;
2751 const GList *files = info->pairs;
2754 for (iter = files; (iter != NULL) && (replaced_files < 2); iter = g_list_next(iter)) {
2755 SaveMimePartPair *pair = iter->data;
2756 if (modest_utils_file_exists (pair->filename)) {
2760 if (replaced_files) {
2761 GtkWidget *confirm_overwrite_dialog;
2762 const gchar *message = (replaced_files == 1) ?
2763 _FM("docm_nc_replace_file") : _FM("docm_nc_replace_multiple");
2764 confirm_overwrite_dialog = hildon_note_new_confirmation (NULL, message);
2765 if (gtk_dialog_run (GTK_DIALOG (confirm_overwrite_dialog)) != GTK_RESPONSE_OK) {
2768 gtk_widget_destroy (confirm_overwrite_dialog);
2772 save_mime_part_info_free (info, TRUE);
2774 GtkWidget *banner = hildon_banner_show_animation (NULL, NULL,
2775 _CS("sfil_ib_saving"));
2776 info->banner = banner;
2777 g_thread_create ((GThreadFunc)save_mime_part_to_file, info, FALSE, NULL);
2783 save_attachments_response (GtkDialog *dialog,
2787 TnyList *mime_parts;
2789 GList *files_to_save = NULL;
2791 mime_parts = TNY_LIST (user_data);
2793 if (arg1 != GTK_RESPONSE_OK)
2796 chooser_uri = gtk_file_chooser_get_uri (GTK_FILE_CHOOSER (dialog));
2798 if (!modest_utils_folder_writable (chooser_uri)) {
2799 hildon_banner_show_information
2800 (NULL, NULL, dgettext("hildon-fm", "sfil_ib_readonly_location"));
2804 iter = tny_list_create_iterator (mime_parts);
2805 while (!tny_iterator_is_done (iter)) {
2806 TnyMimePart *mime_part = (TnyMimePart *) tny_iterator_get_current (iter);
2808 if ((modest_tny_mime_part_is_attachment_for_modest (mime_part)) &&
2809 !tny_mime_part_is_purged (mime_part) &&
2810 (tny_mime_part_get_filename (mime_part) != NULL)) {
2811 SaveMimePartPair *pair;
2813 pair = g_slice_new0 (SaveMimePartPair);
2815 if (tny_list_get_length (mime_parts) > 1) {
2817 gnome_vfs_escape_slashes (tny_mime_part_get_filename (mime_part));
2818 pair->filename = g_build_filename (chooser_uri, escaped, NULL);
2821 pair->filename = g_strdup (chooser_uri);
2823 pair->part = mime_part;
2824 files_to_save = g_list_prepend (files_to_save, pair);
2826 tny_iterator_next (iter);
2828 g_object_unref (iter);
2830 g_free (chooser_uri);
2832 if (files_to_save != NULL) {
2833 SaveMimePartInfo *info = g_slice_new0 (SaveMimePartInfo);
2834 info->pairs = files_to_save;
2835 info->result = TRUE;
2836 save_mime_parts_to_file_with_checks (info);
2840 /* Free and close the dialog */
2841 g_object_unref (mime_parts);
2842 gtk_widget_destroy (GTK_WIDGET (dialog));
2846 modest_msg_view_window_save_attachments (ModestMsgViewWindow *window, TnyList *mime_parts)
2848 ModestMsgViewWindowPrivate *priv;
2849 GtkWidget *save_dialog = NULL;
2850 gchar *folder = NULL;
2851 gchar *filename = NULL;
2852 gchar *save_multiple_str = NULL;
2854 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window));
2855 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
2857 if (mime_parts == NULL) {
2858 mime_parts = modest_msg_view_get_selected_attachments (MODEST_MSG_VIEW (priv->msg_view));
2859 if (mime_parts == NULL || tny_list_get_length (mime_parts) == 0)
2862 g_object_ref (mime_parts);
2865 /* prepare dialog */
2866 if (tny_list_get_length (mime_parts) == 1) {
2868 /* only one attachment selected */
2869 iter = tny_list_create_iterator (mime_parts);
2870 TnyMimePart *mime_part = (TnyMimePart *) tny_iterator_get_current (iter);
2871 g_object_unref (iter);
2872 if (!modest_tny_mime_part_is_msg (mime_part) &&
2873 modest_tny_mime_part_is_attachment_for_modest (mime_part) &&
2874 !tny_mime_part_is_purged (mime_part)) {
2875 filename = g_strdup (tny_mime_part_get_filename (mime_part));
2877 /* TODO: show any error? */
2878 g_warning ("Tried to save a non-file attachment");
2879 g_object_unref (mime_parts);
2882 g_object_unref (mime_part);
2884 save_multiple_str = g_strdup_printf (_FM("sfil_va_number_of_objects_attachments"),
2885 tny_list_get_length (mime_parts));
2888 save_dialog = hildon_file_chooser_dialog_new (GTK_WINDOW (window),
2889 GTK_FILE_CHOOSER_ACTION_SAVE);
2892 folder = g_build_filename (g_get_home_dir (), DEFAULT_FOLDER, NULL);
2893 gtk_file_chooser_set_current_folder (GTK_FILE_CHOOSER (save_dialog), folder);
2898 gtk_file_chooser_set_current_name (GTK_FILE_CHOOSER (save_dialog),
2903 /* if multiple, set multiple string */
2904 if (save_multiple_str) {
2905 g_object_set (G_OBJECT (save_dialog), "save-multiple", save_multiple_str, NULL);
2906 gtk_window_set_title (GTK_WINDOW (save_dialog), _FM("sfil_ti_save_objects_files"));
2909 /* We must run this asynchronously, because the hildon dialog
2910 performs a gtk_dialog_run by itself which leads to gdk
2912 g_signal_connect (save_dialog, "response",
2913 G_CALLBACK (save_attachments_response), mime_parts);
2915 gtk_widget_show_all (save_dialog);
2919 show_remove_attachment_information (gpointer userdata)
2921 ModestMsgViewWindow *window = (ModestMsgViewWindow *) userdata;
2922 ModestMsgViewWindowPrivate *priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
2924 /* We're outside the main lock */
2925 gdk_threads_enter ();
2927 if (priv->remove_attachment_banner != NULL) {
2928 gtk_widget_destroy (priv->remove_attachment_banner);
2929 g_object_unref (priv->remove_attachment_banner);
2932 priv->remove_attachment_banner = g_object_ref (
2933 hildon_banner_show_animation (NULL, NULL, _("mcen_ib_removing_attachment")));
2935 gdk_threads_leave ();
2941 modest_msg_view_window_remove_attachments (ModestMsgViewWindow *window, gboolean get_all)
2943 ModestMsgViewWindowPrivate *priv;
2944 TnyList *mime_parts = NULL;
2945 gchar *confirmation_message;
2951 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window));
2952 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
2955 mime_parts = modest_msg_view_get_attachments (MODEST_MSG_VIEW (priv->msg_view));
2957 mime_parts = modest_msg_view_get_selected_attachments (MODEST_MSG_VIEW (priv->msg_view));
2959 /* Remove already purged messages from mime parts list */
2960 iter = tny_list_create_iterator (mime_parts);
2961 while (!tny_iterator_is_done (iter)) {
2962 TnyMimePart *part = TNY_MIME_PART (tny_iterator_get_current (iter));
2963 tny_iterator_next (iter);
2964 if (tny_mime_part_is_purged (part)) {
2965 tny_list_remove (mime_parts, (GObject *) part);
2967 g_object_unref (part);
2969 g_object_unref (iter);
2971 if (tny_list_get_length (mime_parts) == 0) {
2972 g_object_unref (mime_parts);
2976 n_attachments = tny_list_get_length (mime_parts);
2977 if (n_attachments == 1) {
2981 iter = tny_list_create_iterator (mime_parts);
2982 part = (TnyMimePart *) tny_iterator_get_current (iter);
2983 g_object_unref (iter);
2984 if (modest_tny_mime_part_is_msg (part)) {
2986 header = tny_msg_get_header (TNY_MSG (part));
2987 filename = tny_header_dup_subject (header);
2988 g_object_unref (header);
2989 if (filename == NULL)
2990 filename = g_strdup (_("mail_va_no_subject"));
2992 filename = g_strdup (tny_mime_part_get_filename (TNY_MIME_PART (part)));
2994 confirmation_message = g_strdup_printf (_("mcen_nc_purge_file_text"), filename);
2996 g_object_unref (part);
2998 confirmation_message = g_strdup_printf (ngettext("mcen_nc_purge_file_text",
2999 "mcen_nc_purge_files_text",
3000 n_attachments), n_attachments);
3002 response = modest_platform_run_confirmation_dialog (GTK_WINDOW (window),
3003 confirmation_message);
3004 g_free (confirmation_message);
3006 if (response != GTK_RESPONSE_OK) {
3007 g_object_unref (mime_parts);
3011 priv->purge_timeout = g_timeout_add (2000, show_remove_attachment_information, window);
3013 iter = tny_list_create_iterator (mime_parts);
3014 while (!tny_iterator_is_done (iter)) {
3017 part = (TnyMimePart *) tny_iterator_get_current (iter);
3018 tny_mime_part_set_purged (TNY_MIME_PART (part));
3019 g_object_unref (part);
3020 tny_iterator_next (iter);
3022 g_object_unref (iter);
3024 msg = tny_msg_view_get_msg (TNY_MSG_VIEW (priv->msg_view));
3025 tny_msg_view_clear (TNY_MSG_VIEW (priv->msg_view));
3026 tny_msg_rewrite_cache (msg);
3027 tny_msg_view_set_msg (TNY_MSG_VIEW (priv->msg_view), msg);
3028 g_object_unref (msg);
3030 g_object_unref (mime_parts);
3032 if (priv->purge_timeout > 0) {
3033 g_source_remove (priv->purge_timeout);
3034 priv->purge_timeout = 0;
3037 if (priv->remove_attachment_banner) {
3038 gtk_widget_destroy (priv->remove_attachment_banner);
3039 g_object_unref (priv->remove_attachment_banner);
3040 priv->remove_attachment_banner = NULL;
3048 update_window_title (ModestMsgViewWindow *window)
3050 ModestMsgViewWindowPrivate *priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
3052 TnyHeader *header = NULL;
3053 gchar *subject = NULL;
3055 msg = tny_msg_view_get_msg (TNY_MSG_VIEW (priv->msg_view));
3058 header = tny_msg_get_header (msg);
3059 subject = tny_header_dup_subject (header);
3060 g_object_unref (header);
3061 g_object_unref (msg);
3064 if ((subject == NULL)||(subject[0] == '\0')) {
3066 subject = g_strdup (_("mail_va_no_subject"));
3069 gtk_window_set_title (GTK_WINDOW (window), subject);
3073 static void on_move_focus (GtkWidget *widget,
3074 GtkDirectionType direction,
3077 g_signal_stop_emission_by_name (G_OBJECT (widget), "move-focus");
3081 fetch_image_open_stream (TnyStreamCache *self, gint64 *expected_size, gchar *uri)
3083 GnomeVFSResult result;
3084 GnomeVFSHandle *handle = NULL;
3085 GnomeVFSFileInfo *info = NULL;
3088 result = gnome_vfs_open (&handle, uri, GNOME_VFS_OPEN_READ);
3089 if (result != GNOME_VFS_OK) {
3094 info = gnome_vfs_file_info_new ();
3095 result = gnome_vfs_get_file_info_from_handle (handle, info, GNOME_VFS_FILE_INFO_DEFAULT);
3096 if (result != GNOME_VFS_OK || ! (info->valid_fields & GNOME_VFS_FILE_INFO_FIELDS_SIZE)) {
3097 /* We put a "safe" default size for going to cache */
3098 *expected_size = (300*1024);
3100 *expected_size = info->size;
3102 gnome_vfs_file_info_unref (info);
3104 stream = tny_vfs_stream_new (handle);
3113 TnyStream *output_stream;
3114 GtkWidget *msg_view;
3118 on_fetch_image_idle_refresh_view (gpointer userdata)
3121 FetchImageData *fidata = (FetchImageData *) userdata;
3122 g_message ("REFRESH VIEW");
3123 if (GTK_WIDGET_DRAWABLE (fidata->msg_view)) {
3124 g_message ("QUEUING DRAW");
3125 gtk_widget_queue_draw (fidata->msg_view);
3127 g_object_unref (fidata->msg_view);
3128 g_slice_free (FetchImageData, fidata);
3133 on_fetch_image_thread (gpointer userdata)
3135 FetchImageData *fidata = (FetchImageData *) userdata;
3136 TnyStreamCache *cache;
3137 TnyStream *cache_stream;
3139 cache = modest_runtime_get_images_cache ();
3140 cache_stream = tny_stream_cache_get_stream (cache, fidata->cache_id, (TnyStreamCacheOpenStreamFetcher) fetch_image_open_stream, (gpointer) fidata->uri);
3141 g_free (fidata->cache_id);
3142 g_free (fidata->uri);
3144 if (cache_stream != NULL) {
3145 tny_stream_write_to_stream (cache_stream, fidata->output_stream);
3146 tny_stream_close (cache_stream);
3147 g_object_unref (cache_stream);
3150 tny_stream_close (fidata->output_stream);
3151 g_object_unref (fidata->output_stream);
3154 gdk_threads_enter ();
3155 g_idle_add (on_fetch_image_idle_refresh_view, fidata);
3156 gdk_threads_leave ();
3162 on_fetch_image (ModestMsgView *msgview,
3165 ModestMsgViewWindow *window)
3167 const gchar *current_account;
3168 ModestMsgViewWindowPrivate *priv;
3169 FetchImageData *fidata;
3171 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
3173 current_account = modest_window_get_active_account (MODEST_WINDOW (window));
3175 fidata = g_slice_new0 (FetchImageData);
3176 fidata->msg_view = g_object_ref (msgview);
3177 fidata->uri = g_strdup (uri);
3178 fidata->cache_id = modest_images_cache_get_id (current_account, uri);
3179 fidata->output_stream = g_object_ref (stream);
3181 if (g_thread_create (on_fetch_image_thread, fidata, FALSE, NULL) == NULL) {
3182 g_object_unref (fidata->output_stream);
3183 g_free (fidata->cache_id);
3184 g_free (fidata->uri);
3185 g_object_unref (fidata->msg_view);
3186 g_slice_free (FetchImageData, fidata);
3187 tny_stream_close (stream);