1 /* Copyright (c) 2006, Nokia Corporation
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions are
8 * * Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 * * Redistributions in binary form must reproduce the above copyright
11 * notice, this list of conditions and the following disclaimer in the
12 * documentation and/or other materials provided with the distribution.
13 * * Neither the name of the Nokia Corporation nor the names of its
14 * contributors may be used to endorse or promote products derived from
15 * this software without specific prior written permission.
17 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
18 * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
19 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
20 * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
21 * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
22 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
23 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
24 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
25 * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
26 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
27 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29 #include <glib/gi18n.h>
31 #include <tny-account-store.h>
32 #include <tny-simple-list.h>
34 #include <tny-mime-part.h>
35 #include <tny-vfs-stream.h>
36 #include "modest-marshal.h"
37 #include "modest-platform.h"
38 #include <modest-utils.h>
39 #include <modest-maemo-utils.h>
40 #include <modest-tny-msg.h>
41 #include <modest-msg-view-window.h>
42 #include <modest-main-window-ui.h>
43 #include "modest-msg-view-window-ui-dimming.h"
44 #include <modest-widget-memory.h>
45 #include <modest-runtime.h>
46 #include <modest-window-priv.h>
47 #include <modest-tny-folder.h>
48 #include <modest-text-utils.h>
49 #include <modest-account-mgr-helpers.h>
50 #include "modest-progress-bar.h"
51 #include "modest-defs.h"
52 #include "modest-hildon-includes.h"
53 #include "modest-ui-dimming-manager.h"
54 #include <gdk/gdkkeysyms.h>
55 #include <modest-tny-account.h>
56 #include <modest-mime-part-view.h>
57 #include <modest-isearch-view.h>
58 #include <modest-tny-mime-part.h>
61 #include <glib/gstdio.h>
62 #include <modest-debug.h>
64 #define DEFAULT_FOLDER "MyDocs/.documents"
66 static void modest_msg_view_window_class_init (ModestMsgViewWindowClass *klass);
67 static void modest_msg_view_window_init (ModestMsgViewWindow *obj);
68 static void modest_header_view_observer_init(
69 ModestHeaderViewObserverIface *iface_class);
70 static void modest_msg_view_window_finalize (GObject *obj);
71 static void modest_msg_view_window_toggle_find_toolbar (GtkToggleAction *obj,
73 static void modest_msg_view_window_find_toolbar_close (GtkWidget *widget,
74 ModestMsgViewWindow *obj);
75 static void modest_msg_view_window_find_toolbar_search (GtkWidget *widget,
76 ModestMsgViewWindow *obj);
78 static void modest_msg_view_window_disconnect_signals (ModestWindow *self);
79 static void modest_msg_view_window_set_zoom (ModestWindow *window,
81 static gdouble modest_msg_view_window_get_zoom (ModestWindow *window);
82 static gboolean modest_msg_view_window_zoom_minus (ModestWindow *window);
83 static gboolean modest_msg_view_window_zoom_plus (ModestWindow *window);
84 static gboolean modest_msg_view_window_key_event (GtkWidget *window,
87 static gboolean modest_msg_view_window_window_state_event (GtkWidget *widget,
88 GdkEventWindowState *event,
90 static void modest_msg_view_window_update_priority (ModestMsgViewWindow *window);
92 static void modest_msg_view_window_show_toolbar (ModestWindow *window,
93 gboolean show_toolbar);
95 static void modest_msg_view_window_clipboard_owner_change (GtkClipboard *clipboard,
97 ModestMsgViewWindow *window);
99 static void modest_msg_view_window_on_row_changed (GtkTreeModel *header_model,
102 ModestMsgViewWindow *window);
104 static void modest_msg_view_window_on_row_deleted (GtkTreeModel *header_model,
106 ModestMsgViewWindow *window);
108 static void modest_msg_view_window_on_row_inserted (GtkTreeModel *header_model,
109 GtkTreePath *tree_path,
110 GtkTreeIter *tree_iter,
111 ModestMsgViewWindow *window);
113 static void modest_msg_view_window_on_row_reordered (GtkTreeModel *header_model,
117 ModestMsgViewWindow *window);
119 static void modest_msg_view_window_update_model_replaced (ModestHeaderViewObserver *window,
121 const gchar *tny_folder_id);
123 static void cancel_progressbar (GtkToolButton *toolbutton,
124 ModestMsgViewWindow *self);
126 static void on_queue_changed (ModestMailOperationQueue *queue,
127 ModestMailOperation *mail_op,
128 ModestMailOperationQueueNotification type,
129 ModestMsgViewWindow *self);
131 static void on_account_removed (TnyAccountStore *account_store,
135 static void on_move_focus (GtkWidget *widget,
136 GtkDirectionType direction,
139 static void view_msg_cb (ModestMailOperation *mail_op,
146 static void set_toolbar_mode (ModestMsgViewWindow *self,
147 ModestToolBarModes mode);
149 static void update_window_title (ModestMsgViewWindow *window);
151 static gboolean set_toolbar_transfer_mode (ModestMsgViewWindow *self);
152 static void init_window (ModestMsgViewWindow *obj);
154 static gboolean msg_is_visible (TnyHeader *header, gboolean check_outbox);
156 static void check_dimming_rules_after_change (ModestMsgViewWindow *window);
158 static gboolean modest_msg_view_window_scroll_child (ModestMsgViewWindow *self,
159 GtkScrollType scroll_type,
163 /* list my signals */
170 static const GtkToggleActionEntry msg_view_toggle_action_entries [] = {
171 { "FindInMessage", MODEST_TOOLBAR_ICON_FIND, N_("qgn_toolb_gene_find"), NULL, NULL, G_CALLBACK (modest_msg_view_window_toggle_find_toolbar), FALSE },
172 { "ToolsFindInMessage", NULL, N_("mcen_me_viewer_find"), "<CTRL>F", NULL, G_CALLBACK (modest_msg_view_window_toggle_find_toolbar), FALSE },
175 static const GtkRadioActionEntry msg_view_zoom_action_entries [] = {
176 { "Zoom50", NULL, N_("mcen_me_viewer_50"), NULL, NULL, 50 },
177 { "Zoom80", NULL, N_("mcen_me_viewer_80"), NULL, NULL, 80 },
178 { "Zoom100", NULL, N_("mcen_me_viewer_100"), NULL, NULL, 100 },
179 { "Zoom120", NULL, N_("mcen_me_viewer_120"), NULL, NULL, 120 },
180 { "Zoom150", NULL, N_("mcen_me_viewer_150"), NULL, NULL, 150 },
181 { "Zoom200", NULL, N_("mcen_me_viewer_200"), NULL, NULL, 200 }
184 typedef struct _ModestMsgViewWindowPrivate ModestMsgViewWindowPrivate;
185 struct _ModestMsgViewWindowPrivate {
188 GtkWidget *main_scroll;
189 GtkWidget *find_toolbar;
192 /* Progress observers */
193 GtkWidget *progress_bar;
194 GSList *progress_widgets;
197 GtkWidget *progress_toolitem;
198 GtkWidget *cancel_toolitem;
199 GtkWidget *prev_toolitem;
200 GtkWidget *next_toolitem;
201 ModestToolBarModes current_toolbar_mode;
203 /* Optimized view enabled */
204 gboolean optimized_view;
206 /* Whether this was created via the *_new_for_search_result() function. */
207 gboolean is_search_result;
209 /* Whether the message is in outbox */
212 /* A reference to the @model of the header view
213 * to allow selecting previous/next messages,
214 * if the message is currently selected in the header view.
216 const gchar *header_folder_id;
217 GtkTreeModel *header_model;
218 GtkTreeRowReference *row_reference;
219 GtkTreeRowReference *next_row_reference;
221 gulong clipboard_change_handler;
222 gulong queue_change_handler;
223 gulong account_removed_handler;
224 gulong row_changed_handler;
225 gulong row_deleted_handler;
226 gulong row_inserted_handler;
227 gulong rows_reordered_handler;
230 GtkWidget *remove_attachment_banner;
232 guint progress_bar_timeout;
239 #define MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE((o), \
240 MODEST_TYPE_MSG_VIEW_WINDOW, \
241 ModestMsgViewWindowPrivate))
243 static GtkWindowClass *parent_class = NULL;
245 /* uncomment the following if you have defined any signals */
246 static guint signals[LAST_SIGNAL] = {0};
249 modest_msg_view_window_get_type (void)
251 static GType my_type = 0;
253 static const GTypeInfo my_info = {
254 sizeof(ModestMsgViewWindowClass),
255 NULL, /* base init */
256 NULL, /* base finalize */
257 (GClassInitFunc) modest_msg_view_window_class_init,
258 NULL, /* class finalize */
259 NULL, /* class data */
260 sizeof(ModestMsgViewWindow),
262 (GInstanceInitFunc) modest_msg_view_window_init,
265 my_type = g_type_register_static (MODEST_TYPE_WINDOW,
266 "ModestMsgViewWindow",
269 static const GInterfaceInfo modest_header_view_observer_info =
271 (GInterfaceInitFunc) modest_header_view_observer_init,
272 NULL, /* interface_finalize */
273 NULL /* interface_data */
276 g_type_add_interface_static (my_type,
277 MODEST_TYPE_HEADER_VIEW_OBSERVER,
278 &modest_header_view_observer_info);
284 save_state (ModestWindow *self)
286 modest_widget_memory_save (modest_runtime_get_conf (),
288 MODEST_CONF_MSG_VIEW_WINDOW_KEY);
293 restore_settings (ModestMsgViewWindow *self)
296 ModestWindowPrivate *parent_priv = MODEST_WINDOW_GET_PRIVATE (self);
299 conf = modest_runtime_get_conf ();
300 action = gtk_ui_manager_get_action (parent_priv->ui_manager,
301 "/MenuBar/ViewMenu/ViewShowToolbarMenu/ViewShowToolbarNormalScreenMenu");
302 gtk_toggle_action_set_active (GTK_TOGGLE_ACTION (action),
303 modest_conf_get_bool (conf, MODEST_CONF_MSG_VIEW_WINDOW_SHOW_TOOLBAR, NULL));
304 action = gtk_ui_manager_get_action (parent_priv->ui_manager,
305 "/MenuBar/ViewMenu/ViewShowToolbarMenu/ViewShowToolbarFullScreenMenu");
306 gtk_toggle_action_set_active (GTK_TOGGLE_ACTION (action),
307 modest_conf_get_bool (conf, MODEST_CONF_MSG_VIEW_WINDOW_SHOW_TOOLBAR_FULLSCREEN, NULL));
308 modest_widget_memory_restore (conf,
310 MODEST_CONF_MSG_VIEW_WINDOW_KEY);
313 static gboolean modest_msg_view_window_scroll_child (ModestMsgViewWindow *self,
314 GtkScrollType scroll_type,
318 ModestMsgViewWindowPrivate *priv;
319 gboolean return_value;
321 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(self);
322 g_signal_emit_by_name (priv->main_scroll, "scroll-child", scroll_type, horizontal, &return_value);
327 add_scroll_binding (GtkBindingSet *binding_set,
329 GtkScrollType scroll)
331 guint keypad_keyval = keyval - GDK_Left + GDK_KP_Left;
333 gtk_binding_entry_add_signal (binding_set, keyval, 0,
335 GTK_TYPE_SCROLL_TYPE, scroll,
336 G_TYPE_BOOLEAN, FALSE);
337 gtk_binding_entry_add_signal (binding_set, keypad_keyval, 0,
339 GTK_TYPE_SCROLL_TYPE, scroll,
340 G_TYPE_BOOLEAN, FALSE);
344 modest_msg_view_window_class_init (ModestMsgViewWindowClass *klass)
346 GObjectClass *gobject_class;
347 ModestWindowClass *modest_window_class;
348 GtkBindingSet *binding_set;
350 gobject_class = (GObjectClass*) klass;
351 modest_window_class = (ModestWindowClass *) klass;
353 parent_class = g_type_class_peek_parent (klass);
354 gobject_class->finalize = modest_msg_view_window_finalize;
356 modest_window_class->set_zoom_func = modest_msg_view_window_set_zoom;
357 modest_window_class->get_zoom_func = modest_msg_view_window_get_zoom;
358 modest_window_class->zoom_minus_func = modest_msg_view_window_zoom_minus;
359 modest_window_class->zoom_plus_func = modest_msg_view_window_zoom_plus;
360 modest_window_class->show_toolbar_func = modest_msg_view_window_show_toolbar;
361 modest_window_class->disconnect_signals_func = modest_msg_view_window_disconnect_signals;
363 modest_window_class->save_state_func = save_state;
365 klass->scroll_child = modest_msg_view_window_scroll_child;
367 signals[MSG_CHANGED_SIGNAL] =
368 g_signal_new ("msg-changed",
369 G_TYPE_FROM_CLASS (gobject_class),
371 G_STRUCT_OFFSET (ModestMsgViewWindowClass, msg_changed),
373 modest_marshal_VOID__POINTER_POINTER,
374 G_TYPE_NONE, 2, G_TYPE_POINTER, G_TYPE_POINTER);
376 signals[SCROLL_CHILD_SIGNAL] =
377 g_signal_new ("scroll-child",
378 G_TYPE_FROM_CLASS (gobject_class),
379 G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
380 G_STRUCT_OFFSET (ModestMsgViewWindowClass, scroll_child),
382 modest_marshal_BOOLEAN__ENUM_BOOLEAN,
383 G_TYPE_BOOLEAN, 2, GTK_TYPE_SCROLL_TYPE, G_TYPE_BOOLEAN);
385 binding_set = gtk_binding_set_by_class (klass);
386 add_scroll_binding (binding_set, GDK_Up, GTK_SCROLL_STEP_UP);
387 add_scroll_binding (binding_set, GDK_Down, GTK_SCROLL_STEP_DOWN);
388 add_scroll_binding (binding_set, GDK_Page_Up, GTK_SCROLL_PAGE_UP);
389 add_scroll_binding (binding_set, GDK_Page_Down, GTK_SCROLL_PAGE_DOWN);
390 add_scroll_binding (binding_set, GDK_Home, GTK_SCROLL_START);
391 add_scroll_binding (binding_set, GDK_End, GTK_SCROLL_END);
393 g_type_class_add_private (gobject_class, sizeof(ModestMsgViewWindowPrivate));
397 static void modest_header_view_observer_init(
398 ModestHeaderViewObserverIface *iface_class)
400 iface_class->update_func = modest_msg_view_window_update_model_replaced;
404 modest_msg_view_window_init (ModestMsgViewWindow *obj)
406 ModestMsgViewWindowPrivate *priv;
407 ModestWindowPrivate *parent_priv = NULL;
408 GtkActionGroup *action_group = NULL;
409 GError *error = NULL;
410 GdkPixbuf *window_icon;
412 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(obj);
413 parent_priv = MODEST_WINDOW_GET_PRIVATE(obj);
414 parent_priv->ui_manager = gtk_ui_manager_new();
416 action_group = gtk_action_group_new ("ModestMsgViewWindowActions");
417 gtk_action_group_set_translation_domain (action_group, GETTEXT_PACKAGE);
419 /* Add common actions */
420 gtk_action_group_add_actions (action_group,
421 modest_action_entries,
422 G_N_ELEMENTS (modest_action_entries),
424 gtk_action_group_add_toggle_actions (action_group,
425 modest_toggle_action_entries,
426 G_N_ELEMENTS (modest_toggle_action_entries),
428 gtk_action_group_add_toggle_actions (action_group,
429 msg_view_toggle_action_entries,
430 G_N_ELEMENTS (msg_view_toggle_action_entries),
432 gtk_action_group_add_radio_actions (action_group,
433 msg_view_zoom_action_entries,
434 G_N_ELEMENTS (msg_view_zoom_action_entries),
436 G_CALLBACK (modest_ui_actions_on_change_zoom),
439 gtk_ui_manager_insert_action_group (parent_priv->ui_manager, action_group, 0);
440 g_object_unref (action_group);
442 /* Load the UI definition */
443 gtk_ui_manager_add_ui_from_file (parent_priv->ui_manager, MODEST_UIDIR "modest-msg-view-window-ui.xml",
446 g_printerr ("modest: could not merge modest-msg-view-window-ui.xml: %s\n", error->message);
447 g_error_free (error);
452 /* Add accelerators */
453 gtk_window_add_accel_group (GTK_WINDOW (obj),
454 gtk_ui_manager_get_accel_group (parent_priv->ui_manager));
456 priv->is_search_result = FALSE;
457 priv->is_outbox = FALSE;
459 priv->msg_view = NULL;
460 priv->header_model = NULL;
461 priv->header_folder_id = NULL;
462 priv->clipboard_change_handler = 0;
463 priv->queue_change_handler = 0;
464 priv->account_removed_handler = 0;
465 priv->row_changed_handler = 0;
466 priv->row_deleted_handler = 0;
467 priv->row_inserted_handler = 0;
468 priv->rows_reordered_handler = 0;
469 priv->current_toolbar_mode = TOOLBAR_MODE_NORMAL;
471 priv->optimized_view = FALSE;
472 priv->progress_bar_timeout = 0;
473 priv->purge_timeout = 0;
474 priv->remove_attachment_banner = NULL;
475 priv->msg_uid = NULL;
477 priv->sighandlers = NULL;
480 init_window (MODEST_MSG_VIEW_WINDOW(obj));
482 /* Set window icon */
483 window_icon = modest_platform_get_icon (MODEST_APP_MSG_VIEW_ICON, MODEST_ICON_SIZE_BIG);
485 gtk_window_set_icon (GTK_WINDOW (obj), window_icon);
486 g_object_unref (window_icon);
489 hildon_program_add_window (hildon_program_get_instance(),
492 modest_window_mgr_register_help_id (modest_runtime_get_window_mgr(),
493 GTK_WINDOW(obj),"applications_email_viewer");
498 set_toolbar_transfer_mode (ModestMsgViewWindow *self)
500 ModestMsgViewWindowPrivate *priv = NULL;
502 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self), FALSE);
504 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(self);
506 set_toolbar_mode (self, TOOLBAR_MODE_TRANSFER);
508 if (priv->progress_bar_timeout > 0) {
509 g_source_remove (priv->progress_bar_timeout);
510 priv->progress_bar_timeout = 0;
517 set_toolbar_mode (ModestMsgViewWindow *self,
518 ModestToolBarModes mode)
520 ModestWindowPrivate *parent_priv;
521 ModestMsgViewWindowPrivate *priv;
522 /* GtkWidget *widget = NULL; */
524 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self));
526 parent_priv = MODEST_WINDOW_GET_PRIVATE(self);
527 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(self);
529 /* Sets current toolbar mode */
530 priv->current_toolbar_mode = mode;
532 /* Update toolbar dimming state */
533 modest_ui_actions_check_toolbar_dimming_rules (MODEST_WINDOW (self));
536 case TOOLBAR_MODE_NORMAL:
537 if (priv->progress_toolitem) {
538 gtk_tool_item_set_expand (GTK_TOOL_ITEM (priv->progress_toolitem), FALSE);
539 gtk_widget_hide (priv->progress_toolitem);
542 if (priv->progress_bar)
543 gtk_widget_hide (priv->progress_bar);
545 if (priv->cancel_toolitem)
546 gtk_widget_hide (priv->cancel_toolitem);
548 if (priv->prev_toolitem)
549 gtk_widget_show (priv->prev_toolitem);
551 if (priv->next_toolitem)
552 gtk_widget_show (priv->next_toolitem);
554 /* Hide toolbar if optimized view is enabled */
555 if (priv->optimized_view) {
556 gtk_widget_set_no_show_all (parent_priv->toolbar, TRUE);
557 gtk_widget_hide (GTK_WIDGET(parent_priv->toolbar));
561 case TOOLBAR_MODE_TRANSFER:
562 if (priv->prev_toolitem)
563 gtk_widget_hide (priv->prev_toolitem);
565 if (priv->next_toolitem)
566 gtk_widget_hide (priv->next_toolitem);
568 if (priv->progress_bar)
569 gtk_widget_show (priv->progress_bar);
571 if (priv->progress_toolitem) {
572 gtk_tool_item_set_expand (GTK_TOOL_ITEM (priv->progress_toolitem), TRUE);
573 gtk_widget_show (priv->progress_toolitem);
576 if (priv->cancel_toolitem)
577 gtk_widget_show (priv->cancel_toolitem);
579 /* Show toolbar if it's hiden (optimized view ) */
580 if (priv->optimized_view) {
581 gtk_widget_set_no_show_all (parent_priv->toolbar, FALSE);
582 gtk_widget_show (GTK_WIDGET (parent_priv->toolbar));
587 g_return_if_reached ();
594 init_window (ModestMsgViewWindow *obj)
596 GtkWidget *main_vbox;
597 ModestMsgViewWindowPrivate *priv;
599 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(obj);
601 priv->msg_view = GTK_WIDGET (tny_platform_factory_new_msg_view (modest_tny_platform_factory_get_instance ()));
602 modest_msg_view_set_shadow_type (MODEST_MSG_VIEW (priv->msg_view), GTK_SHADOW_NONE);
603 main_vbox = gtk_vbox_new (FALSE, 6);
605 #ifdef MODEST_USE_MOZEMBED
606 priv->main_scroll = priv->msg_view;
607 gtk_widget_set_size_request (priv->msg_view, -1, 1600);
609 priv->main_scroll = gtk_scrolled_window_new (NULL, NULL);
610 gtk_container_add (GTK_CONTAINER (priv->main_scroll), priv->msg_view);
612 gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (priv->main_scroll), GTK_POLICY_NEVER, GTK_POLICY_AUTOMATIC);
613 gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW (priv->main_scroll), GTK_SHADOW_NONE);
614 modest_maemo_set_thumbable_scrollbar (GTK_SCROLLED_WINDOW(priv->main_scroll), TRUE);
616 gtk_box_pack_start (GTK_BOX(main_vbox), priv->main_scroll, TRUE, TRUE, 0);
617 gtk_container_add (GTK_CONTAINER(obj), main_vbox);
619 priv->find_toolbar = hildon_find_toolbar_new (NULL);
620 hildon_window_add_toolbar (HILDON_WINDOW (obj), GTK_TOOLBAR (priv->find_toolbar));
621 gtk_widget_set_no_show_all (priv->find_toolbar, TRUE);
623 gtk_widget_show_all (GTK_WIDGET(main_vbox));
627 modest_msg_view_window_disconnect_signals (ModestWindow *self)
629 ModestMsgViewWindowPrivate *priv;
630 ModestHeaderView *header_view = NULL;
631 ModestWindow *main_window = NULL;
633 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
635 if (gtk_clipboard_get (GDK_SELECTION_PRIMARY) &&
636 g_signal_handler_is_connected (gtk_clipboard_get (GDK_SELECTION_PRIMARY),
637 priv->clipboard_change_handler))
638 g_signal_handler_disconnect (gtk_clipboard_get (GDK_SELECTION_PRIMARY),
639 priv->clipboard_change_handler);
641 if (g_signal_handler_is_connected (G_OBJECT (modest_runtime_get_mail_operation_queue ()),
642 priv->queue_change_handler))
643 g_signal_handler_disconnect (G_OBJECT (modest_runtime_get_mail_operation_queue ()),
644 priv->queue_change_handler);
646 if (g_signal_handler_is_connected (G_OBJECT (modest_runtime_get_account_store ()),
647 priv->account_removed_handler))
648 g_signal_handler_disconnect (G_OBJECT (modest_runtime_get_account_store ()),
649 priv->account_removed_handler);
651 if (priv->header_model) {
652 if (g_signal_handler_is_connected(G_OBJECT (priv->header_model),
653 priv->row_changed_handler))
654 g_signal_handler_disconnect(G_OBJECT (priv->header_model),
655 priv->row_changed_handler);
657 if (g_signal_handler_is_connected(G_OBJECT (priv->header_model),
658 priv->row_deleted_handler))
659 g_signal_handler_disconnect(G_OBJECT (priv->header_model),
660 priv->row_deleted_handler);
662 if (g_signal_handler_is_connected(G_OBJECT (priv->header_model),
663 priv->row_inserted_handler))
664 g_signal_handler_disconnect(G_OBJECT (priv->header_model),
665 priv->row_inserted_handler);
667 if (g_signal_handler_is_connected(G_OBJECT (priv->header_model),
668 priv->rows_reordered_handler))
669 g_signal_handler_disconnect(G_OBJECT (priv->header_model),
670 priv->rows_reordered_handler);
673 modest_signal_mgr_disconnect_all_and_destroy (priv->sighandlers);
674 priv->sighandlers = NULL;
676 main_window = modest_window_mgr_get_main_window (modest_runtime_get_window_mgr(),
677 FALSE); /* don't create */
681 header_view = MODEST_HEADER_VIEW(
682 modest_main_window_get_child_widget(
683 MODEST_MAIN_WINDOW(main_window),
684 MODEST_MAIN_WINDOW_WIDGET_TYPE_HEADER_VIEW));
685 if (header_view == NULL)
688 modest_header_view_remove_observer(header_view,
689 MODEST_HEADER_VIEW_OBSERVER(self));
693 modest_msg_view_window_finalize (GObject *obj)
695 ModestMsgViewWindowPrivate *priv;
697 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (obj);
699 /* Sanity check: shouldn't be needed, the window mgr should
700 call this function before */
701 modest_msg_view_window_disconnect_signals (MODEST_WINDOW (obj));
703 if (priv->header_model != NULL) {
704 g_object_unref (priv->header_model);
705 priv->header_model = NULL;
708 if (priv->progress_bar_timeout > 0) {
709 g_source_remove (priv->progress_bar_timeout);
710 priv->progress_bar_timeout = 0;
713 if (priv->remove_attachment_banner) {
714 gtk_widget_destroy (priv->remove_attachment_banner);
715 g_object_unref (priv->remove_attachment_banner);
716 priv->remove_attachment_banner = NULL;
719 if (priv->purge_timeout > 0) {
720 g_source_remove (priv->purge_timeout);
721 priv->purge_timeout = 0;
724 if (priv->row_reference) {
725 gtk_tree_row_reference_free (priv->row_reference);
726 priv->row_reference = NULL;
729 if (priv->next_row_reference) {
730 gtk_tree_row_reference_free (priv->next_row_reference);
731 priv->next_row_reference = NULL;
735 g_free (priv->msg_uid);
736 priv->msg_uid = NULL;
739 G_OBJECT_CLASS(parent_class)->finalize (obj);
743 select_next_valid_row (GtkTreeModel *model,
744 GtkTreeRowReference **row_reference,
748 GtkTreeIter tmp_iter;
750 GtkTreePath *next = NULL;
751 gboolean retval = FALSE, finished;
753 g_return_val_if_fail (gtk_tree_row_reference_valid (*row_reference), FALSE);
755 path = gtk_tree_row_reference_get_path (*row_reference);
756 gtk_tree_model_get_iter (model, &tmp_iter, path);
757 gtk_tree_row_reference_free (*row_reference);
758 *row_reference = NULL;
762 TnyHeader *header = NULL;
764 if (gtk_tree_model_iter_next (model, &tmp_iter)) {
765 gtk_tree_model_get (model, &tmp_iter,
766 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
770 if (msg_is_visible (header, is_outbox)) {
771 next = gtk_tree_model_get_path (model, &tmp_iter);
772 *row_reference = gtk_tree_row_reference_new (model, next);
773 gtk_tree_path_free (next);
777 g_object_unref (header);
780 } else if (cycle && gtk_tree_model_get_iter_first (model, &tmp_iter)) {
781 next = gtk_tree_model_get_path (model, &tmp_iter);
783 /* Ensure that we are not selecting the same */
784 if (gtk_tree_path_compare (path, next) != 0) {
785 gtk_tree_model_get (model, &tmp_iter,
786 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
789 if (msg_is_visible (header, is_outbox)) {
790 *row_reference = gtk_tree_row_reference_new (model, next);
794 g_object_unref (header);
798 /* If we ended up in the same message
799 then there is no valid next
803 gtk_tree_path_free (next);
805 /* If there are no more messages and we don't
806 want to start again in the first one then
807 there is no valid next message */
813 gtk_tree_path_free (path);
818 /* TODO: This should be in _init(), with the parameters as properties. */
820 modest_msg_view_window_construct (ModestMsgViewWindow *self,
821 const gchar *modest_account_name,
822 const gchar *msg_uid)
825 ModestMsgViewWindowPrivate *priv = NULL;
826 ModestWindowPrivate *parent_priv = NULL;
827 ModestDimmingRulesGroup *menu_rules_group = NULL;
828 ModestDimmingRulesGroup *toolbar_rules_group = NULL;
829 ModestDimmingRulesGroup *clipboard_rules_group = NULL;
831 obj = G_OBJECT (self);
832 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(obj);
833 parent_priv = MODEST_WINDOW_GET_PRIVATE(obj);
835 priv->msg_uid = g_strdup (msg_uid);
838 parent_priv->menubar = modest_maemo_utils_get_manager_menubar_as_menu (parent_priv->ui_manager, "/MenuBar");
839 hildon_window_set_menu (HILDON_WINDOW(obj), GTK_MENU(parent_priv->menubar));
840 gtk_widget_show (parent_priv->menubar);
841 parent_priv->ui_dimming_manager = modest_ui_dimming_manager_new();
843 menu_rules_group = modest_dimming_rules_group_new (MODEST_DIMMING_RULES_MENU, FALSE);
844 toolbar_rules_group = modest_dimming_rules_group_new (MODEST_DIMMING_RULES_TOOLBAR, TRUE);
845 clipboard_rules_group = modest_dimming_rules_group_new (MODEST_DIMMING_RULES_CLIPBOARD, FALSE);
847 /* Add common dimming rules */
848 modest_dimming_rules_group_add_rules (menu_rules_group,
849 modest_msg_view_menu_dimming_entries,
850 G_N_ELEMENTS (modest_msg_view_menu_dimming_entries),
851 MODEST_WINDOW (self));
852 modest_dimming_rules_group_add_rules (toolbar_rules_group,
853 modest_msg_view_toolbar_dimming_entries,
854 G_N_ELEMENTS (modest_msg_view_toolbar_dimming_entries),
855 MODEST_WINDOW (self));
856 modest_dimming_rules_group_add_rules (clipboard_rules_group,
857 modest_msg_view_clipboard_dimming_entries,
858 G_N_ELEMENTS (modest_msg_view_clipboard_dimming_entries),
859 MODEST_WINDOW (self));
861 /* Insert dimming rules group for this window */
862 modest_ui_dimming_manager_insert_rules_group (parent_priv->ui_dimming_manager, menu_rules_group);
863 modest_ui_dimming_manager_insert_rules_group (parent_priv->ui_dimming_manager, toolbar_rules_group);
864 modest_ui_dimming_manager_insert_rules_group (parent_priv->ui_dimming_manager, clipboard_rules_group);
865 g_object_unref (menu_rules_group);
866 g_object_unref (toolbar_rules_group);
867 g_object_unref (clipboard_rules_group);
869 restore_settings (MODEST_MSG_VIEW_WINDOW(obj));
871 /* g_signal_connect (G_OBJECT(obj), "delete-event", G_CALLBACK(on_delete_event), obj); */
873 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);
874 g_signal_connect (G_OBJECT(priv->msg_view), "activate_link",
875 G_CALLBACK (modest_ui_actions_on_msg_link_clicked), obj);
876 g_signal_connect (G_OBJECT(priv->msg_view), "link_hover",
877 G_CALLBACK (modest_ui_actions_on_msg_link_hover), obj);
878 g_signal_connect (G_OBJECT(priv->msg_view), "attachment_clicked",
879 G_CALLBACK (modest_ui_actions_on_msg_attachment_clicked), obj);
880 g_signal_connect (G_OBJECT(priv->msg_view), "recpt_activated",
881 G_CALLBACK (modest_ui_actions_on_msg_recpt_activated), obj);
882 g_signal_connect (G_OBJECT(priv->msg_view), "link_contextual",
883 G_CALLBACK (modest_ui_actions_on_msg_link_contextual), obj);
885 g_signal_connect (G_OBJECT (obj), "key-release-event",
886 G_CALLBACK (modest_msg_view_window_key_event),
889 g_signal_connect (G_OBJECT (obj), "key-press-event",
890 G_CALLBACK (modest_msg_view_window_key_event),
893 g_signal_connect (G_OBJECT (obj), "window-state-event",
894 G_CALLBACK (modest_msg_view_window_window_state_event),
897 g_signal_connect (G_OBJECT (obj), "move-focus",
898 G_CALLBACK (on_move_focus), obj);
900 /* Mail Operation Queue */
901 priv->queue_change_handler = g_signal_connect (G_OBJECT (modest_runtime_get_mail_operation_queue ()),
903 G_CALLBACK (on_queue_changed),
906 /* Account manager */
907 priv->account_removed_handler = g_signal_connect (G_OBJECT (modest_runtime_get_account_store ()),
909 G_CALLBACK(on_account_removed),
912 modest_window_set_active_account (MODEST_WINDOW(obj), modest_account_name);
914 g_signal_connect (G_OBJECT (priv->find_toolbar), "close", G_CALLBACK (modest_msg_view_window_find_toolbar_close), obj);
915 g_signal_connect (G_OBJECT (priv->find_toolbar), "search", G_CALLBACK (modest_msg_view_window_find_toolbar_search), obj);
916 priv->last_search = NULL;
918 /* Init the clipboard actions dim status */
919 modest_msg_view_grab_focus(MODEST_MSG_VIEW (priv->msg_view));
921 update_window_title (MODEST_MSG_VIEW_WINDOW (obj));
926 /* FIXME: parameter checks */
928 modest_msg_view_window_new_with_header_model (TnyMsg *msg,
929 const gchar *modest_account_name,
930 const gchar *msg_uid,
932 GtkTreeRowReference *row_reference)
934 ModestMsgViewWindow *window = NULL;
935 ModestMsgViewWindowPrivate *priv = NULL;
936 TnyFolder *header_folder = NULL;
937 ModestHeaderView *header_view = NULL;
938 ModestWindow *main_window = NULL;
939 ModestWindowMgr *mgr = NULL;
942 modest_tny_mime_part_to_string (TNY_MIME_PART (msg), 0);
945 mgr = modest_runtime_get_window_mgr ();
946 window = MODEST_MSG_VIEW_WINDOW (modest_window_mgr_get_msg_view_window (mgr));
947 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), NULL);
949 modest_msg_view_window_construct (window, modest_account_name, msg_uid);
951 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
953 /* Remember the message list's TreeModel so we can detect changes
954 * and change the list selection when necessary: */
956 main_window = modest_window_mgr_get_main_window(mgr, FALSE); /* don't create */
958 header_view = MODEST_HEADER_VIEW(modest_main_window_get_child_widget(
959 MODEST_MAIN_WINDOW(main_window),
960 MODEST_MAIN_WINDOW_WIDGET_TYPE_HEADER_VIEW));
963 if (header_view != NULL){
964 header_folder = modest_header_view_get_folder(header_view);
965 /* This could happen if the header folder was
966 unseleted before opening this msg window (for
967 example if the user selects an account in the
968 folder view of the main window */
970 priv->is_outbox = (modest_tny_folder_guess_folder_type (header_folder) == TNY_FOLDER_TYPE_OUTBOX);
971 priv->header_folder_id = tny_folder_get_id(header_folder);
972 g_assert(priv->header_folder_id != NULL);
973 g_object_unref(header_folder);
977 /* Setup row references and connect signals */
978 priv->header_model = g_object_ref (model);
981 priv->row_reference = gtk_tree_row_reference_copy (row_reference);
982 priv->next_row_reference = gtk_tree_row_reference_copy (row_reference);
983 select_next_valid_row (model, &(priv->next_row_reference), TRUE, priv->is_outbox);
985 priv->row_reference = NULL;
986 priv->next_row_reference = NULL;
989 /* Connect signals */
990 priv->row_changed_handler =
991 g_signal_connect (GTK_TREE_MODEL(model), "row-changed",
992 G_CALLBACK(modest_msg_view_window_on_row_changed),
994 priv->row_deleted_handler =
995 g_signal_connect (GTK_TREE_MODEL(model), "row-deleted",
996 G_CALLBACK(modest_msg_view_window_on_row_deleted),
998 priv->row_inserted_handler =
999 g_signal_connect (GTK_TREE_MODEL(model), "row-inserted",
1000 G_CALLBACK(modest_msg_view_window_on_row_inserted),
1002 priv->rows_reordered_handler =
1003 g_signal_connect(GTK_TREE_MODEL(model), "rows-reordered",
1004 G_CALLBACK(modest_msg_view_window_on_row_reordered),
1007 if (header_view != NULL){
1008 modest_header_view_add_observer(header_view,
1009 MODEST_HEADER_VIEW_OBSERVER(window));
1012 tny_msg_view_set_msg (TNY_MSG_VIEW (priv->msg_view), msg);
1013 update_window_title (MODEST_MSG_VIEW_WINDOW (window));
1014 gtk_widget_show_all (GTK_WIDGET (window));
1015 modest_msg_view_window_update_priority (window);
1017 /* Check dimming rules */
1018 modest_ui_actions_check_toolbar_dimming_rules (MODEST_WINDOW (window));
1019 modest_ui_actions_check_menu_dimming_rules (MODEST_WINDOW (window));
1020 modest_window_check_dimming_rules_group (MODEST_WINDOW (window), MODEST_DIMMING_RULES_CLIPBOARD);
1022 return MODEST_WINDOW(window);
1026 modest_msg_view_window_new_for_search_result (TnyMsg *msg,
1027 const gchar *modest_account_name,
1028 const gchar *msg_uid)
1030 ModestMsgViewWindow *window = NULL;
1031 ModestMsgViewWindowPrivate *priv = NULL;
1032 ModestWindowMgr *mgr = NULL;
1034 mgr = modest_runtime_get_window_mgr ();
1035 window = MODEST_MSG_VIEW_WINDOW (modest_window_mgr_get_msg_view_window (mgr));
1036 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), NULL);
1037 modest_msg_view_window_construct (window, modest_account_name, msg_uid);
1039 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1041 /* Remember that this is a search result,
1042 * so we can disable some UI appropriately: */
1043 priv->is_search_result = TRUE;
1045 tny_msg_view_set_msg (TNY_MSG_VIEW (priv->msg_view), msg);
1047 update_window_title (window);
1048 gtk_widget_show_all (GTK_WIDGET (window));
1049 modest_msg_view_window_update_priority (window);
1051 /* Check dimming rules */
1052 modest_ui_actions_check_toolbar_dimming_rules (MODEST_WINDOW (window));
1053 modest_ui_actions_check_menu_dimming_rules (MODEST_WINDOW (window));
1054 modest_window_check_dimming_rules_group (MODEST_WINDOW (window), MODEST_DIMMING_RULES_CLIPBOARD);
1056 return MODEST_WINDOW(window);
1060 modest_msg_view_window_new_for_attachment (TnyMsg *msg,
1061 const gchar *modest_account_name,
1062 const gchar *msg_uid)
1064 GObject *obj = NULL;
1065 ModestMsgViewWindowPrivate *priv;
1066 ModestWindowMgr *mgr = NULL;
1068 g_return_val_if_fail (msg, NULL);
1069 mgr = modest_runtime_get_window_mgr ();
1070 obj = G_OBJECT (modest_window_mgr_get_msg_view_window (mgr));
1071 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (obj);
1072 modest_msg_view_window_construct (MODEST_MSG_VIEW_WINDOW (obj),
1073 modest_account_name, msg_uid);
1075 tny_msg_view_set_msg (TNY_MSG_VIEW (priv->msg_view), msg);
1076 update_window_title (MODEST_MSG_VIEW_WINDOW (obj));
1078 gtk_widget_show_all (GTK_WIDGET (obj));
1080 /* Check dimming rules */
1081 modest_ui_actions_check_toolbar_dimming_rules (MODEST_WINDOW (obj));
1082 modest_ui_actions_check_menu_dimming_rules (MODEST_WINDOW (obj));
1083 modest_window_check_dimming_rules_group (MODEST_WINDOW (obj), MODEST_DIMMING_RULES_CLIPBOARD);
1085 return MODEST_WINDOW(obj);
1089 modest_msg_view_window_on_row_changed (GtkTreeModel *header_model,
1092 ModestMsgViewWindow *window)
1094 check_dimming_rules_after_change (window);
1098 modest_msg_view_window_on_row_deleted(GtkTreeModel *header_model,
1100 ModestMsgViewWindow *window)
1102 check_dimming_rules_after_change (window);
1104 /* The window could have dissapeared */
1107 check_dimming_rules_after_change (ModestMsgViewWindow *window)
1109 modest_ui_actions_check_menu_dimming_rules (MODEST_WINDOW (window));
1110 modest_ui_actions_check_toolbar_dimming_rules (MODEST_WINDOW (window));
1114 /* On insertions we check if the folder still has the message we are
1115 * showing or do not. If do not, we do nothing. Which means we are still
1116 * not attached to any header folder and thus next/prev buttons are
1117 * still dimmed. Once the message that is shown by msg-view is found, the
1118 * new model of header-view will be attached and the references will be set.
1119 * On each further insertions dimming rules will be checked. However
1120 * this requires extra CPU time at least works.
1121 * (An message might be deleted from TnyFolder and thus will not be
1122 * inserted into the model again for example if it is removed by the
1123 * imap server and the header view is refreshed.)
1126 modest_msg_view_window_on_row_inserted (GtkTreeModel *model,
1127 GtkTreePath *tree_path,
1128 GtkTreeIter *tree_iter,
1129 ModestMsgViewWindow *window)
1131 ModestMsgViewWindowPrivate *priv = NULL;
1132 TnyHeader *header = NULL;
1134 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window));
1135 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1137 g_assert (model == priv->header_model);
1139 /* Check if the newly inserted message is the same we are actually
1140 * showing. IF not, we should remain detached from the header model
1141 * and thus prev and next toolbar buttons should remain dimmed. */
1142 gtk_tree_model_get (model, tree_iter,
1143 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
1146 if (TNY_IS_HEADER (header)) {
1149 uid = modest_tny_folder_get_header_unique_id (header);
1150 if (!g_str_equal(priv->msg_uid, uid)) {
1151 check_dimming_rules_after_change (window);
1153 g_object_unref (G_OBJECT(header));
1157 g_object_unref(G_OBJECT(header));
1160 if (priv->row_reference) {
1161 gtk_tree_row_reference_free (priv->row_reference);
1164 /* Setup row_reference for the actual msg. */
1165 priv->row_reference = gtk_tree_row_reference_new (priv->header_model, tree_path);
1166 if (priv->row_reference == NULL) {
1167 g_warning("No reference for msg header item.");
1171 /* Now set up next_row_reference. */
1172 if (priv->next_row_reference) {
1173 gtk_tree_row_reference_free (priv->next_row_reference);
1176 priv->next_row_reference = gtk_tree_row_reference_copy (priv->row_reference);
1177 select_next_valid_row (priv->header_model,
1178 &(priv->next_row_reference), FALSE, priv->is_outbox);
1180 /* Connect the remaining callbacks to become able to detect
1181 * changes in header-view. */
1182 priv->row_changed_handler =
1183 g_signal_connect (priv->header_model, "row-changed",
1184 G_CALLBACK (modest_msg_view_window_on_row_changed),
1186 priv->row_deleted_handler =
1187 g_signal_connect (priv->header_model, "row-deleted",
1188 G_CALLBACK (modest_msg_view_window_on_row_deleted),
1190 priv->rows_reordered_handler =
1191 g_signal_connect (priv->header_model, "rows-reordered",
1192 G_CALLBACK (modest_msg_view_window_on_row_reordered),
1195 check_dimming_rules_after_change (window);
1199 modest_msg_view_window_on_row_reordered (GtkTreeModel *header_model,
1203 ModestMsgViewWindow *window)
1205 ModestMsgViewWindowPrivate *priv = NULL;
1206 gboolean already_changed = FALSE;
1208 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(window);
1210 /* If the current row was reordered select the proper next
1211 valid row. The same if the next row reference changes */
1212 if (priv->row_reference &&
1213 gtk_tree_row_reference_valid (priv->row_reference)) {
1215 path = gtk_tree_row_reference_get_path (priv->row_reference);
1216 if (gtk_tree_path_compare (path, arg1) == 0) {
1217 if (priv->next_row_reference) {
1218 gtk_tree_row_reference_free (priv->next_row_reference);
1220 priv->next_row_reference = gtk_tree_row_reference_copy (priv->row_reference);
1221 select_next_valid_row (header_model, &(priv->next_row_reference), FALSE, priv->is_outbox);
1222 already_changed = TRUE;
1224 gtk_tree_path_free (path);
1226 if (!already_changed &&
1227 priv->next_row_reference &&
1228 gtk_tree_row_reference_valid (priv->next_row_reference)) {
1230 path = gtk_tree_row_reference_get_path (priv->next_row_reference);
1231 if (gtk_tree_path_compare (path, arg1) == 0) {
1232 if (priv->next_row_reference) {
1233 gtk_tree_row_reference_free (priv->next_row_reference);
1235 priv->next_row_reference = gtk_tree_row_reference_copy (priv->row_reference);
1236 select_next_valid_row (header_model, &(priv->next_row_reference), FALSE, priv->is_outbox);
1238 gtk_tree_path_free (path);
1240 check_dimming_rules_after_change (window);
1243 /* The modest_msg_view_window_update_model_replaced implements update
1244 * function for ModestHeaderViewObserver. Checks whether the TnyFolder
1245 * actually belongs to the header-view is the same as the TnyFolder of
1246 * the message of msg-view or not. If they are different, there is
1247 * nothing to do. If they are the same, then the model has replaced and
1248 * the reference in msg-view shall be replaced from the old model to
1249 * the new model. In this case the view will be detached from it's
1250 * header folder. From this point the next/prev buttons are dimmed.
1253 modest_msg_view_window_update_model_replaced (ModestHeaderViewObserver *observer,
1254 GtkTreeModel *model,
1255 const gchar *tny_folder_id)
1257 ModestMsgViewWindowPrivate *priv = NULL;
1258 ModestMsgViewWindow *window = NULL;
1260 g_assert(MODEST_IS_HEADER_VIEW_OBSERVER(observer));
1261 g_assert(MODEST_IS_MSG_VIEW_WINDOW(observer));
1263 window = MODEST_MSG_VIEW_WINDOW(observer);
1264 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(window);
1266 /* If there is an other folder in the header-view then we do
1267 * not care about it's model (msg list). Else if the
1268 * header-view shows the folder the msg shown by us is in, we
1269 * shall replace our model reference and make some check. */
1270 if(model == NULL || tny_folder_id == NULL ||
1271 (priv->header_folder_id && !g_str_equal(tny_folder_id, priv->header_folder_id)))
1274 /* Model is changed(replaced), so we should forget the old
1275 * one. Because there might be other references and there
1276 * might be some change on the model even if we unreferenced
1277 * it, we need to disconnect our signals here. */
1278 if (priv->header_model) {
1279 if (g_signal_handler_is_connected(G_OBJECT (priv->header_model),
1280 priv->row_changed_handler))
1281 g_signal_handler_disconnect(G_OBJECT (priv->header_model),
1282 priv->row_changed_handler);
1283 if (g_signal_handler_is_connected(G_OBJECT (priv->header_model),
1284 priv->row_deleted_handler))
1285 g_signal_handler_disconnect(G_OBJECT (priv->header_model),
1286 priv->row_deleted_handler);
1287 if (g_signal_handler_is_connected(G_OBJECT (priv->header_model),
1288 priv->row_inserted_handler))
1289 g_signal_handler_disconnect(G_OBJECT (priv->header_model),
1290 priv->row_inserted_handler);
1291 if (g_signal_handler_is_connected(G_OBJECT (priv->header_model),
1292 priv->rows_reordered_handler))
1293 g_signal_handler_disconnect(G_OBJECT (priv->header_model),
1294 priv->rows_reordered_handler);
1297 if (priv->row_reference)
1298 gtk_tree_row_reference_free (priv->row_reference);
1299 if (priv->next_row_reference)
1300 gtk_tree_row_reference_free (priv->next_row_reference);
1301 g_object_unref(priv->header_model);
1304 priv->row_changed_handler = 0;
1305 priv->row_deleted_handler = 0;
1306 priv->row_inserted_handler = 0;
1307 priv->rows_reordered_handler = 0;
1308 priv->next_row_reference = NULL;
1309 priv->row_reference = NULL;
1310 priv->header_model = NULL;
1313 priv->header_model = g_object_ref (model);
1315 /* Also we must connect to the new model for row insertions.
1316 * Only for insertions now. We will need other ones only after
1317 * the msg is show by msg-view is added to the new model. */
1318 priv->row_inserted_handler =
1319 g_signal_connect (priv->header_model, "row-inserted",
1320 G_CALLBACK(modest_msg_view_window_on_row_inserted),
1323 modest_ui_actions_check_menu_dimming_rules(MODEST_WINDOW(window));
1324 modest_ui_actions_check_toolbar_dimming_rules(MODEST_WINDOW(window));
1328 modest_msg_view_window_toolbar_on_transfer_mode (ModestMsgViewWindow *self)
1330 ModestMsgViewWindowPrivate *priv= NULL;
1332 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self), FALSE);
1333 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
1335 return priv->current_toolbar_mode == TOOLBAR_MODE_TRANSFER;
1339 modest_msg_view_window_get_header (ModestMsgViewWindow *self)
1341 ModestMsgViewWindowPrivate *priv= NULL;
1343 TnyHeader *header = NULL;
1344 GtkTreePath *path = NULL;
1347 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self), NULL);
1348 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
1350 /* If the message was not obtained from a treemodel,
1351 * for instance if it was opened directly by the search UI:
1353 if (priv->header_model == NULL ||
1354 priv->row_reference == NULL ||
1355 !gtk_tree_row_reference_valid (priv->row_reference)) {
1356 msg = modest_msg_view_window_get_message (self);
1358 header = tny_msg_get_header (msg);
1359 g_object_unref (msg);
1364 /* Get iter of the currently selected message in the header view: */
1365 path = gtk_tree_row_reference_get_path (priv->row_reference);
1366 g_return_val_if_fail (path != NULL, NULL);
1367 gtk_tree_model_get_iter (priv->header_model,
1371 /* Get current message header */
1372 gtk_tree_model_get (priv->header_model, &iter,
1373 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
1376 gtk_tree_path_free (path);
1381 modest_msg_view_window_get_message (ModestMsgViewWindow *self)
1383 ModestMsgViewWindowPrivate *priv;
1385 g_return_val_if_fail (self, NULL);
1387 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(self);
1389 return tny_msg_view_get_msg (TNY_MSG_VIEW (priv->msg_view));
1393 modest_msg_view_window_get_message_uid (ModestMsgViewWindow *self)
1395 ModestMsgViewWindowPrivate *priv;
1397 g_return_val_if_fail (self, NULL);
1399 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
1401 return (const gchar*) priv->msg_uid;
1405 modest_msg_view_window_toggle_find_toolbar (GtkToggleAction *toggle,
1408 ModestMsgViewWindow *window = MODEST_MSG_VIEW_WINDOW (data);
1409 ModestMsgViewWindowPrivate *priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1410 ModestWindowPrivate *parent_priv = MODEST_WINDOW_GET_PRIVATE (window);
1414 is_active = gtk_toggle_action_get_active (toggle);
1417 gtk_widget_show (priv->find_toolbar);
1418 hildon_find_toolbar_highlight_entry (HILDON_FIND_TOOLBAR (priv->find_toolbar), TRUE);
1420 gtk_widget_hide (priv->find_toolbar);
1423 /* update the toggle buttons status */
1424 action = gtk_ui_manager_get_action (parent_priv->ui_manager, "/ToolBar/FindInMessage");
1425 modest_utils_toggle_action_set_active_block_notify (GTK_TOGGLE_ACTION (action), is_active);
1426 action = gtk_ui_manager_get_action (parent_priv->ui_manager, "/MenuBar/ToolsMenu/ToolsFindInMessageMenu");
1427 modest_utils_toggle_action_set_active_block_notify (GTK_TOGGLE_ACTION (action), is_active);
1432 modest_msg_view_window_find_toolbar_close (GtkWidget *widget,
1433 ModestMsgViewWindow *obj)
1435 GtkToggleAction *toggle;
1436 ModestWindowPrivate *parent_priv;
1437 ModestMsgViewWindowPrivate *priv;
1439 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (obj);
1440 parent_priv = MODEST_WINDOW_GET_PRIVATE (obj);
1442 toggle = GTK_TOGGLE_ACTION (gtk_ui_manager_get_action (parent_priv->ui_manager, "/ToolBar/FindInMessage"));
1443 gtk_toggle_action_set_active (toggle, FALSE);
1444 modest_msg_view_grab_focus (MODEST_MSG_VIEW (priv->msg_view));
1448 modest_msg_view_window_find_toolbar_search (GtkWidget *widget,
1449 ModestMsgViewWindow *obj)
1451 gchar *current_search;
1452 ModestMsgViewWindowPrivate *priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (obj);
1454 if (modest_mime_part_view_is_empty (MODEST_MIME_PART_VIEW (priv->msg_view))) {
1455 hildon_banner_show_information (NULL, NULL, _("mail_ib_nothing_to_find"));
1459 g_object_get (G_OBJECT (widget), "prefix", ¤t_search, NULL);
1461 if ((current_search == NULL) || (strcmp (current_search, "") == 0)) {
1462 g_free (current_search);
1463 hildon_banner_show_information (NULL, NULL, dgettext("hildon-common-strings", "ecdg_ib_find_rep_enter_text"));
1467 if ((priv->last_search == NULL) || (strcmp (priv->last_search, current_search) != 0)) {
1469 g_free (priv->last_search);
1470 priv->last_search = g_strdup (current_search);
1471 result = modest_isearch_view_search (MODEST_ISEARCH_VIEW (priv->msg_view),
1474 hildon_banner_show_information (NULL, NULL, dgettext("hildon-libs", "ckct_ib_find_no_matches"));
1475 g_free (priv->last_search);
1476 priv->last_search = NULL;
1478 modest_msg_view_grab_focus (MODEST_MSG_VIEW (priv->msg_view));
1479 hildon_find_toolbar_highlight_entry (HILDON_FIND_TOOLBAR (priv->find_toolbar), TRUE);
1482 if (!modest_isearch_view_search_next (MODEST_ISEARCH_VIEW (priv->msg_view))) {
1483 hildon_banner_show_information (NULL, NULL, dgettext("hildon-libs", "ckct_ib_find_search_complete"));
1484 g_free (priv->last_search);
1485 priv->last_search = NULL;
1487 modest_msg_view_grab_focus (MODEST_MSG_VIEW (priv->msg_view));
1488 hildon_find_toolbar_highlight_entry (HILDON_FIND_TOOLBAR (priv->find_toolbar), TRUE);
1492 g_free (current_search);
1497 modest_msg_view_window_set_zoom (ModestWindow *window,
1500 ModestMsgViewWindowPrivate *priv;
1501 ModestWindowPrivate *parent_priv;
1502 GtkAction *action = NULL;
1503 gint int_zoom = (gint) rint (zoom*100.0+0.1);
1505 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window));
1507 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1508 parent_priv = MODEST_WINDOW_GET_PRIVATE (window);
1509 modest_zoomable_set_zoom (MODEST_ZOOMABLE (priv->msg_view), zoom);
1511 action = gtk_ui_manager_get_action (parent_priv->ui_manager,
1512 "/MenuBar/ViewMenu/ZoomMenu/Zoom50Menu");
1514 gtk_radio_action_set_current_value (GTK_RADIO_ACTION (action), int_zoom);
1518 modest_msg_view_window_get_zoom (ModestWindow *window)
1520 ModestMsgViewWindowPrivate *priv;
1522 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), 1.0);
1524 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1525 return modest_zoomable_get_zoom (MODEST_ZOOMABLE (priv->msg_view));
1529 modest_msg_view_window_zoom_plus (ModestWindow *window)
1531 ModestWindowPrivate *parent_priv;
1532 GtkRadioAction *zoom_radio_action;
1533 GSList *group, *node;
1535 parent_priv = MODEST_WINDOW_GET_PRIVATE (window);
1536 zoom_radio_action = GTK_RADIO_ACTION (gtk_ui_manager_get_action (parent_priv->ui_manager,
1537 "/MenuBar/ViewMenu/ZoomMenu/Zoom50Menu"));
1539 group = gtk_radio_action_get_group (zoom_radio_action);
1541 if (gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (group->data))) {
1542 hildon_banner_show_information (NULL, NULL, dgettext("hildon-common-strings", "ckct_ib_max_zoom_level_reached"));
1546 for (node = group; node != NULL; node = g_slist_next (node)) {
1547 if ((node->next != NULL) && gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (node->next->data))) {
1548 gtk_toggle_action_set_active (GTK_TOGGLE_ACTION (node->data), TRUE);
1556 modest_msg_view_window_zoom_minus (ModestWindow *window)
1558 ModestWindowPrivate *parent_priv;
1559 GtkRadioAction *zoom_radio_action;
1560 GSList *group, *node;
1562 parent_priv = MODEST_WINDOW_GET_PRIVATE (window);
1563 zoom_radio_action = GTK_RADIO_ACTION (gtk_ui_manager_get_action (parent_priv->ui_manager,
1564 "/MenuBar/ViewMenu/ZoomMenu/Zoom50Menu"));
1566 group = gtk_radio_action_get_group (zoom_radio_action);
1568 for (node = group; node != NULL; node = g_slist_next (node)) {
1569 if (gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (node->data))) {
1570 if (node->next != NULL) {
1571 gtk_toggle_action_set_active (GTK_TOGGLE_ACTION (node->next->data), TRUE);
1574 hildon_banner_show_information (NULL, NULL, dgettext("hildon-common-strings", "ckct_ib_min_zoom_level_reached"));
1584 modest_msg_view_window_key_event (GtkWidget *window,
1590 focus = gtk_window_get_focus (GTK_WINDOW (window));
1592 /* for the find toolbar case */
1593 if (focus && GTK_IS_ENTRY (focus)) {
1594 if (event->keyval == GDK_BackSpace) {
1596 copy = gdk_event_copy ((GdkEvent *) event);
1597 gtk_widget_event (focus, copy);
1598 gdk_event_free (copy);
1603 if (event->keyval == GDK_Up || event->keyval == GDK_KP_Up ||
1604 event->keyval == GDK_Down || event->keyval == GDK_KP_Down ||
1605 event->keyval == GDK_Page_Up || event->keyval == GDK_KP_Page_Up ||
1606 event->keyval == GDK_Page_Down || event->keyval == GDK_KP_Page_Down ||
1607 event->keyval == GDK_Home || event->keyval == GDK_KP_Home ||
1608 event->keyval == GDK_End || event->keyval == GDK_KP_End) {
1609 /* ModestMsgViewWindowPrivate *priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window); */
1610 /* gboolean return_value; */
1612 if (event->type == GDK_KEY_PRESS) {
1613 GtkScrollType scroll_type;
1615 switch (event->keyval) {
1618 scroll_type = GTK_SCROLL_STEP_UP; break;
1621 scroll_type = GTK_SCROLL_STEP_DOWN; break;
1623 case GDK_KP_Page_Up:
1624 scroll_type = GTK_SCROLL_PAGE_UP; break;
1626 case GDK_KP_Page_Down:
1627 scroll_type = GTK_SCROLL_PAGE_DOWN; break;
1630 scroll_type = GTK_SCROLL_START; break;
1633 scroll_type = GTK_SCROLL_END; break;
1634 default: scroll_type = GTK_SCROLL_NONE;
1637 /* g_signal_emit_by_name (G_OBJECT (priv->main_scroll), "scroll-child", */
1638 /* scroll_type, FALSE, &return_value); */
1649 modest_msg_view_window_last_message_selected (ModestMsgViewWindow *window)
1652 ModestMsgViewWindowPrivate *priv;
1653 GtkTreeIter tmp_iter;
1654 gboolean is_last_selected;
1656 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), TRUE);
1657 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1659 /*if no model (so no rows at all), then virtually we are the last*/
1660 if (!priv->header_model || !priv->row_reference)
1663 if (!gtk_tree_row_reference_valid (priv->row_reference))
1666 path = gtk_tree_row_reference_get_path (priv->row_reference);
1670 is_last_selected = TRUE;
1671 while (is_last_selected) {
1673 gtk_tree_path_next (path);
1674 if (!gtk_tree_model_get_iter (priv->header_model, &tmp_iter, path))
1676 gtk_tree_model_get (priv->header_model, &tmp_iter,
1677 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
1680 if (msg_is_visible (header, priv->is_outbox))
1681 is_last_selected = FALSE;
1682 g_object_unref(G_OBJECT(header));
1685 gtk_tree_path_free (path);
1686 return is_last_selected;
1690 modest_msg_view_window_has_headers_model (ModestMsgViewWindow *window)
1692 ModestMsgViewWindowPrivate *priv;
1694 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), TRUE);
1695 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1697 return priv->header_model != NULL;
1701 modest_msg_view_window_is_search_result (ModestMsgViewWindow *window)
1703 ModestMsgViewWindowPrivate *priv;
1705 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), TRUE);
1706 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1708 return priv->is_search_result;
1712 msg_is_visible (TnyHeader *header, gboolean check_outbox)
1714 return (!(tny_header_get_flags(header) & TNY_HEADER_FLAG_DELETED)) &&
1715 ( (!check_outbox) || (modest_tny_all_send_queues_get_msg_status (header) != MODEST_TNY_SEND_QUEUE_FAILED)) ;
1720 modest_msg_view_window_first_message_selected (ModestMsgViewWindow *window)
1723 ModestMsgViewWindowPrivate *priv;
1724 gboolean is_first_selected;
1725 GtkTreeIter tmp_iter;
1727 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), TRUE);
1728 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1730 /*if no model (so no rows at all), then virtually we are the first*/
1731 if (!priv->header_model || !priv->row_reference)
1734 if (!gtk_tree_row_reference_valid (priv->row_reference))
1737 path = gtk_tree_row_reference_get_path (priv->row_reference);
1741 is_first_selected = TRUE;
1742 while (is_first_selected) {
1744 if(!gtk_tree_path_prev (path))
1746 /* Here the 'if' is needless for logic, but let make sure
1747 * iter is valid for gtk_tree_model_get. */
1748 if (!gtk_tree_model_get_iter (priv->header_model, &tmp_iter, path))
1750 gtk_tree_model_get (priv->header_model, &tmp_iter,
1751 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
1754 if (msg_is_visible (header, priv->is_outbox))
1755 is_first_selected = FALSE;
1756 g_object_unref(G_OBJECT(header));
1759 gtk_tree_path_free (path);
1760 return is_first_selected;
1765 GtkTreeRowReference *row_reference;
1769 message_reader_performer (gboolean canceled,
1771 GtkWindow *parent_window,
1772 TnyAccount *account,
1775 ModestMailOperation *mail_op = NULL;
1776 MsgReaderInfo *info;
1778 info = (MsgReaderInfo *) user_data;
1779 if (canceled || err) {
1783 /* Register the header - it'll be unregistered in the callback */
1784 modest_window_mgr_register_header (modest_runtime_get_window_mgr (), info->header, NULL);
1786 /* New mail operation */
1787 mail_op = modest_mail_operation_new_with_error_handling (G_OBJECT(parent_window),
1788 modest_ui_actions_disk_operations_error_handler,
1791 modest_mail_operation_queue_add (modest_runtime_get_mail_operation_queue (), mail_op);
1792 modest_mail_operation_get_msg (mail_op, info->header, TRUE, view_msg_cb, info->row_reference);
1793 g_object_unref (mail_op);
1795 /* Update dimming rules */
1796 modest_ui_actions_check_toolbar_dimming_rules (MODEST_WINDOW (parent_window));
1797 modest_ui_actions_check_menu_dimming_rules (MODEST_WINDOW (parent_window));
1800 /* Frees. The row_reference will be freed by the view_msg_cb callback */
1801 g_object_unref (info->header);
1802 g_slice_free (MsgReaderInfo, info);
1807 * Reads the message whose summary item is @header. It takes care of
1808 * several things, among others:
1810 * If the message was not previously downloaded then ask the user
1811 * before downloading. If there is no connection launch the connection
1812 * dialog. Update toolbar dimming rules.
1814 * Returns: TRUE if the mail operation was started, otherwise if the
1815 * user do not want to download the message, or if the user do not
1816 * want to connect, then the operation is not issued
1819 message_reader (ModestMsgViewWindow *window,
1820 ModestMsgViewWindowPrivate *priv,
1822 GtkTreeRowReference *row_reference)
1824 gboolean already_showing = FALSE;
1825 ModestWindow *msg_window = NULL;
1826 ModestWindowMgr *mgr;
1827 TnyAccount *account;
1829 MsgReaderInfo *info;
1831 g_return_val_if_fail (row_reference != NULL, FALSE);
1833 mgr = modest_runtime_get_window_mgr ();
1834 already_showing = modest_window_mgr_find_registered_header (mgr, header, &msg_window);
1835 if (already_showing && (msg_window != MODEST_WINDOW (window))) {
1838 gtk_window_present (GTK_WINDOW (msg_window));
1839 g_signal_emit_by_name (G_OBJECT (window), "delete-event", NULL, &retval);
1843 /* Msg download completed */
1844 if (!(tny_header_get_flags (header) & TNY_HEADER_FLAG_CACHED)) {
1845 /* Ask the user if he wants to download the message if
1847 if (!tny_device_is_online (modest_runtime_get_device())) {
1848 GtkResponseType response;
1850 response = modest_platform_run_confirmation_dialog (GTK_WINDOW (window),
1851 _("mcen_nc_get_msg"));
1852 if (response == GTK_RESPONSE_CANCEL)
1855 folder = tny_header_get_folder (header);
1856 info = g_slice_new (MsgReaderInfo);
1857 info->header = g_object_ref (header);
1858 info->row_reference = gtk_tree_row_reference_copy (row_reference);
1860 /* Offer the connection dialog if necessary */
1861 modest_platform_connect_if_remote_and_perform ((GtkWindow *) window,
1863 TNY_FOLDER_STORE (folder),
1864 message_reader_performer,
1866 g_object_unref (folder);
1871 folder = tny_header_get_folder (header);
1872 account = tny_folder_get_account (folder);
1873 info = g_slice_new (MsgReaderInfo);
1874 info->header = g_object_ref (header);
1875 info->row_reference = gtk_tree_row_reference_copy (row_reference);
1877 message_reader_performer (FALSE, NULL, (GtkWindow *) window, account, info);
1878 g_object_unref (account);
1879 g_object_unref (folder);
1885 modest_msg_view_window_select_next_message (ModestMsgViewWindow *window)
1887 ModestMsgViewWindowPrivate *priv;
1888 GtkTreePath *path= NULL;
1889 GtkTreeIter tmp_iter;
1891 gboolean retval = TRUE;
1892 GtkTreeRowReference *row_reference = NULL;
1894 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), FALSE);
1895 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1897 if (!priv->row_reference)
1900 /* Update the next row reference if it's not valid. This could
1901 happen if for example the header which it was pointing to,
1902 was deleted. The best place to do it is in the row-deleted
1903 handler but the tinymail model do not work like the glib
1904 tree models and reports the deletion when the row is still
1906 if (!gtk_tree_row_reference_valid (priv->next_row_reference)) {
1907 if (gtk_tree_row_reference_valid (priv->row_reference)) {
1908 priv->next_row_reference = gtk_tree_row_reference_copy (priv->row_reference);
1909 select_next_valid_row (priv->header_model, &(priv->next_row_reference), FALSE, priv->is_outbox);
1912 if (priv->next_row_reference)
1913 path = gtk_tree_row_reference_get_path (priv->next_row_reference);
1917 row_reference = gtk_tree_row_reference_copy (priv->next_row_reference);
1919 gtk_tree_model_get_iter (priv->header_model,
1922 gtk_tree_path_free (path);
1924 gtk_tree_model_get (priv->header_model, &tmp_iter,
1925 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
1928 /* Read the message & show it */
1929 if (!message_reader (window, priv, header, row_reference)) {
1932 gtk_tree_row_reference_free (row_reference);
1935 g_object_unref (header);
1941 modest_msg_view_window_select_previous_message (ModestMsgViewWindow *window)
1943 ModestMsgViewWindowPrivate *priv = NULL;
1945 gboolean finished = FALSE;
1946 gboolean retval = FALSE;
1948 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), FALSE);
1949 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1951 /* Return inmediatly if there is no header model */
1952 if (!priv->header_model || !priv->row_reference)
1955 path = gtk_tree_row_reference_get_path (priv->row_reference);
1956 while (!finished && gtk_tree_path_prev (path)) {
1960 gtk_tree_model_get_iter (priv->header_model, &iter, path);
1961 gtk_tree_model_get (priv->header_model, &iter,
1962 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
1966 if (msg_is_visible (header, priv->is_outbox)) {
1967 GtkTreeRowReference *row_reference;
1968 row_reference = gtk_tree_row_reference_new (priv->header_model, path);
1969 /* Read the message & show it */
1970 retval = message_reader (window, priv, header, row_reference);
1971 gtk_tree_row_reference_free (row_reference);
1975 g_object_unref (header);
1979 gtk_tree_path_free (path);
1984 view_msg_cb (ModestMailOperation *mail_op,
1991 ModestMsgViewWindow *self = NULL;
1992 ModestMsgViewWindowPrivate *priv = NULL;
1993 GtkTreeRowReference *row_reference = NULL;
1995 /* Unregister the header (it was registered before creating the mail operation) */
1996 modest_window_mgr_unregister_header (modest_runtime_get_window_mgr (), header);
1998 row_reference = (GtkTreeRowReference *) user_data;
2000 gtk_tree_row_reference_free (row_reference);
2004 /* If there was any error */
2005 if (!modest_ui_actions_msg_retrieval_check (mail_op, header, msg)) {
2006 gtk_tree_row_reference_free (row_reference);
2010 /* Get the window */
2011 self = (ModestMsgViewWindow *) modest_mail_operation_get_source (mail_op);
2012 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self));
2013 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
2015 /* Update the row reference */
2016 if (priv->row_reference != NULL) {
2017 gtk_tree_row_reference_free (priv->row_reference);
2018 priv->row_reference = gtk_tree_row_reference_copy (row_reference);
2019 if (priv->next_row_reference != NULL) {
2020 gtk_tree_row_reference_free (priv->next_row_reference);
2022 priv->next_row_reference = gtk_tree_row_reference_copy (priv->row_reference);
2023 select_next_valid_row (priv->header_model, &(priv->next_row_reference), TRUE, priv->is_outbox);
2026 /* Mark header as read */
2027 if (!(tny_header_get_flags (header) & TNY_HEADER_FLAG_SEEN))
2028 tny_header_set_flag (header, TNY_HEADER_FLAG_SEEN);
2030 /* Set new message */
2031 if (priv->msg_view != NULL && TNY_IS_MSG_VIEW (priv->msg_view)) {
2032 tny_msg_view_set_msg (TNY_MSG_VIEW (priv->msg_view), msg);
2033 modest_msg_view_window_update_priority (self);
2034 update_window_title (MODEST_MSG_VIEW_WINDOW (self));
2035 modest_msg_view_grab_focus (MODEST_MSG_VIEW (priv->msg_view));
2038 /* Set the new message uid of the window */
2039 if (priv->msg_uid) {
2040 g_free (priv->msg_uid);
2041 priv->msg_uid = modest_tny_folder_get_header_unique_id (header);
2044 /* Notify the observers */
2045 g_signal_emit (G_OBJECT (self), signals[MSG_CHANGED_SIGNAL],
2046 0, priv->header_model, priv->row_reference);
2049 g_object_unref (self);
2050 gtk_tree_row_reference_free (row_reference);
2054 modest_msg_view_window_get_folder_type (ModestMsgViewWindow *window)
2056 ModestMsgViewWindowPrivate *priv;
2058 TnyFolderType folder_type;
2060 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
2062 folder_type = TNY_FOLDER_TYPE_UNKNOWN;
2064 msg = tny_msg_view_get_msg (TNY_MSG_VIEW (priv->msg_view));
2068 folder = tny_msg_get_folder (msg);
2070 folder_type = modest_tny_folder_guess_folder_type (folder);
2071 g_object_unref (folder);
2073 g_object_unref (msg);
2081 modest_msg_view_window_update_priority (ModestMsgViewWindow *window)
2083 ModestMsgViewWindowPrivate *priv;
2084 TnyHeader *header = NULL;
2085 TnyHeaderFlags flags = 0;
2087 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
2089 if (priv->header_model && priv->row_reference) {
2091 GtkTreePath *path = NULL;
2093 path = gtk_tree_row_reference_get_path (priv->row_reference);
2094 g_return_if_fail (path != NULL);
2095 gtk_tree_model_get_iter (priv->header_model,
2097 gtk_tree_row_reference_get_path (priv->row_reference));
2099 gtk_tree_model_get (priv->header_model, &iter, TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
2101 gtk_tree_path_free (path);
2104 msg = tny_msg_view_get_msg (TNY_MSG_VIEW (priv->msg_view));
2106 header = tny_msg_get_header (msg);
2107 g_object_unref (msg);
2112 flags = tny_header_get_flags (header);
2113 g_object_unref(G_OBJECT(header));
2116 modest_msg_view_set_priority (MODEST_MSG_VIEW(priv->msg_view), flags);
2121 toolbar_resize (ModestMsgViewWindow *self)
2123 ModestMsgViewWindowPrivate *priv = NULL;
2124 ModestWindowPrivate *parent_priv = NULL;
2126 gint static_button_size;
2127 ModestWindowMgr *mgr;
2129 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self));
2130 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
2131 parent_priv = MODEST_WINDOW_GET_PRIVATE(self);
2133 mgr = modest_runtime_get_window_mgr ();
2134 static_button_size = modest_window_mgr_get_fullscreen_mode (mgr)?118:108;
2136 if (parent_priv->toolbar) {
2137 /* left size buttons */
2138 widget = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/ToolbarMessageReply");
2139 gtk_tool_item_set_expand (GTK_TOOL_ITEM (widget), FALSE);
2140 gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (widget), FALSE);
2141 gtk_widget_set_size_request (GTK_WIDGET (widget), static_button_size, -1);
2142 widget = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/ToolbarMessageMoveTo");
2143 gtk_tool_item_set_expand (GTK_TOOL_ITEM (widget), FALSE);
2144 gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (widget), FALSE);
2145 gtk_widget_set_size_request (GTK_WIDGET (widget), static_button_size, -1);
2146 widget = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/ToolbarDeleteMessage");
2147 gtk_tool_item_set_expand (GTK_TOOL_ITEM (widget), FALSE);
2148 gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (widget), FALSE);
2149 gtk_widget_set_size_request (GTK_WIDGET (widget), static_button_size, -1);
2150 widget = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/FindInMessage");
2151 gtk_tool_item_set_expand (GTK_TOOL_ITEM (widget), FALSE);
2152 gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (widget), FALSE);
2153 gtk_widget_set_size_request (GTK_WIDGET (widget), static_button_size, -1);
2155 gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (priv->progress_toolitem), FALSE);
2156 gtk_tool_item_set_expand (GTK_TOOL_ITEM (priv->progress_toolitem), TRUE);
2157 gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (priv->cancel_toolitem), FALSE);
2158 gtk_tool_item_set_expand (GTK_TOOL_ITEM (priv->cancel_toolitem), FALSE);
2159 gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (priv->next_toolitem), TRUE);
2160 gtk_tool_item_set_expand (GTK_TOOL_ITEM (priv->next_toolitem), TRUE);
2161 gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (priv->prev_toolitem), TRUE);
2162 gtk_tool_item_set_expand (GTK_TOOL_ITEM (priv->prev_toolitem), TRUE);
2168 modest_msg_view_window_window_state_event (GtkWidget *widget, GdkEventWindowState *event, gpointer userdata)
2170 if (event->changed_mask & GDK_WINDOW_STATE_FULLSCREEN) {
2171 ModestWindowPrivate *parent_priv;
2172 ModestWindowMgr *mgr;
2173 gboolean is_fullscreen;
2174 GtkAction *fs_toggle_action;
2177 mgr = modest_runtime_get_window_mgr ();
2178 is_fullscreen = (modest_window_mgr_get_fullscreen_mode (mgr))?1:0;
2180 parent_priv = MODEST_WINDOW_GET_PRIVATE (widget);
2182 fs_toggle_action = gtk_ui_manager_get_action (parent_priv->ui_manager, "/MenuBar/ViewMenu/ViewToggleFullscreenMenu");
2183 active = (gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (fs_toggle_action)))?1:0;
2184 if (is_fullscreen != active) {
2185 gtk_toggle_action_set_active (GTK_TOGGLE_ACTION (fs_toggle_action), is_fullscreen);
2187 toolbar_resize (MODEST_MSG_VIEW_WINDOW (widget));
2195 modest_msg_view_window_show_toolbar (ModestWindow *self,
2196 gboolean show_toolbar)
2198 ModestMsgViewWindowPrivate *priv = NULL;
2199 ModestWindowPrivate *parent_priv;
2200 GtkWidget *reply_button = NULL, *menu = NULL;
2201 GtkWidget *placeholder = NULL;
2203 const gchar *action_name;
2206 parent_priv = MODEST_WINDOW_GET_PRIVATE(self);
2207 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(self);
2209 /* Set optimized view status */
2210 priv->optimized_view = !show_toolbar;
2212 if (!parent_priv->toolbar) {
2213 parent_priv->toolbar = gtk_ui_manager_get_widget (parent_priv->ui_manager,
2215 gtk_widget_set_no_show_all (parent_priv->toolbar, TRUE);
2217 priv->progress_toolitem = GTK_WIDGET (gtk_tool_item_new ());
2218 priv->cancel_toolitem = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/ToolbarCancel");
2219 priv->next_toolitem = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/ToolbarMessageNext");
2220 priv->prev_toolitem = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/ToolbarMessageBack");
2221 toolbar_resize (MODEST_MSG_VIEW_WINDOW (self));
2223 /* Add ProgressBar (Transfer toolbar) */
2224 priv->progress_bar = modest_progress_bar_new ();
2225 gtk_widget_set_no_show_all (priv->progress_bar, TRUE);
2226 placeholder = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/ProgressbarView");
2227 insert_index = gtk_toolbar_get_item_index(GTK_TOOLBAR (parent_priv->toolbar), GTK_TOOL_ITEM(placeholder));
2228 gtk_container_add (GTK_CONTAINER (priv->progress_toolitem), priv->progress_bar);
2229 gtk_toolbar_insert(GTK_TOOLBAR(parent_priv->toolbar), GTK_TOOL_ITEM (priv->progress_toolitem), insert_index);
2231 /* Connect cancel 'clicked' signal to abort progress mode */
2232 g_signal_connect(priv->cancel_toolitem, "clicked",
2233 G_CALLBACK(cancel_progressbar),
2236 /* Add it to the observers list */
2237 priv->progress_widgets = g_slist_prepend(priv->progress_widgets, priv->progress_bar);
2240 hildon_window_add_toolbar (HILDON_WINDOW (self),
2241 GTK_TOOLBAR (parent_priv->toolbar));
2243 /* Set reply button tap and hold menu */
2244 reply_button = gtk_ui_manager_get_widget (parent_priv->ui_manager,
2245 "/ToolBar/ToolbarMessageReply");
2246 menu = gtk_ui_manager_get_widget (parent_priv->ui_manager,
2247 "/ToolbarReplyCSM");
2248 gtk_widget_tap_and_hold_setup (GTK_WIDGET (reply_button), menu, NULL, 0);
2252 /* Quick hack: this prevents toolbar icons "dance" when progress bar show status is changed */
2253 /* TODO: resize mode migth be GTK_RESIZE_QUEUE, in order to avoid unneccesary shows */
2254 gtk_container_set_resize_mode (GTK_CONTAINER(parent_priv->toolbar), GTK_RESIZE_IMMEDIATE);
2256 gtk_widget_show (GTK_WIDGET (parent_priv->toolbar));
2257 if (modest_msg_view_window_transfer_mode_enabled (MODEST_MSG_VIEW_WINDOW (self)))
2258 set_toolbar_mode (MODEST_MSG_VIEW_WINDOW (self), TOOLBAR_MODE_TRANSFER);
2260 set_toolbar_mode (MODEST_MSG_VIEW_WINDOW (self), TOOLBAR_MODE_NORMAL);
2263 gtk_widget_set_no_show_all (parent_priv->toolbar, TRUE);
2264 gtk_widget_hide (GTK_WIDGET (parent_priv->toolbar));
2267 /* Update also the actions (to update the toggles in the
2268 menus), we have to do it manually because some other window
2269 of the same time could have changed it (remember that the
2270 toolbar fullscreen mode is shared by all the windows of the
2272 if (modest_window_mgr_get_fullscreen_mode (modest_runtime_get_window_mgr ()))
2273 action_name = "/MenuBar/ViewMenu/ViewShowToolbarMenu/ViewShowToolbarFullScreenMenu";
2275 action_name = "/MenuBar/ViewMenu/ViewShowToolbarMenu/ViewShowToolbarNormalScreenMenu";
2277 action = gtk_ui_manager_get_action (parent_priv->ui_manager, action_name);
2278 modest_utils_toggle_action_set_active_block_notify (GTK_TOGGLE_ACTION (action),
2283 modest_msg_view_window_clipboard_owner_change (GtkClipboard *clipboard,
2285 ModestMsgViewWindow *window)
2287 if (!GTK_WIDGET_VISIBLE (window))
2290 modest_window_check_dimming_rules_group (MODEST_WINDOW (window), MODEST_DIMMING_RULES_CLIPBOARD);
2294 modest_msg_view_window_transfer_mode_enabled (ModestMsgViewWindow *self)
2296 ModestMsgViewWindowPrivate *priv;
2298 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self), FALSE);
2299 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(self);
2301 return priv->current_toolbar_mode == TOOLBAR_MODE_TRANSFER;
2305 cancel_progressbar (GtkToolButton *toolbutton,
2306 ModestMsgViewWindow *self)
2309 ModestMsgViewWindowPrivate *priv;
2311 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(self);
2313 /* Get operation observers and cancel its current operation */
2314 tmp = priv->progress_widgets;
2316 modest_progress_object_cancel_current_operation (MODEST_PROGRESS_OBJECT(tmp->data));
2317 tmp=g_slist_next(tmp);
2321 observers_empty (ModestMsgViewWindow *self)
2324 ModestMsgViewWindowPrivate *priv;
2325 gboolean is_empty = TRUE;
2326 guint pending_ops = 0;
2328 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(self);
2329 tmp = priv->progress_widgets;
2331 /* Check all observers */
2332 while (tmp && is_empty) {
2333 pending_ops = modest_progress_object_num_pending_operations (MODEST_PROGRESS_OBJECT(tmp->data));
2334 is_empty = pending_ops == 0;
2336 tmp = g_slist_next(tmp);
2343 on_account_removed (TnyAccountStore *account_store,
2344 TnyAccount *account,
2347 /* Do nothing if it's a transport account, because we only
2348 show the messages of a store account */
2349 if (tny_account_get_account_type(account) == TNY_ACCOUNT_TYPE_STORE) {
2350 const gchar *parent_acc = NULL;
2351 const gchar *our_acc = NULL;
2353 our_acc = modest_window_get_active_account (MODEST_WINDOW (user_data));
2354 parent_acc = modest_tny_account_get_parent_modest_account_name_for_server_account (account);
2356 /* Close this window if I'm showing a message of the removed account */
2357 if (strcmp (parent_acc, our_acc) == 0)
2358 modest_ui_actions_on_close_window (NULL, MODEST_WINDOW (user_data));
2363 on_mail_operation_started (ModestMailOperation *mail_op,
2366 ModestMsgViewWindow *self;
2367 ModestMailOperationTypeOperation op_type;
2369 ModestMsgViewWindowPrivate *priv;
2370 GObject *source = NULL;
2372 self = MODEST_MSG_VIEW_WINDOW (user_data);
2373 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
2374 op_type = modest_mail_operation_get_type_operation (mail_op);
2375 tmp = priv->progress_widgets;
2376 source = modest_mail_operation_get_source(mail_op);
2377 if (G_OBJECT (self) == source) {
2378 if (op_type == MODEST_MAIL_OPERATION_TYPE_RECEIVE || op_type == MODEST_MAIL_OPERATION_TYPE_OPEN ) {
2379 set_toolbar_transfer_mode(self);
2381 modest_progress_object_add_operation (
2382 MODEST_PROGRESS_OBJECT (tmp->data),
2384 tmp = g_slist_next (tmp);
2388 g_object_unref (source);
2392 on_mail_operation_finished (ModestMailOperation *mail_op,
2395 ModestMsgViewWindow *self;
2396 ModestMailOperationTypeOperation op_type;
2398 ModestMsgViewWindowPrivate *priv;
2400 self = MODEST_MSG_VIEW_WINDOW (user_data);
2401 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
2402 op_type = modest_mail_operation_get_type_operation (mail_op);
2403 tmp = priv->progress_widgets;
2405 if (op_type == MODEST_MAIL_OPERATION_TYPE_RECEIVE || op_type == MODEST_MAIL_OPERATION_TYPE_OPEN ) {
2407 modest_progress_object_remove_operation (MODEST_PROGRESS_OBJECT (tmp->data),
2409 tmp = g_slist_next (tmp);
2412 /* If no more operations are being observed, NORMAL mode is enabled again */
2413 if (observers_empty (self)) {
2414 set_toolbar_mode (self, TOOLBAR_MODE_NORMAL);
2417 /* Update dimming rules. We have to do this right here
2418 and not in view_msg_cb because at that point the
2419 transfer mode is still enabled so the dimming rule
2420 won't let the user delete the message that has been
2421 readed for example */
2422 modest_ui_actions_check_toolbar_dimming_rules (MODEST_WINDOW (self));
2423 modest_ui_actions_check_menu_dimming_rules (MODEST_WINDOW (self));
2428 on_queue_changed (ModestMailOperationQueue *queue,
2429 ModestMailOperation *mail_op,
2430 ModestMailOperationQueueNotification type,
2431 ModestMsgViewWindow *self)
2433 ModestMsgViewWindowPrivate *priv;
2435 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
2437 /* If this operations was created by another window, do nothing */
2438 if (!modest_mail_operation_is_mine (mail_op, G_OBJECT(self)))
2441 if (type == MODEST_MAIL_OPERATION_QUEUE_OPERATION_ADDED) {
2442 priv->sighandlers = modest_signal_mgr_connect (priv->sighandlers,
2444 "operation-started",
2445 G_CALLBACK (on_mail_operation_started),
2447 priv->sighandlers = modest_signal_mgr_connect (priv->sighandlers,
2449 "operation-finished",
2450 G_CALLBACK (on_mail_operation_finished),
2452 } else if (type == MODEST_MAIL_OPERATION_QUEUE_OPERATION_REMOVED) {
2453 priv->sighandlers = modest_signal_mgr_disconnect (priv->sighandlers,
2455 "operation-started");
2456 priv->sighandlers = modest_signal_mgr_disconnect (priv->sighandlers,
2458 "operation-finished");
2463 modest_msg_view_window_get_attachments (ModestMsgViewWindow *win)
2465 ModestMsgViewWindowPrivate *priv;
2466 TnyList *selected_attachments = NULL;
2468 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (win), NULL);
2469 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (win);
2471 selected_attachments = modest_msg_view_get_selected_attachments (MODEST_MSG_VIEW (priv->msg_view));
2473 return selected_attachments;
2479 guint banner_idle_id;
2480 } DecodeAsyncHelper;
2483 decode_async_banner_idle (gpointer user_data)
2485 DecodeAsyncHelper *helper = (DecodeAsyncHelper *) user_data;
2487 helper->banner_idle_id = 0;
2488 helper->banner = hildon_banner_show_animation (NULL, NULL, _("mail_me_opening"));
2489 g_object_ref (helper->banner);
2495 on_decode_to_stream_async_handler (TnyMimePart *mime_part,
2501 DecodeAsyncHelper *helper = (DecodeAsyncHelper *) user_data;
2503 if (helper->banner_idle_id > 0) {
2504 g_source_remove (helper->banner_idle_id);
2505 helper->banner_idle_id = 0;
2507 if (helper->banner) {
2508 gtk_widget_destroy (helper->banner);
2510 if (cancelled || err) {
2511 modest_platform_information_banner (NULL, NULL,
2512 _("mail_ib_file_operation_failed"));
2516 /* make the file read-only */
2517 g_chmod(helper->filepath, 0444);
2519 /* Activate the file */
2520 modest_platform_activate_file (helper->filepath, tny_mime_part_get_content_type (mime_part));
2524 g_free (helper->filepath);
2525 g_object_unref (helper->banner);
2526 g_slice_free (DecodeAsyncHelper, helper);
2530 modest_msg_view_window_view_attachment (ModestMsgViewWindow *window, TnyMimePart *mime_part)
2532 ModestMsgViewWindowPrivate *priv;
2533 const gchar *msg_uid;
2534 gchar *attachment_uid = NULL;
2535 gint attachment_index = 0;
2536 TnyList *attachments;
2538 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window));
2539 g_return_if_fail (TNY_IS_MIME_PART (mime_part) || (mime_part == NULL));
2540 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
2542 msg_uid = modest_msg_view_window_get_message_uid (MODEST_MSG_VIEW_WINDOW (window));
2543 attachments = modest_msg_view_get_attachments (MODEST_MSG_VIEW (priv->msg_view));
2544 attachment_index = modest_list_index (attachments, (GObject *) mime_part);
2545 g_object_unref (attachments);
2547 if (msg_uid && attachment_index >= 0) {
2548 attachment_uid = g_strdup_printf ("%s/%d", msg_uid, attachment_index);
2551 if (mime_part == NULL) {
2552 gboolean error = FALSE;
2553 TnyList *selected_attachments = modest_msg_view_get_selected_attachments (MODEST_MSG_VIEW (priv->msg_view));
2554 if (selected_attachments == NULL || tny_list_get_length (selected_attachments) == 0) {
2556 } else if (tny_list_get_length (selected_attachments) > 1) {
2557 hildon_banner_show_information (NULL, NULL, _("mcen_ib_unable_to_display_more"));
2561 iter = tny_list_create_iterator (selected_attachments);
2562 mime_part = (TnyMimePart *) tny_iterator_get_current (iter);
2563 g_object_unref (iter);
2565 g_object_unref (selected_attachments);
2570 g_object_ref (mime_part);
2573 if (tny_mime_part_is_purged (mime_part)) {
2574 g_object_unref (mime_part);
2578 if (!modest_tny_mime_part_is_msg (mime_part)) {
2579 gchar *filepath = NULL;
2580 const gchar *att_filename = tny_mime_part_get_filename (mime_part);
2581 gboolean show_error_banner = FALSE;
2582 TnyFsStream *temp_stream = NULL;
2583 temp_stream = modest_utils_create_temp_stream (att_filename, attachment_uid,
2586 if (temp_stream != NULL) {
2587 DecodeAsyncHelper *helper = g_slice_new (DecodeAsyncHelper);
2588 helper->filepath = g_strdup (filepath);
2589 helper->banner = NULL;
2590 helper->banner_idle_id = g_timeout_add (1000, decode_async_banner_idle, helper);
2591 tny_mime_part_decode_to_stream_async (mime_part, TNY_STREAM (temp_stream),
2592 on_decode_to_stream_async_handler,
2595 g_object_unref (temp_stream);
2596 /* NOTE: files in the temporary area will be automatically
2597 * cleaned after some time if they are no longer in use */
2599 if (filepath != NULL) {
2600 const gchar *content_type;
2601 /* the file may already exist but it isn't writable,
2602 * let's try to open it anyway */
2603 content_type = tny_mime_part_get_content_type (mime_part);
2604 modest_platform_activate_file (filepath, content_type);
2607 g_warning ("%s: modest_utils_create_temp_stream failed", __FUNCTION__);
2608 show_error_banner = TRUE;
2611 if (show_error_banner)
2612 modest_platform_information_banner (NULL, NULL, _("mail_ib_file_operation_failed"));
2614 /* message attachment */
2615 TnyHeader *header = NULL;
2616 ModestWindowMgr *mgr;
2617 ModestWindow *msg_win = NULL;
2620 header = tny_msg_get_header (TNY_MSG (mime_part));
2621 mgr = modest_runtime_get_window_mgr ();
2622 found = modest_window_mgr_find_registered_header (mgr, header, &msg_win);
2625 if (msg_win) /* there is already a window for this uid; top it */
2626 gtk_window_present (GTK_WINDOW(msg_win));
2628 /* if it's found, but there is no msg_win, it's probably in the process of being created;
2629 * thus, we don't do anything */
2630 g_warning ("window for is already being created");
2632 /* it's not found, so create a new window for it */
2633 modest_window_mgr_register_header (mgr, header, attachment_uid); /* register the uid before building the window */
2634 gchar *account = g_strdup (modest_window_get_active_account (MODEST_WINDOW (window)));
2636 account = modest_account_mgr_get_default_account (modest_runtime_get_account_mgr ());
2637 msg_win = modest_msg_view_window_new_for_attachment (TNY_MSG (mime_part), account, attachment_uid);
2638 modest_window_set_zoom (MODEST_WINDOW (msg_win),
2639 modest_window_get_zoom (MODEST_WINDOW (window)));
2640 modest_window_mgr_register_window (mgr, msg_win);
2641 gtk_widget_show_all (GTK_WIDGET (msg_win));
2644 g_object_unref (mime_part);
2657 GnomeVFSResult result;
2660 static void save_mime_part_info_free (SaveMimePartInfo *info, gboolean with_struct);
2661 static gboolean idle_save_mime_part_show_result (SaveMimePartInfo *info);
2662 static gpointer save_mime_part_to_file (SaveMimePartInfo *info);
2663 static void save_mime_parts_to_file_with_checks (SaveMimePartInfo *info);
2666 save_mime_part_info_free (SaveMimePartInfo *info, gboolean with_struct)
2670 for (node = info->pairs; node != NULL; node = g_list_next (node)) {
2671 SaveMimePartPair *pair = (SaveMimePartPair *) node->data;
2672 g_free (pair->filename);
2673 g_object_unref (pair->part);
2674 g_slice_free (SaveMimePartPair, pair);
2676 g_list_free (info->pairs);
2679 gtk_widget_destroy (info->banner);
2680 g_slice_free (SaveMimePartInfo, info);
2685 idle_save_mime_part_show_result (SaveMimePartInfo *info)
2687 if (info->pairs != NULL) {
2688 save_mime_part_to_file (info);
2690 /* This is a GDK lock because we are an idle callback and
2691 * hildon_banner_show_information is or does Gtk+ code */
2693 gdk_threads_enter (); /* CHECKED */
2694 save_mime_part_info_free (info, TRUE);
2695 if (info->result == GNOME_VFS_OK) {
2696 hildon_banner_show_information (NULL, NULL, _CS("sfil_ib_saved"));
2697 } else if (info->result == GNOME_VFS_ERROR_NO_SPACE) {
2698 hildon_banner_show_information (NULL, NULL, dgettext("ke-recv",
2699 "cerm_device_memory_full"));
2701 hildon_banner_show_information (NULL, NULL, _("mail_ib_file_operation_failed"));
2703 gdk_threads_leave (); /* CHECKED */
2710 save_mime_part_to_file (SaveMimePartInfo *info)
2712 GnomeVFSHandle *handle;
2714 SaveMimePartPair *pair = (SaveMimePartPair *) info->pairs->data;
2716 info->result = gnome_vfs_create (&handle, pair->filename, GNOME_VFS_OPEN_WRITE, FALSE, 0644);
2717 if (info->result == GNOME_VFS_OK) {
2718 GError *error = NULL;
2719 stream = tny_vfs_stream_new (handle);
2720 if (tny_mime_part_decode_to_stream (pair->part, stream, &error) < 0) {
2721 g_warning ("modest: could not save attachment %s: %d (%s)\n", pair->filename, error?error->code:-1, error?error->message:"Unknown error");
2723 info->result = GNOME_VFS_ERROR_IO;
2725 g_object_unref (G_OBJECT (stream));
2726 g_object_unref (pair->part);
2727 g_slice_free (SaveMimePartPair, pair);
2728 info->pairs = g_list_delete_link (info->pairs, info->pairs);
2730 g_warning ("modest: could not create save attachment %s: %s\n", pair->filename, gnome_vfs_result_to_string (info->result));
2731 save_mime_part_info_free (info, FALSE);
2734 g_idle_add ((GSourceFunc) idle_save_mime_part_show_result, info);
2739 save_mime_parts_to_file_with_checks (SaveMimePartInfo *info)
2741 gboolean is_ok = TRUE;
2742 gint replaced_files = 0;
2743 const GList *files = info->pairs;
2746 for (iter = files; (iter != NULL) && (replaced_files < 2); iter = g_list_next(iter)) {
2747 SaveMimePartPair *pair = iter->data;
2748 if (modest_utils_file_exists (pair->filename)) {
2752 if (replaced_files) {
2753 GtkWidget *confirm_overwrite_dialog;
2754 const gchar *message = (replaced_files == 1) ?
2755 _FM("docm_nc_replace_file") : _FM("docm_nc_replace_multiple");
2756 confirm_overwrite_dialog = hildon_note_new_confirmation (NULL, message);
2757 if (gtk_dialog_run (GTK_DIALOG (confirm_overwrite_dialog)) != GTK_RESPONSE_OK) {
2760 gtk_widget_destroy (confirm_overwrite_dialog);
2764 save_mime_part_info_free (info, TRUE);
2766 GtkWidget *banner = hildon_banner_show_animation (NULL, NULL,
2767 _CS("sfil_ib_saving"));
2768 info->banner = banner;
2769 g_thread_create ((GThreadFunc)save_mime_part_to_file, info, FALSE, NULL);
2775 modest_msg_view_window_save_attachments (ModestMsgViewWindow *window, TnyList *mime_parts)
2777 ModestMsgViewWindowPrivate *priv;
2778 GList *files_to_save = NULL;
2779 GtkWidget *save_dialog = NULL;
2780 gchar *folder = NULL;
2781 const gchar *filename = NULL;
2782 gchar *save_multiple_str = NULL;
2784 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window));
2785 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
2787 if (mime_parts == NULL) {
2788 mime_parts = modest_msg_view_get_selected_attachments (MODEST_MSG_VIEW (priv->msg_view));
2789 if (mime_parts == NULL || tny_list_get_length (mime_parts) == 0)
2792 g_object_ref (mime_parts);
2795 /* prepare dialog */
2796 if (tny_list_get_length (mime_parts) == 1) {
2798 /* only one attachment selected */
2799 iter = tny_list_create_iterator (mime_parts);
2800 TnyMimePart *mime_part = (TnyMimePart *) tny_iterator_get_current (iter);
2801 g_object_unref (iter);
2802 if (!modest_tny_mime_part_is_msg (mime_part) &&
2803 modest_tny_mime_part_is_attachment_for_modest (mime_part) &&
2804 !tny_mime_part_is_purged (mime_part)) {
2805 filename = tny_mime_part_get_filename (mime_part);
2807 /* TODO: show any error? */
2808 g_warning ("Tried to save a non-file attachment");
2809 g_object_unref (mime_parts);
2812 g_object_unref (mime_part);
2814 save_multiple_str = g_strdup_printf (_FM("sfil_va_number_of_objects_attachments"),
2815 tny_list_get_length (mime_parts));
2818 save_dialog = hildon_file_chooser_dialog_new (GTK_WINDOW (window),
2819 GTK_FILE_CHOOSER_ACTION_SAVE);
2822 folder = g_build_filename (g_get_home_dir (), DEFAULT_FOLDER, NULL);
2823 gtk_file_chooser_set_current_folder (GTK_FILE_CHOOSER (save_dialog), folder);
2827 if (filename != NULL)
2828 gtk_file_chooser_set_current_name (GTK_FILE_CHOOSER (save_dialog),
2831 /* if multiple, set multiple string */
2832 if (save_multiple_str) {
2833 g_object_set (G_OBJECT (save_dialog), "save-multiple", save_multiple_str, NULL);
2834 gtk_window_set_title (GTK_WINDOW (save_dialog), _FM("sfil_ti_save_objects_files"));
2838 if (gtk_dialog_run (GTK_DIALOG (save_dialog)) == GTK_RESPONSE_OK) {
2839 gchar *chooser_uri = gtk_file_chooser_get_uri (GTK_FILE_CHOOSER (save_dialog));
2841 if (!modest_utils_folder_writable (chooser_uri)) {
2842 hildon_banner_show_information
2843 (NULL, NULL, dgettext("hildon-fm", "sfil_ib_readonly_location"));
2847 iter = tny_list_create_iterator (mime_parts);
2848 while (!tny_iterator_is_done (iter)) {
2849 TnyMimePart *mime_part = (TnyMimePart *) tny_iterator_get_current (iter);
2851 if ((modest_tny_mime_part_is_attachment_for_modest (mime_part)) &&
2852 !tny_mime_part_is_purged (mime_part) &&
2853 (tny_mime_part_get_filename (mime_part) != NULL)) {
2854 SaveMimePartPair *pair;
2856 pair = g_slice_new0 (SaveMimePartPair);
2857 if (save_multiple_str) {
2858 gchar *escaped = gnome_vfs_escape_slashes (
2859 tny_mime_part_get_filename (mime_part));
2860 pair->filename = g_build_filename (chooser_uri, escaped, NULL);
2863 pair->filename = g_strdup (chooser_uri);
2865 pair->part = mime_part;
2866 files_to_save = g_list_prepend (files_to_save, pair);
2868 tny_iterator_next (iter);
2870 g_object_unref (iter);
2872 g_free (chooser_uri);
2875 gtk_widget_destroy (save_dialog);
2877 g_object_unref (mime_parts);
2879 if (files_to_save != NULL) {
2880 SaveMimePartInfo *info = g_slice_new0 (SaveMimePartInfo);
2881 info->pairs = files_to_save;
2882 info->result = TRUE;
2883 save_mime_parts_to_file_with_checks (info);
2888 show_remove_attachment_information (gpointer userdata)
2890 ModestMsgViewWindow *window = (ModestMsgViewWindow *) userdata;
2891 ModestMsgViewWindowPrivate *priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
2893 /* We're outside the main lock */
2894 gdk_threads_enter ();
2896 if (priv->remove_attachment_banner != NULL) {
2897 gtk_widget_destroy (priv->remove_attachment_banner);
2898 g_object_unref (priv->remove_attachment_banner);
2901 priv->remove_attachment_banner = g_object_ref (
2902 hildon_banner_show_animation (NULL, NULL, _("mcen_ib_removing_attachment")));
2904 gdk_threads_leave ();
2910 modest_msg_view_window_remove_attachments (ModestMsgViewWindow *window, gboolean get_all)
2912 ModestMsgViewWindowPrivate *priv;
2913 TnyList *mime_parts = NULL;
2914 gchar *confirmation_message;
2920 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window));
2921 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
2924 mime_parts = modest_msg_view_get_attachments (MODEST_MSG_VIEW (priv->msg_view));
2926 mime_parts = modest_msg_view_get_selected_attachments (MODEST_MSG_VIEW (priv->msg_view));
2928 /* Remove already purged messages from mime parts list */
2929 iter = tny_list_create_iterator (mime_parts);
2930 while (!tny_iterator_is_done (iter)) {
2931 TnyMimePart *part = TNY_MIME_PART (tny_iterator_get_current (iter));
2932 tny_iterator_next (iter);
2933 if (tny_mime_part_is_purged (part)) {
2934 tny_list_remove (mime_parts, (GObject *) part);
2936 g_object_unref (part);
2938 g_object_unref (iter);
2940 if (tny_list_get_length (mime_parts) == 0) {
2941 g_object_unref (mime_parts);
2945 n_attachments = tny_list_get_length (mime_parts);
2946 if (n_attachments == 1) {
2950 iter = tny_list_create_iterator (mime_parts);
2951 part = (TnyMimePart *) tny_iterator_get_current (iter);
2952 g_object_unref (iter);
2953 if (modest_tny_mime_part_is_msg (part)) {
2955 header = tny_msg_get_header (TNY_MSG (part));
2956 filename = tny_header_dup_subject (header);
2957 g_object_unref (header);
2958 if (filename == NULL)
2959 filename = g_strdup (_("mail_va_no_subject"));
2961 filename = g_strdup (tny_mime_part_get_filename (TNY_MIME_PART (part)));
2963 confirmation_message = g_strdup_printf (_("mcen_nc_purge_file_text"), filename);
2965 g_object_unref (part);
2967 confirmation_message = g_strdup_printf (ngettext("mcen_nc_purge_file_text",
2968 "mcen_nc_purge_files_text",
2969 n_attachments), n_attachments);
2971 response = modest_platform_run_confirmation_dialog (GTK_WINDOW (window),
2972 confirmation_message);
2973 g_free (confirmation_message);
2975 if (response != GTK_RESPONSE_OK) {
2976 g_object_unref (mime_parts);
2980 priv->purge_timeout = g_timeout_add (2000, show_remove_attachment_information, window);
2982 iter = tny_list_create_iterator (mime_parts);
2983 while (!tny_iterator_is_done (iter)) {
2986 part = (TnyMimePart *) tny_iterator_get_current (iter);
2987 tny_mime_part_set_purged (TNY_MIME_PART (part));
2988 g_object_unref (part);
2989 tny_iterator_next (iter);
2991 g_object_unref (iter);
2993 msg = tny_msg_view_get_msg (TNY_MSG_VIEW (priv->msg_view));
2994 tny_msg_view_clear (TNY_MSG_VIEW (priv->msg_view));
2995 tny_msg_rewrite_cache (msg);
2996 tny_msg_view_set_msg (TNY_MSG_VIEW (priv->msg_view), msg);
2997 g_object_unref (msg);
2999 g_object_unref (mime_parts);
3001 if (priv->purge_timeout > 0) {
3002 g_source_remove (priv->purge_timeout);
3003 priv->purge_timeout = 0;
3006 if (priv->remove_attachment_banner) {
3007 gtk_widget_destroy (priv->remove_attachment_banner);
3008 g_object_unref (priv->remove_attachment_banner);
3009 priv->remove_attachment_banner = NULL;
3017 update_window_title (ModestMsgViewWindow *window)
3019 ModestMsgViewWindowPrivate *priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
3021 TnyHeader *header = NULL;
3022 gchar *subject = NULL;
3024 msg = tny_msg_view_get_msg (TNY_MSG_VIEW (priv->msg_view));
3027 header = tny_msg_get_header (msg);
3028 subject = tny_header_dup_subject (header);
3029 g_object_unref (header);
3030 g_object_unref (msg);
3033 if ((subject == NULL)||(subject[0] == '\0')) {
3035 subject = g_strdup (_("mail_va_no_subject"));
3038 gtk_window_set_title (GTK_WINDOW (window), subject);
3042 static void on_move_focus (GtkWidget *widget,
3043 GtkDirectionType direction,
3046 g_signal_stop_emission_by_name (G_OBJECT (widget), "move-focus");