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-maemo-utils.h>
39 #include <modest-tny-msg.h>
40 #include <modest-msg-view-window.h>
41 #include <modest-attachments-view.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-widget.h"
51 #include "modest-defs.h"
52 #include "modest-hildon-includes.h"
53 #include <gtkhtml/gtkhtml-search.h>
54 #include "modest-ui-dimming-manager.h"
55 #include <gdk/gdkkeysyms.h>
56 #include <modest-tny-account.h>
59 #define DEFAULT_FOLDER "MyDocs/.documents"
61 static void modest_msg_view_window_class_init (ModestMsgViewWindowClass *klass);
62 static void modest_msg_view_window_init (ModestMsgViewWindow *obj);
63 static void modest_header_view_observer_init(
64 ModestHeaderViewObserverIface *iface_class);
65 static void modest_msg_view_window_finalize (GObject *obj);
66 static void modest_msg_view_window_toggle_find_toolbar (GtkToggleAction *obj,
68 static void modest_msg_view_window_find_toolbar_close (GtkWidget *widget,
69 ModestMsgViewWindow *obj);
70 static void modest_msg_view_window_find_toolbar_search (GtkWidget *widget,
71 ModestMsgViewWindow *obj);
73 static void modest_msg_view_window_disconnect_signals (ModestWindow *self);
74 static void modest_msg_view_window_set_zoom (ModestWindow *window,
76 static gdouble modest_msg_view_window_get_zoom (ModestWindow *window);
77 static gboolean modest_msg_view_window_zoom_minus (ModestWindow *window);
78 static gboolean modest_msg_view_window_zoom_plus (ModestWindow *window);
79 static gboolean modest_msg_view_window_key_release_event (GtkWidget *window,
82 static gboolean modest_msg_view_window_window_state_event (GtkWidget *widget,
83 GdkEventWindowState *event,
85 static void modest_msg_view_window_scroll_up (ModestWindow *window);
86 static void modest_msg_view_window_scroll_down (ModestWindow *window);
87 static void modest_msg_view_window_update_priority (ModestMsgViewWindow *window);
89 static void modest_msg_view_window_show_toolbar (ModestWindow *window,
90 gboolean show_toolbar);
92 static void modest_msg_view_window_clipboard_owner_change (GtkClipboard *clipboard,
94 ModestMsgViewWindow *window);
95 void modest_msg_view_window_on_row_changed(
96 GtkTreeModel *header_model,
99 ModestMsgViewWindow *window);
101 void modest_msg_view_window_on_row_deleted(
102 GtkTreeModel *header_model,
104 ModestMsgViewWindow *window);
106 void modest_msg_view_window_on_row_inserted(
107 GtkTreeModel *header_model,
108 GtkTreePath *tree_path,
109 GtkTreeIter *tree_iter,
110 ModestMsgViewWindow *window);
112 void modest_msg_view_window_on_row_reordered(
113 GtkTreeModel *header_model,
117 ModestMsgViewWindow *window);
119 void modest_msg_view_window_update_model_replaced(
120 ModestHeaderViewObserver *window,
122 const gchar *tny_folder_id);
124 static void cancel_progressbar (GtkToolButton *toolbutton,
125 ModestMsgViewWindow *self);
127 static void on_queue_changed (ModestMailOperationQueue *queue,
128 ModestMailOperation *mail_op,
129 ModestMailOperationQueueNotification type,
130 ModestMsgViewWindow *self);
132 static void on_account_removed (TnyAccountStore *account_store,
136 static void on_move_focus (ModestMsgViewWindow *window,
137 GtkDirectionType direction,
140 static void view_msg_cb (ModestMailOperation *mail_op,
145 static void set_toolbar_mode (ModestMsgViewWindow *self,
146 ModestToolBarModes mode);
148 static void update_window_title (ModestMsgViewWindow *window);
150 static gboolean set_toolbar_transfer_mode (ModestMsgViewWindow *self);
153 /* list my signals */
159 static const GtkToggleActionEntry msg_view_toggle_action_entries [] = {
160 { "FindInMessage", MODEST_TOOLBAR_ICON_FIND, N_("qgn_toolb_gene_find"), NULL, NULL, G_CALLBACK (modest_msg_view_window_toggle_find_toolbar), FALSE },
161 { "ToolsFindInMessage", NULL, N_("mcen_me_viewer_find"), NULL, NULL, G_CALLBACK (modest_msg_view_window_toggle_find_toolbar), FALSE },
164 static const GtkRadioActionEntry msg_view_zoom_action_entries [] = {
165 { "Zoom50", NULL, N_("mcen_me_viewer_50"), NULL, NULL, 50 },
166 { "Zoom80", NULL, N_("mcen_me_viewer_80"), NULL, NULL, 80 },
167 { "Zoom100", NULL, N_("mcen_me_viewer_100"), NULL, NULL, 100 },
168 { "Zoom120", NULL, N_("mcen_me_viewer_120"), NULL, NULL, 120 },
169 { "Zoom150", NULL, N_("mcen_me_viewer_150"), NULL, NULL, 150 },
170 { "Zoom200", NULL, N_("mcen_me_viewer_200"), NULL, NULL, 200 }
173 typedef struct _ModestMsgViewWindowPrivate ModestMsgViewWindowPrivate;
174 struct _ModestMsgViewWindowPrivate {
177 GtkWidget *main_scroll;
178 GtkWidget *find_toolbar;
181 /* Progress observers */
182 GtkWidget *progress_bar;
183 GSList *progress_widgets;
186 GtkWidget *progress_toolitem;
187 GtkWidget *cancel_toolitem;
188 GtkWidget *prev_toolitem;
189 GtkWidget *next_toolitem;
190 ModestToolBarModes current_toolbar_mode;
192 /* Optimized view enabled */
193 gboolean optimized_view;
195 /* Whether this was created via the *_new_for_search_result() function. */
196 gboolean is_search_result;
198 /* A reference to the @model of the header view
199 * to allow selecting previous/next messages,
200 * if the message is currently selected in the header view.
202 const gchar *header_folder_id;
203 GtkTreeModel *header_model;
204 GtkTreeRowReference *row_reference;
205 GtkTreeRowReference *next_row_reference;
207 gulong clipboard_change_handler;
208 gulong queue_change_handler;
209 gulong account_removed_handler;
210 gulong row_changed_handler;
211 gulong row_deleted_handler;
212 gulong row_inserted_handler;
213 gulong rows_reordered_handler;
216 GtkWidget *remove_attachment_banner;
218 guint progress_bar_timeout;
223 #define MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE((o), \
224 MODEST_TYPE_MSG_VIEW_WINDOW, \
225 ModestMsgViewWindowPrivate))
227 static GtkWindowClass *parent_class = NULL;
229 /* uncomment the following if you have defined any signals */
230 static guint signals[LAST_SIGNAL] = {0};
233 modest_msg_view_window_get_type (void)
235 static GType my_type = 0;
237 static const GTypeInfo my_info = {
238 sizeof(ModestMsgViewWindowClass),
239 NULL, /* base init */
240 NULL, /* base finalize */
241 (GClassInitFunc) modest_msg_view_window_class_init,
242 NULL, /* class finalize */
243 NULL, /* class data */
244 sizeof(ModestMsgViewWindow),
246 (GInstanceInitFunc) modest_msg_view_window_init,
249 my_type = g_type_register_static (MODEST_TYPE_WINDOW,
250 "ModestMsgViewWindow",
253 static const GInterfaceInfo modest_header_view_observer_info =
255 (GInterfaceInitFunc) modest_header_view_observer_init,
256 NULL, /* interface_finalize */
257 NULL /* interface_data */
260 g_type_add_interface_static (my_type,
261 MODEST_TYPE_HEADER_VIEW_OBSERVER,
262 &modest_header_view_observer_info);
268 save_state (ModestWindow *self)
270 modest_widget_memory_save (modest_runtime_get_conf (),
272 MODEST_CONF_MSG_VIEW_WINDOW_KEY);
277 restore_settings (ModestMsgViewWindow *self)
279 modest_widget_memory_restore (modest_runtime_get_conf (),
281 MODEST_CONF_MSG_VIEW_WINDOW_KEY);
285 modest_msg_view_window_class_init (ModestMsgViewWindowClass *klass)
287 GObjectClass *gobject_class;
288 ModestWindowClass *modest_window_class;
289 gobject_class = (GObjectClass*) klass;
290 modest_window_class = (ModestWindowClass *) klass;
292 parent_class = g_type_class_peek_parent (klass);
293 gobject_class->finalize = modest_msg_view_window_finalize;
295 modest_window_class->set_zoom_func = modest_msg_view_window_set_zoom;
296 modest_window_class->get_zoom_func = modest_msg_view_window_get_zoom;
297 modest_window_class->zoom_minus_func = modest_msg_view_window_zoom_minus;
298 modest_window_class->zoom_plus_func = modest_msg_view_window_zoom_plus;
299 modest_window_class->show_toolbar_func = modest_msg_view_window_show_toolbar;
300 modest_window_class->disconnect_signals_func = modest_msg_view_window_disconnect_signals;
302 g_type_class_add_private (gobject_class, sizeof(ModestMsgViewWindowPrivate));
304 modest_window_class->save_state_func = save_state;
306 signals[MSG_CHANGED_SIGNAL] =
307 g_signal_new ("msg-changed",
308 G_TYPE_FROM_CLASS (gobject_class),
310 G_STRUCT_OFFSET (ModestMsgViewWindowClass, msg_changed),
312 modest_marshal_VOID__POINTER_POINTER,
313 G_TYPE_NONE, 2, G_TYPE_POINTER, G_TYPE_POINTER);
316 static void modest_header_view_observer_init(
317 ModestHeaderViewObserverIface *iface_class)
319 iface_class->update_func = modest_msg_view_window_update_model_replaced;
323 modest_msg_view_window_init (ModestMsgViewWindow *obj)
325 ModestMsgViewWindowPrivate *priv;
326 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(obj);
328 priv->is_search_result = FALSE;
330 priv->msg_view = NULL;
331 priv->header_model = NULL;
332 priv->header_folder_id = NULL;
333 priv->clipboard_change_handler = 0;
334 priv->queue_change_handler = 0;
335 priv->account_removed_handler = 0;
336 priv->row_changed_handler = 0;
337 priv->row_deleted_handler = 0;
338 priv->row_inserted_handler = 0;
339 priv->rows_reordered_handler = 0;
340 priv->current_toolbar_mode = TOOLBAR_MODE_NORMAL;
342 priv->optimized_view = FALSE;
343 priv->progress_bar_timeout = 0;
344 priv->purge_timeout = 0;
345 priv->remove_attachment_banner = NULL;
346 priv->msg_uid = NULL;
351 set_toolbar_transfer_mode (ModestMsgViewWindow *self)
353 ModestMsgViewWindowPrivate *priv = NULL;
355 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self), FALSE);
357 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(self);
359 set_toolbar_mode (self, TOOLBAR_MODE_TRANSFER);
361 if (priv->progress_bar_timeout > 0) {
362 g_source_remove (priv->progress_bar_timeout);
363 priv->progress_bar_timeout = 0;
370 set_toolbar_mode (ModestMsgViewWindow *self,
371 ModestToolBarModes mode)
373 ModestWindowPrivate *parent_priv;
374 ModestMsgViewWindowPrivate *priv;
375 /* GtkWidget *widget = NULL; */
377 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self));
379 parent_priv = MODEST_WINDOW_GET_PRIVATE(self);
380 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(self);
382 /* Sets current toolbar mode */
383 priv->current_toolbar_mode = mode;
385 /* Update toolbar dimming state */
386 modest_ui_actions_check_toolbar_dimming_rules (MODEST_WINDOW (self));
389 case TOOLBAR_MODE_NORMAL:
390 /* widget = gtk_ui_manager_get_action (parent_priv->ui_manager, "/ToolBar/ToolbarMessageReply"); */
391 /* gtk_action_set_sensitive (widget, TRUE); */
392 /* widget = gtk_ui_manager_get_action (parent_priv->ui_manager, "/ToolBar/ToolbarDeleteMessage"); */
393 /* gtk_action_set_sensitive (widget, TRUE); */
394 /* widget = gtk_ui_manager_get_action (parent_priv->ui_manager, "/ToolBar/ToolbarMessageMoveTo"); */
395 /* gtk_action_set_sensitive (widget, TRUE); */
396 /* widget = gtk_ui_manager_get_action (parent_priv->ui_manager, "/ToolBar/FindInMessage"); */
397 /* gtk_action_set_sensitive (widget, TRUE); */
399 if (priv->prev_toolitem)
400 gtk_widget_show (priv->prev_toolitem);
402 if (priv->next_toolitem)
403 gtk_widget_show (priv->next_toolitem);
405 if (priv->progress_toolitem)
406 gtk_tool_item_set_expand (GTK_TOOL_ITEM (priv->progress_toolitem), FALSE);
407 if (priv->progress_bar)
408 gtk_widget_hide (priv->progress_bar);
410 if (priv->cancel_toolitem)
411 gtk_widget_hide (priv->cancel_toolitem);
413 /* Hide toolbar if optimized view is enabled */
414 if (priv->optimized_view) {
415 gtk_widget_set_no_show_all (parent_priv->toolbar, TRUE);
416 gtk_widget_hide (GTK_WIDGET(parent_priv->toolbar));
420 case TOOLBAR_MODE_TRANSFER:
421 /* widget = gtk_ui_manager_get_action (parent_priv->ui_manager, "/ToolBar/ToolbarMessageReply"); */
422 /* gtk_action_set_sensitive (widget, FALSE); */
423 /* widget = gtk_ui_manager_get_action (parent_priv->ui_manager, "/ToolBar/ToolbarDeleteMessage"); */
424 /* gtk_action_set_sensitive (widget, FALSE); */
425 /* widget = gtk_ui_manager_get_action (parent_priv->ui_manager, "/ToolBar/ToolbarMessageMoveTo"); */
426 /* gtk_action_set_sensitive (widget, FALSE); */
427 /* widget = gtk_ui_manager_get_action (parent_priv->ui_manager, "/ToolBar/FindInMessage"); */
428 /* gtk_action_set_sensitive (widget, FALSE); */
430 if (priv->prev_toolitem)
431 gtk_widget_hide (priv->prev_toolitem);
433 if (priv->next_toolitem)
434 gtk_widget_hide (priv->next_toolitem);
436 if (priv->progress_toolitem)
437 gtk_tool_item_set_expand (GTK_TOOL_ITEM (priv->progress_toolitem), TRUE);
438 if (priv->progress_bar)
439 gtk_widget_show (priv->progress_bar);
441 if (priv->cancel_toolitem)
442 gtk_widget_show (priv->cancel_toolitem);
444 /* Show toolbar if it's hiden (optimized view ) */
445 if (priv->optimized_view) {
446 gtk_widget_set_no_show_all (parent_priv->toolbar, FALSE);
447 gtk_widget_show (GTK_WIDGET (parent_priv->toolbar));
452 g_return_if_reached ();
459 menubar_to_menu (GtkUIManager *ui_manager)
461 GtkWidget *main_menu;
463 GList *iter, *children;
465 /* Create new main menu */
466 main_menu = gtk_menu_new();
468 /* Get the menubar from the UI manager */
469 menubar = gtk_ui_manager_get_widget (ui_manager, "/MenuBar");
471 iter = children = gtk_container_get_children (GTK_CONTAINER (menubar));
475 menu = GTK_WIDGET (iter->data);
476 gtk_widget_reparent(menu, main_menu);
478 iter = g_list_next (iter);
480 g_list_free (children);
486 init_window (ModestMsgViewWindow *obj, TnyMsg *msg)
488 GtkWidget *main_vbox;
489 ModestMsgViewWindowPrivate *priv;
490 ModestWindowPrivate *parent_priv;
492 GtkAction *action = NULL;
494 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(obj);
495 parent_priv = MODEST_WINDOW_GET_PRIVATE(obj);
497 priv->msg_view = modest_msg_view_new (msg);
498 modest_msg_view_set_shadow_type (MODEST_MSG_VIEW (priv->msg_view), GTK_SHADOW_NONE);
499 main_vbox = gtk_vbox_new (FALSE, 6);
502 parent_priv->menubar = menubar_to_menu (parent_priv->ui_manager);
503 conf = modest_runtime_get_conf ();
504 action = gtk_ui_manager_get_action (parent_priv->ui_manager,
505 "/MenuBar/ViewMenu/ViewShowToolbarMenu/ViewShowToolbarNormalScreenMenu");
506 gtk_toggle_action_set_active (GTK_TOGGLE_ACTION (action),
507 modest_conf_get_bool (conf, MODEST_CONF_MSG_VIEW_WINDOW_SHOW_TOOLBAR, NULL));
508 action = gtk_ui_manager_get_action (parent_priv->ui_manager,
509 "/MenuBar/ViewMenu/ViewShowToolbarMenu/ViewShowToolbarFullScreenMenu");
510 gtk_toggle_action_set_active (GTK_TOGGLE_ACTION (action),
511 modest_conf_get_bool (conf, MODEST_CONF_MSG_VIEW_WINDOW_SHOW_TOOLBAR_FULLSCREEN, NULL));
512 hildon_window_set_menu (HILDON_WINDOW(obj), GTK_MENU(parent_priv->menubar));
513 gtk_widget_show (GTK_WIDGET(parent_priv->menubar));
515 priv->main_scroll = gtk_scrolled_window_new (NULL, NULL);
516 gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (priv->main_scroll), GTK_POLICY_NEVER, GTK_POLICY_AUTOMATIC);
517 gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW (priv->main_scroll), GTK_SHADOW_NONE);
518 modest_maemo_set_thumbable_scrollbar (GTK_SCROLLED_WINDOW(priv->main_scroll), TRUE);
520 gtk_container_add (GTK_CONTAINER (priv->main_scroll), priv->msg_view);
521 gtk_box_pack_start (GTK_BOX(main_vbox), priv->main_scroll, TRUE, TRUE, 0);
522 gtk_container_add (GTK_CONTAINER(obj), main_vbox);
524 priv->find_toolbar = hildon_find_toolbar_new (NULL);
525 hildon_window_add_toolbar (HILDON_WINDOW (obj), GTK_TOOLBAR (priv->find_toolbar));
526 gtk_widget_set_no_show_all (priv->find_toolbar, TRUE);
527 g_signal_connect (G_OBJECT (priv->find_toolbar), "close", G_CALLBACK (modest_msg_view_window_find_toolbar_close), obj);
528 g_signal_connect (G_OBJECT (priv->find_toolbar), "search", G_CALLBACK (modest_msg_view_window_find_toolbar_search), obj);
530 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);
531 gtk_widget_show_all (GTK_WIDGET(main_vbox));
535 modest_msg_view_window_disconnect_signals (ModestWindow *self)
537 ModestMsgViewWindowPrivate *priv;
538 ModestHeaderView *header_view = NULL;
539 ModestMainWindow *main_window = NULL;
540 ModestWindowMgr *window_mgr = NULL;
542 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
544 if (gtk_clipboard_get (GDK_SELECTION_PRIMARY) &&
545 g_signal_handler_is_connected (gtk_clipboard_get (GDK_SELECTION_PRIMARY),
546 priv->clipboard_change_handler))
547 g_signal_handler_disconnect (gtk_clipboard_get (GDK_SELECTION_PRIMARY),
548 priv->clipboard_change_handler);
550 if (g_signal_handler_is_connected (G_OBJECT (modest_runtime_get_mail_operation_queue ()),
551 priv->queue_change_handler))
552 g_signal_handler_disconnect (G_OBJECT (modest_runtime_get_mail_operation_queue ()),
553 priv->queue_change_handler);
555 if (g_signal_handler_is_connected (G_OBJECT (modest_runtime_get_account_store ()),
556 priv->account_removed_handler))
557 g_signal_handler_disconnect (G_OBJECT (modest_runtime_get_account_store ()),
558 priv->account_removed_handler);
560 if (priv->header_model) {
561 if (g_signal_handler_is_connected(G_OBJECT (priv->header_model),
562 priv->row_changed_handler))
563 g_signal_handler_disconnect(G_OBJECT (priv->header_model),
564 priv->row_changed_handler);
566 if (g_signal_handler_is_connected(G_OBJECT (priv->header_model),
567 priv->row_deleted_handler))
568 g_signal_handler_disconnect(G_OBJECT (priv->header_model),
569 priv->row_deleted_handler);
571 if (g_signal_handler_is_connected(G_OBJECT (priv->header_model),
572 priv->row_inserted_handler))
573 g_signal_handler_disconnect(G_OBJECT (priv->header_model),
574 priv->row_inserted_handler);
576 if (g_signal_handler_is_connected(G_OBJECT (priv->header_model),
577 priv->rows_reordered_handler))
578 g_signal_handler_disconnect(G_OBJECT (priv->header_model),
579 priv->rows_reordered_handler);
582 window_mgr = modest_runtime_get_window_mgr();
583 g_assert(window_mgr != NULL);
585 main_window = MODEST_MAIN_WINDOW(
586 modest_window_mgr_get_main_window(window_mgr));
588 if(main_window == NULL)
591 header_view = MODEST_HEADER_VIEW(
592 modest_main_window_get_child_widget(
593 main_window, MODEST_MAIN_WINDOW_WIDGET_TYPE_HEADER_VIEW));
595 if (header_view == NULL)
598 modest_header_view_remove_observer(header_view,
599 MODEST_HEADER_VIEW_OBSERVER(self));
603 modest_msg_view_window_finalize (GObject *obj)
605 ModestMsgViewWindowPrivate *priv;
607 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (obj);
609 /* Sanity check: shouldn't be needed, the window mgr should
610 call this function before */
611 modest_msg_view_window_disconnect_signals (MODEST_WINDOW (obj));
613 if (priv->header_model != NULL) {
614 g_object_unref (priv->header_model);
615 priv->header_model = NULL;
618 if (priv->progress_bar_timeout > 0) {
619 g_source_remove (priv->progress_bar_timeout);
620 priv->progress_bar_timeout = 0;
623 if (priv->remove_attachment_banner) {
624 gtk_widget_destroy (priv->remove_attachment_banner);
625 g_object_unref (priv->remove_attachment_banner);
626 priv->remove_attachment_banner = NULL;
629 if (priv->purge_timeout > 0) {
630 g_source_remove (priv->purge_timeout);
631 priv->purge_timeout = 0;
634 if (priv->row_reference) {
635 gtk_tree_row_reference_free (priv->row_reference);
636 priv->row_reference = NULL;
639 if (priv->next_row_reference) {
640 gtk_tree_row_reference_free (priv->next_row_reference);
641 priv->next_row_reference = NULL;
645 g_free (priv->msg_uid);
646 priv->msg_uid = NULL;
649 G_OBJECT_CLASS(parent_class)->finalize (obj);
653 select_next_valid_row (GtkTreeModel *model,
654 GtkTreeRowReference **row_reference,
657 GtkTreeIter tmp_iter;
658 GtkTreePath *path, *next;
659 gboolean retval = FALSE;
661 g_return_val_if_fail (gtk_tree_row_reference_valid (*row_reference), FALSE);
663 path = gtk_tree_row_reference_get_path (*row_reference);
664 gtk_tree_model_get_iter (model, &tmp_iter, path);
665 gtk_tree_row_reference_free (*row_reference);
666 *row_reference = NULL;
668 if (gtk_tree_model_iter_next (model, &tmp_iter)) {
669 next = gtk_tree_model_get_path (model, &tmp_iter);
670 *row_reference = gtk_tree_row_reference_new (model, next);
672 } else if (cycle && gtk_tree_model_get_iter_first (model, &tmp_iter)) {
673 next = gtk_tree_model_get_path (model, &tmp_iter);
675 /* Ensure that we are not selecting the same */
676 if (gtk_tree_path_compare (path, next) != 0) {
677 *row_reference = gtk_tree_row_reference_new (model, next);
683 gtk_tree_path_free (path);
688 /* TODO: This should be in _init(), with the parameters as properties. */
690 modest_msg_view_window_construct (ModestMsgViewWindow *self, TnyMsg *msg,
691 const gchar *modest_account_name,
692 const gchar *msg_uid)
695 ModestMsgViewWindowPrivate *priv = NULL;
696 ModestWindowPrivate *parent_priv = NULL;
697 ModestDimmingRulesGroup *menu_rules_group = NULL;
698 ModestDimmingRulesGroup *toolbar_rules_group = NULL;
699 ModestDimmingRulesGroup *clipboard_rules_group = NULL;
700 GtkActionGroup *action_group = NULL;
701 GError *error = NULL;
702 GdkPixbuf *window_icon;
704 g_return_if_fail (msg);
706 obj = G_OBJECT (self);
707 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(obj);
708 parent_priv = MODEST_WINDOW_GET_PRIVATE(obj);
710 priv->msg_uid = g_strdup (msg_uid);
712 parent_priv->ui_manager = gtk_ui_manager_new();
713 parent_priv->ui_dimming_manager = modest_ui_dimming_manager_new();
715 action_group = gtk_action_group_new ("ModestMsgViewWindowActions");
716 gtk_action_group_set_translation_domain (action_group, GETTEXT_PACKAGE);
718 menu_rules_group = modest_dimming_rules_group_new ("ModestMenuDimmingRules", FALSE);
719 toolbar_rules_group = modest_dimming_rules_group_new ("ModestToolbarDimmingRules", TRUE);
720 clipboard_rules_group = modest_dimming_rules_group_new ("ModestClipboardDimmingRules", FALSE);
722 /* Add common actions */
723 gtk_action_group_add_actions (action_group,
724 modest_action_entries,
725 G_N_ELEMENTS (modest_action_entries),
727 gtk_action_group_add_toggle_actions (action_group,
728 modest_toggle_action_entries,
729 G_N_ELEMENTS (modest_toggle_action_entries),
731 gtk_action_group_add_toggle_actions (action_group,
732 msg_view_toggle_action_entries,
733 G_N_ELEMENTS (msg_view_toggle_action_entries),
735 gtk_action_group_add_radio_actions (action_group,
736 msg_view_zoom_action_entries,
737 G_N_ELEMENTS (msg_view_zoom_action_entries),
739 G_CALLBACK (modest_ui_actions_on_change_zoom),
742 gtk_ui_manager_insert_action_group (parent_priv->ui_manager, action_group, 0);
743 g_object_unref (action_group);
745 /* Load the UI definition */
746 gtk_ui_manager_add_ui_from_file (parent_priv->ui_manager, MODEST_UIDIR "modest-msg-view-window-ui.xml",
749 g_printerr ("modest: could not merge modest-msg-view-window-ui.xml: %s\n", error->message);
750 g_error_free (error);
755 /* Add common dimming rules */
756 modest_dimming_rules_group_add_rules (menu_rules_group,
757 modest_msg_view_menu_dimming_entries,
758 G_N_ELEMENTS (modest_msg_view_menu_dimming_entries),
759 MODEST_WINDOW (self));
760 modest_dimming_rules_group_add_rules (toolbar_rules_group,
761 modest_msg_view_toolbar_dimming_entries,
762 G_N_ELEMENTS (modest_msg_view_toolbar_dimming_entries),
763 MODEST_WINDOW (self));
764 modest_dimming_rules_group_add_rules (clipboard_rules_group,
765 modest_msg_view_clipboard_dimming_entries,
766 G_N_ELEMENTS (modest_msg_view_clipboard_dimming_entries),
767 MODEST_WINDOW (self));
769 /* Insert dimming rules group for this window */
770 modest_ui_dimming_manager_insert_rules_group (parent_priv->ui_dimming_manager, menu_rules_group);
771 modest_ui_dimming_manager_insert_rules_group (parent_priv->ui_dimming_manager, toolbar_rules_group);
772 modest_ui_dimming_manager_insert_rules_group (parent_priv->ui_dimming_manager, clipboard_rules_group);
773 g_object_unref (menu_rules_group);
774 g_object_unref (toolbar_rules_group);
775 g_object_unref (clipboard_rules_group);
777 /* Add accelerators */
778 gtk_window_add_accel_group (GTK_WINDOW (obj),
779 gtk_ui_manager_get_accel_group (parent_priv->ui_manager));
782 init_window (MODEST_MSG_VIEW_WINDOW(obj), msg);
783 restore_settings (MODEST_MSG_VIEW_WINDOW(obj));
785 /* Set window icon */
786 window_icon = modest_platform_get_icon (MODEST_APP_MSG_VIEW_ICON);
788 gtk_window_set_icon (GTK_WINDOW (obj), window_icon);
789 g_object_unref (window_icon);
792 /* g_signal_connect (G_OBJECT(obj), "delete-event", G_CALLBACK(on_delete_event), obj); */
794 g_signal_connect (G_OBJECT(priv->msg_view), "link_clicked",
795 G_CALLBACK (modest_ui_actions_on_msg_link_clicked), obj);
796 g_signal_connect (G_OBJECT(priv->msg_view), "link_hover",
797 G_CALLBACK (modest_ui_actions_on_msg_link_hover), obj);
798 g_signal_connect (G_OBJECT(priv->msg_view), "attachment_clicked",
799 G_CALLBACK (modest_ui_actions_on_msg_attachment_clicked), obj);
800 g_signal_connect (G_OBJECT(priv->msg_view), "recpt_activated",
801 G_CALLBACK (modest_ui_actions_on_msg_recpt_activated), obj);
802 g_signal_connect (G_OBJECT(priv->msg_view), "link_contextual",
803 G_CALLBACK (modest_ui_actions_on_msg_link_contextual), obj);
805 g_signal_connect (G_OBJECT (obj), "key-release-event",
806 G_CALLBACK (modest_msg_view_window_key_release_event),
809 g_signal_connect (G_OBJECT (obj), "window-state-event",
810 G_CALLBACK (modest_msg_view_window_window_state_event),
813 g_signal_connect (G_OBJECT (obj), "move-focus",
814 G_CALLBACK (on_move_focus), obj);
816 /* Mail Operation Queue */
817 priv->queue_change_handler = g_signal_connect (G_OBJECT (modest_runtime_get_mail_operation_queue ()),
819 G_CALLBACK (on_queue_changed),
822 /* Account manager */
823 priv->account_removed_handler = g_signal_connect (G_OBJECT (modest_runtime_get_account_store ()),
825 G_CALLBACK(on_account_removed),
828 modest_window_set_active_account (MODEST_WINDOW(obj), modest_account_name);
830 priv->last_search = NULL;
832 /* Init the clipboard actions dim status */
833 modest_msg_view_grab_focus(MODEST_MSG_VIEW (priv->msg_view));
835 update_window_title (MODEST_MSG_VIEW_WINDOW (obj));
837 /* Check toolbar dimming rules */
838 modest_ui_actions_check_toolbar_dimming_rules (MODEST_WINDOW (obj));
839 modest_window_check_dimming_rules_group (MODEST_WINDOW (obj), "ModestClipboardDimmingRules");
844 modest_msg_view_window_new_with_header_model (TnyMsg *msg,
845 const gchar *modest_account_name,
846 const gchar *msg_uid,
848 GtkTreeRowReference *row_reference)
850 ModestMsgViewWindow *window = NULL;
851 ModestMsgViewWindowPrivate *priv = NULL;
852 TnyFolder *header_folder = NULL;
853 ModestHeaderView *header_view = NULL;
854 ModestMainWindow *main_window = NULL;
855 ModestWindowMgr *window_mgr = NULL;
857 window = g_object_new(MODEST_TYPE_MSG_VIEW_WINDOW, NULL);
858 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), NULL);
859 modest_msg_view_window_construct (window, msg, modest_account_name, msg_uid);
861 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
863 /* Remember the message list's TreeModel so we can detect changes
864 * and change the list selection when necessary: */
865 window_mgr = modest_runtime_get_window_mgr();
866 g_assert(window_mgr != NULL);
867 main_window = MODEST_MAIN_WINDOW(
868 modest_window_mgr_get_main_window(window_mgr));
869 g_assert(main_window != NULL);
870 header_view = MODEST_HEADER_VIEW(modest_main_window_get_child_widget(
871 main_window, MODEST_MAIN_WINDOW_WIDGET_TYPE_HEADER_VIEW));
872 if (header_view != NULL){
873 header_folder = modest_header_view_get_folder(header_view);
874 g_assert(header_folder != NULL);
875 priv->header_folder_id = tny_folder_get_id(header_folder);
876 g_assert(priv->header_folder_id != NULL);
877 g_object_unref(header_folder);
880 priv->header_model = g_object_ref(model);
881 priv->row_reference = gtk_tree_row_reference_copy (row_reference);
882 priv->next_row_reference = gtk_tree_row_reference_copy (row_reference);
883 select_next_valid_row (model, &(priv->next_row_reference), TRUE);
885 priv->row_changed_handler = g_signal_connect(
886 GTK_TREE_MODEL(model), "row-changed",
887 G_CALLBACK(modest_msg_view_window_on_row_changed),
889 priv->row_deleted_handler = g_signal_connect(
890 GTK_TREE_MODEL(model), "row-deleted",
891 G_CALLBACK(modest_msg_view_window_on_row_deleted),
893 priv->row_inserted_handler = g_signal_connect (
894 GTK_TREE_MODEL(model), "row-inserted",
895 G_CALLBACK(modest_msg_view_window_on_row_inserted),
897 priv->rows_reordered_handler = g_signal_connect(
898 GTK_TREE_MODEL(model), "rows-reordered",
899 G_CALLBACK(modest_msg_view_window_on_row_reordered),
902 if (header_view != NULL){
903 modest_header_view_add_observer(header_view,
904 MODEST_HEADER_VIEW_OBSERVER(window));
907 modest_msg_view_window_update_priority (window);
909 /* Check toolbar dimming rules */
910 modest_ui_actions_check_toolbar_dimming_rules (MODEST_WINDOW (window));
912 return MODEST_WINDOW(window);
916 modest_msg_view_window_new_for_search_result (TnyMsg *msg,
917 const gchar *modest_account_name,
918 const gchar *msg_uid)
920 ModestMsgViewWindow *window = NULL;
921 ModestMsgViewWindowPrivate *priv = NULL;
923 window = g_object_new(MODEST_TYPE_MSG_VIEW_WINDOW, NULL);
924 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), NULL);
925 modest_msg_view_window_construct (window, msg, modest_account_name, msg_uid);
927 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
929 /* Remember that this is a search result,
930 * so we can disable some UI appropriately: */
931 priv->is_search_result = TRUE;
933 return MODEST_WINDOW(window);
937 modest_msg_view_window_new_for_attachment (TnyMsg *msg,
938 const gchar *modest_account_name,
939 const gchar *msg_uid)
942 g_return_val_if_fail (msg, NULL);
944 obj = g_object_new(MODEST_TYPE_MSG_VIEW_WINDOW, NULL);
945 modest_msg_view_window_construct (MODEST_MSG_VIEW_WINDOW (obj),
946 msg, modest_account_name, msg_uid);
948 return MODEST_WINDOW(obj);
951 void modest_msg_view_window_on_row_changed(
952 GtkTreeModel *header_model,
955 ModestMsgViewWindow *window){
956 modest_ui_actions_check_toolbar_dimming_rules (MODEST_WINDOW (window));
959 void modest_msg_view_window_on_row_deleted(
960 GtkTreeModel *header_model,
962 ModestMsgViewWindow *window){
963 modest_ui_actions_check_toolbar_dimming_rules (MODEST_WINDOW (window));
966 /* On insertions we check if the folder still has the message we are
967 * showing or do not. If do not, we do nothing. Which means we are still
968 * not attached to any header folder and thus next/prev buttons are
969 * still dimmed. Once the message that is shown by msg-view is found, the
970 * new model of header-view will be attached and the references will be set.
971 * On each further insertions dimming rules will be checked. However
972 * this requires extra CPU time at least works.
973 * (An message might be deleted from TnyFolder and thus will not be
974 * inserted into the model again for example if it is removed by the
975 * imap server and the header view is refreshed.)
977 void modest_msg_view_window_on_row_inserted(
978 GtkTreeModel *new_model,
979 GtkTreePath *tree_path,
980 GtkTreeIter *tree_iter,
981 ModestMsgViewWindow *window){
982 ModestMsgViewWindowPrivate *priv = NULL;
983 TnyHeader *header = NULL;
986 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window));
988 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
990 /* If we already has a model attached then the message shown by
991 * msg-view is in it, and thus we do not need any actions but
992 * to check the dimming rules.*/
993 if(priv->header_model != NULL){
994 gtk_tree_row_reference_free(priv->next_row_reference);
995 priv->next_row_reference = gtk_tree_row_reference_copy(
996 priv->row_reference);
997 select_next_valid_row (priv->header_model,
998 &(priv->next_row_reference), FALSE);
999 modest_ui_actions_check_toolbar_dimming_rules (
1000 MODEST_WINDOW (window));
1004 /* Check if the newly inserted message is the same we are actually
1005 * showing. IF not, we should remain detached from the header model
1006 * and thus prev and next toolbarbuttons should remain dimmed. */
1007 gtk_tree_model_get (new_model, tree_iter,
1008 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN, &header, -1);
1009 uid = modest_tny_folder_get_header_unique_id(header);
1010 if(!g_str_equal(priv->msg_uid, uid)){
1016 /* Setup row_reference for the actual msg. */
1017 priv->row_reference = gtk_tree_row_reference_new(
1018 new_model, tree_path);
1019 if(priv->row_reference == NULL){
1020 g_warning("No reference for msg header item.");
1024 /* Attach new_model and connect some callback to it to become able
1025 * to detect changes in header-view. */
1026 priv->header_model = g_object_ref(new_model);
1027 g_signal_connect (new_model, "row-changed",
1028 G_CALLBACK (modest_msg_view_window_on_row_changed),
1030 g_signal_connect (new_model, "row-deleted",
1031 G_CALLBACK (modest_msg_view_window_on_row_deleted),
1033 g_signal_connect (new_model, "rows-reordered",
1034 G_CALLBACK (modest_msg_view_window_on_row_reordered),
1037 /* Now set up next_row_reference. */
1038 priv->next_row_reference = gtk_tree_row_reference_copy(
1039 priv->row_reference);
1040 select_next_valid_row (priv->header_model,
1041 &(priv->next_row_reference), FALSE);
1043 modest_ui_actions_check_toolbar_dimming_rules (MODEST_WINDOW (window));
1046 void modest_msg_view_window_on_row_reordered(
1047 GtkTreeModel *header_model,
1051 ModestMsgViewWindow *window){
1052 modest_ui_actions_check_toolbar_dimming_rules (MODEST_WINDOW (window));
1055 /* The modest_msg_view_window_update_model_replaced implements update
1056 * function for ModestHeaderViewObserver. Checks whether the TnyFolder
1057 * actually belongs to the header-view is the same as the TnyFolder of
1058 * the message of msg-view or not. If they are different, there is
1059 * nothing to do. If they are the same, then the model has replaced and
1060 * the reference in msg-view shall be replaced from the old model to
1061 * the new model. In this case the view will be detached from it's
1062 * header folder. From this point the next/prev buttons are dimmed.
1064 void modest_msg_view_window_update_model_replaced(
1065 ModestHeaderViewObserver *observer,
1066 GtkTreeModel *model,
1067 const gchar *tny_folder_id){
1068 ModestMsgViewWindowPrivate *priv = NULL;
1069 ModestMsgViewWindow *window = NULL;
1071 g_assert(MODEST_IS_HEADER_VIEW_OBSERVER(observer));
1072 g_assert(MODEST_IS_MSG_VIEW_WINDOW(observer));
1074 window = MODEST_MSG_VIEW_WINDOW(observer);
1075 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(window);
1077 /* If there is an other folder in the header-view then we do
1078 * not care about it's model (msg list). Else if the
1079 * header-view shows the folder the msg shown by us is in, we
1080 * shall replace our model reference and make some check. */
1081 if(tny_folder_id == NULL ||
1082 !g_str_equal(tny_folder_id, priv->header_folder_id))
1085 /* Model is changed(replaced), so we should forget the old
1086 * one. Because there might be other references and there
1087 * might be some change on the model even if we unreferenced
1088 * it, we need to disconnect our signals here. */
1089 if (g_signal_handler_is_connected(G_OBJECT (priv->header_model),
1090 priv->row_changed_handler))
1091 g_signal_handler_disconnect(G_OBJECT (priv->header_model),
1092 priv->row_changed_handler);
1093 priv->row_changed_handler = 0;
1094 if (g_signal_handler_is_connected(G_OBJECT (priv->header_model),
1095 priv->row_deleted_handler))
1096 g_signal_handler_disconnect(G_OBJECT (priv->header_model),
1097 priv->row_deleted_handler);
1098 priv->row_deleted_handler = 0;
1099 if (g_signal_handler_is_connected(G_OBJECT (priv->header_model),
1100 priv->row_inserted_handler))
1101 g_signal_handler_disconnect(G_OBJECT (priv->header_model),
1102 priv->row_inserted_handler);
1103 priv->row_inserted_handler = 0;
1104 if (g_signal_handler_is_connected(G_OBJECT (priv->header_model),
1105 priv->rows_reordered_handler))
1106 g_signal_handler_disconnect(G_OBJECT (priv->header_model),
1107 priv->rows_reordered_handler);
1108 priv->rows_reordered_handler = 0;
1109 g_object_unref(priv->header_model);
1110 priv->header_model = NULL;
1111 g_object_unref(priv->row_reference);
1112 priv->row_reference = NULL;
1113 g_object_unref(priv->next_row_reference);
1114 priv->next_row_reference = NULL;
1116 modest_ui_actions_check_toolbar_dimming_rules(MODEST_WINDOW(window));
1118 if(tny_folder_id == NULL)
1121 g_assert(model != NULL);
1123 /* Also we must connect to the new model for row insertions.
1124 * Only for insertions now. We will need other ones only after
1125 * the msg is show by msg-view is added to the new model. */
1126 priv->row_inserted_handler = g_signal_connect (
1127 model, "row-inserted",
1128 G_CALLBACK(modest_msg_view_window_on_row_inserted),
1133 modest_msg_view_window_toolbar_on_transfer_mode (ModestMsgViewWindow *self)
1135 ModestMsgViewWindowPrivate *priv= NULL;
1137 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self), FALSE);
1138 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
1140 return priv->current_toolbar_mode == TOOLBAR_MODE_TRANSFER;
1144 modest_msg_view_window_get_header (ModestMsgViewWindow *self)
1146 ModestMsgViewWindowPrivate *priv= NULL;
1148 TnyHeader *header = NULL;
1149 GtkTreePath *path = NULL;
1152 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self), NULL);
1153 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
1155 /* If the message was not obtained from a treemodel,
1156 * for instance if it was opened directly by the search UI:
1158 if (priv->header_model == NULL) {
1159 msg = modest_msg_view_window_get_message (self);
1160 header = tny_msg_get_header (msg);
1161 g_object_unref (msg);
1165 /* Get iter of the currently selected message in the header view: */
1166 /* TODO: Why not just give this window a ref of the TnyHeader or TnyMessage,
1167 * instead of sometimes retrieving it from the header view?
1168 * Then we wouldn't be dependent on the message actually still being selected
1169 * in the header view. murrayc. */
1170 path = gtk_tree_row_reference_get_path (priv->row_reference);
1171 g_return_val_if_fail (path != NULL, NULL);
1172 gtk_tree_model_get_iter (priv->header_model,
1176 /* Get current message header */
1177 gtk_tree_model_get (priv->header_model, &iter,
1178 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
1181 gtk_tree_path_free (path);
1186 modest_msg_view_window_get_message (ModestMsgViewWindow *self)
1188 ModestMsgView *msg_view;
1189 ModestMsgViewWindowPrivate *priv;
1191 g_return_val_if_fail (self, NULL);
1193 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(self);
1195 msg_view = MODEST_MSG_VIEW (priv->msg_view);
1197 return modest_msg_view_get_message (msg_view);
1201 modest_msg_view_window_get_message_uid (ModestMsgViewWindow *self)
1203 ModestMsgViewWindowPrivate *priv;
1205 g_return_val_if_fail (self, NULL);
1207 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
1209 return (const gchar*) priv->msg_uid;
1213 modest_msg_view_window_toggle_find_toolbar (GtkToggleAction *toggle,
1216 ModestMsgViewWindow *window = MODEST_MSG_VIEW_WINDOW (data);
1217 ModestMsgViewWindowPrivate *priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1218 ModestWindowPrivate *parent_priv = MODEST_WINDOW_GET_PRIVATE (window);
1222 is_active = gtk_toggle_action_get_active (toggle);
1225 gtk_widget_show (priv->find_toolbar);
1226 hildon_find_toolbar_highlight_entry (HILDON_FIND_TOOLBAR (priv->find_toolbar), TRUE);
1228 gtk_widget_hide (priv->find_toolbar);
1231 /* update the toggle buttons status */
1232 action = gtk_ui_manager_get_action (parent_priv->ui_manager, "/ToolBar/FindInMessage");
1233 modest_maemo_toggle_action_set_active_block_notify (GTK_TOGGLE_ACTION (action), is_active);
1234 action = gtk_ui_manager_get_action (parent_priv->ui_manager, "/MenuBar/ToolsMenu/ToolsFindInMessageMenu");
1235 modest_maemo_toggle_action_set_active_block_notify (GTK_TOGGLE_ACTION (action), is_active);
1240 modest_msg_view_window_find_toolbar_close (GtkWidget *widget,
1241 ModestMsgViewWindow *obj)
1243 GtkToggleAction *toggle;
1244 ModestWindowPrivate *parent_priv;
1245 parent_priv = MODEST_WINDOW_GET_PRIVATE (obj);
1247 toggle = GTK_TOGGLE_ACTION (gtk_ui_manager_get_action (parent_priv->ui_manager, "/ToolBar/FindInMessage"));
1248 gtk_toggle_action_set_active (toggle, FALSE);
1252 modest_msg_view_window_find_toolbar_search (GtkWidget *widget,
1253 ModestMsgViewWindow *obj)
1255 gchar *current_search;
1256 ModestMsgViewWindowPrivate *priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (obj);
1258 if (modest_msg_view_get_message_is_empty (MODEST_MSG_VIEW (priv->msg_view))) {
1259 hildon_banner_show_information (NULL, NULL, _("mail_ib_nothing_to_find"));
1263 g_object_get (G_OBJECT (widget), "prefix", ¤t_search, NULL);
1265 if ((current_search == NULL) || (strcmp (current_search, "") == 0)) {
1266 g_free (current_search);
1267 hildon_banner_show_information (NULL, NULL, dgettext("hildon-common-strings", "ecdg_ib_find_rep_enter_text"));
1271 if ((priv->last_search == NULL) || (strcmp (priv->last_search, current_search) != 0)) {
1273 g_free (priv->last_search);
1274 priv->last_search = g_strdup (current_search);
1275 result = modest_msg_view_search (MODEST_MSG_VIEW (priv->msg_view),
1278 hildon_banner_show_information (NULL, NULL, dgettext("hildon-libs", "ckct_ib_find_no_matches"));
1279 g_free (priv->last_search);
1280 priv->last_search = NULL;
1283 if (!modest_msg_view_search_next (MODEST_MSG_VIEW (priv->msg_view))) {
1284 hildon_banner_show_information (NULL, NULL, dgettext("hildon-libs", "ckct_ib_find_search_complete"));
1285 g_free (priv->last_search);
1286 priv->last_search = NULL;
1290 g_free (current_search);
1295 modest_msg_view_window_set_zoom (ModestWindow *window,
1298 ModestMsgViewWindowPrivate *priv;
1299 ModestWindowPrivate *parent_priv;
1300 GtkAction *action = NULL;
1301 gint int_zoom = (gint) rint (zoom*100.0+0.1);
1303 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window));
1305 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1306 parent_priv = MODEST_WINDOW_GET_PRIVATE (window);
1307 modest_msg_view_set_zoom (MODEST_MSG_VIEW (priv->msg_view), zoom);
1309 action = gtk_ui_manager_get_action (parent_priv->ui_manager,
1310 "/MenuBar/ViewMenu/ZoomMenu/Zoom50Menu");
1312 gtk_radio_action_set_current_value (GTK_RADIO_ACTION (action), int_zoom);
1316 modest_msg_view_window_get_zoom (ModestWindow *window)
1318 ModestMsgViewWindowPrivate *priv;
1320 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), 1.0);
1322 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1323 return modest_msg_view_get_zoom (MODEST_MSG_VIEW (priv->msg_view));
1327 modest_msg_view_window_zoom_plus (ModestWindow *window)
1329 ModestWindowPrivate *parent_priv;
1330 GtkRadioAction *zoom_radio_action;
1331 GSList *group, *node;
1333 parent_priv = MODEST_WINDOW_GET_PRIVATE (window);
1334 zoom_radio_action = GTK_RADIO_ACTION (gtk_ui_manager_get_action (parent_priv->ui_manager,
1335 "/MenuBar/ViewMenu/ZoomMenu/Zoom50Menu"));
1337 group = gtk_radio_action_get_group (zoom_radio_action);
1339 if (gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (group->data))) {
1340 hildon_banner_show_information (NULL, NULL, dgettext("hildon-common-strings", "ckct_ib_max_zoom_level_reached"));
1344 for (node = group; node != NULL; node = g_slist_next (node)) {
1345 if ((node->next != NULL) && gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (node->next->data))) {
1346 gtk_toggle_action_set_active (GTK_TOGGLE_ACTION (node->data), TRUE);
1354 modest_msg_view_window_zoom_minus (ModestWindow *window)
1356 ModestWindowPrivate *parent_priv;
1357 GtkRadioAction *zoom_radio_action;
1358 GSList *group, *node;
1360 parent_priv = MODEST_WINDOW_GET_PRIVATE (window);
1361 zoom_radio_action = GTK_RADIO_ACTION (gtk_ui_manager_get_action (parent_priv->ui_manager,
1362 "/MenuBar/ViewMenu/ZoomMenu/Zoom50Menu"));
1364 group = gtk_radio_action_get_group (zoom_radio_action);
1366 for (node = group; node != NULL; node = g_slist_next (node)) {
1367 if (gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (node->data))) {
1368 if (node->next != NULL) {
1369 gtk_toggle_action_set_active (GTK_TOGGLE_ACTION (node->next->data), TRUE);
1372 hildon_banner_show_information (NULL, NULL, dgettext("hildon-common-strings", "ckct_ib_min_zoom_level_reached"));
1382 modest_msg_view_window_key_release_event (GtkWidget *window,
1386 if (event->type == GDK_KEY_RELEASE) {
1387 switch (event->keyval) {
1389 modest_msg_view_window_scroll_up (MODEST_WINDOW (window));
1393 modest_msg_view_window_scroll_down (MODEST_WINDOW (window));
1406 modest_msg_view_window_scroll_up (ModestWindow *window)
1408 ModestMsgViewWindowPrivate *priv;
1409 gboolean return_value;
1411 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1412 g_signal_emit_by_name (G_OBJECT (priv->main_scroll), "scroll-child", GTK_SCROLL_STEP_UP, FALSE, &return_value);
1416 modest_msg_view_window_scroll_down (ModestWindow *window)
1418 ModestMsgViewWindowPrivate *priv;
1419 gboolean return_value;
1421 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1422 g_signal_emit_by_name (G_OBJECT (priv->main_scroll), "scroll-child", GTK_SCROLL_STEP_DOWN, FALSE, &return_value);
1426 modest_msg_view_window_last_message_selected (ModestMsgViewWindow *window)
1429 ModestMsgViewWindowPrivate *priv;
1430 GtkTreeIter tmp_iter;
1431 gboolean is_last_selected;
1433 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), TRUE);
1434 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1436 /*if no model (so no rows at all), then virtually we are the last*/
1437 if (!priv->header_model)
1440 path = gtk_tree_row_reference_get_path (priv->row_reference);
1444 is_last_selected = TRUE;
1445 while (is_last_selected) {
1447 gtk_tree_path_next (path);
1448 if (!gtk_tree_model_get_iter (priv->header_model, &tmp_iter, path))
1450 gtk_tree_model_get (priv->header_model, &tmp_iter,
1451 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
1453 if (!(tny_header_get_flags(header) & TNY_HEADER_FLAG_DELETED))
1454 is_last_selected = FALSE;
1456 gtk_tree_path_free (path);
1457 return is_last_selected;
1461 modest_msg_view_window_has_headers_model (ModestMsgViewWindow *window)
1463 ModestMsgViewWindowPrivate *priv;
1465 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), TRUE);
1466 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1468 return priv->header_model != NULL;
1472 modest_msg_view_window_is_search_result (ModestMsgViewWindow *window)
1474 ModestMsgViewWindowPrivate *priv;
1476 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), TRUE);
1477 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1479 return priv->is_search_result;
1483 modest_msg_view_window_first_message_selected (ModestMsgViewWindow *window)
1486 ModestMsgViewWindowPrivate *priv;
1487 gboolean is_first_selected;
1488 GtkTreeIter tmp_iter;
1489 /* gchar * path_string;*/
1491 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), TRUE);
1492 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1494 /*if no model (so no rows at all), then virtually we are the first*/
1495 if (!priv->header_model)
1498 path = gtk_tree_row_reference_get_path (priv->row_reference);
1502 /* path_string = gtk_tree_path_to_string (path);
1503 is_first_selected = strcmp (path_string, "0");
1505 g_free (path_string);
1506 gtk_tree_path_free (path);
1508 return is_first_selected;*/
1510 is_first_selected = TRUE;
1511 while (is_first_selected) {
1513 if(!gtk_tree_path_prev (path))
1515 /* Here the 'if' is needless for logic, but let make sure
1516 * iter is valid for gtk_tree_model_get. */
1517 if (!gtk_tree_model_get_iter (priv->header_model, &tmp_iter, path))
1519 gtk_tree_model_get (priv->header_model, &tmp_iter,
1520 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
1522 if (!(tny_header_get_flags(header) & TNY_HEADER_FLAG_DELETED))
1523 is_first_selected = FALSE;
1525 gtk_tree_path_free (path);
1526 return is_first_selected;
1530 * Reads the message whose summary item is @header. It takes care of
1531 * several things, among others:
1533 * If the message was not previously downloaded then ask the user
1534 * before downloading. If there is no connection launch the connection
1535 * dialog. Update toolbar dimming rules.
1537 * Returns: TRUE if the mail operation was started, otherwise if the
1538 * user do not want to download the message, or if the user do not
1539 * want to connect, then the operation is not issued
1542 message_reader (ModestMsgViewWindow *window,
1543 ModestMsgViewWindowPrivate *priv,
1547 ModestMailOperation *mail_op = NULL;
1548 ModestMailOperationTypeOperation op_type;
1549 gboolean already_showing = FALSE;
1550 ModestWindow *msg_window = NULL;
1551 ModestWindowMgr *mgr;
1553 g_return_val_if_fail (path != NULL, FALSE);
1555 mgr = modest_runtime_get_window_mgr ();
1556 already_showing = modest_window_mgr_find_registered_header (mgr, header, &msg_window);
1557 if (already_showing && (msg_window != MODEST_WINDOW (window))) {
1560 gtk_window_present (GTK_WINDOW (msg_window));
1561 g_signal_emit_by_name (G_OBJECT (window), "delete-event", NULL, &retval);
1565 /* Msg download completed */
1566 if (tny_header_get_flags (header) & TNY_HEADER_FLAG_CACHED) {
1567 op_type = MODEST_MAIL_OPERATION_TYPE_OPEN;
1569 op_type = MODEST_MAIL_OPERATION_TYPE_RECEIVE;
1571 /* Ask the user if he wants to download the message if
1573 if (!tny_device_is_online (modest_runtime_get_device())) {
1574 TnyFolder *folder = NULL;
1575 GtkResponseType response;
1577 response = modest_platform_run_confirmation_dialog (GTK_WINDOW (window),
1578 _("mcen_nc_get_msg"));
1579 if (response == GTK_RESPONSE_CANCEL)
1582 /* Offer the connection dialog if necessary */
1583 folder = tny_header_get_folder (header);
1585 if (!modest_platform_connect_and_wait_if_network_folderstore (NULL,
1586 TNY_FOLDER_STORE (folder))) {
1587 g_object_unref (folder);
1590 g_object_unref (folder);
1595 /* New mail operation */
1596 mail_op = modest_mail_operation_new_with_error_handling (op_type,
1598 modest_ui_actions_get_msgs_full_error_handler,
1601 modest_mail_operation_queue_add (modest_runtime_get_mail_operation_queue (), mail_op);
1602 modest_mail_operation_get_msg (mail_op, header, view_msg_cb, path);
1603 g_object_unref (mail_op);
1605 /* Update toolbar dimming rules */
1606 modest_ui_actions_check_toolbar_dimming_rules (MODEST_WINDOW (window));
1612 modest_msg_view_window_select_next_message (ModestMsgViewWindow *window)
1614 ModestMsgViewWindowPrivate *priv;
1615 GtkTreePath *path= NULL;
1616 GtkTreeIter tmp_iter;
1618 gboolean retval = TRUE;
1620 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), FALSE);
1621 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1623 /* Update the next row reference if it's not valid. This could
1624 happen if for example the header which it was pointing to,
1625 was deleted. The best place to do it is in the row-deleted
1626 handler but the tinymail model do not work like the glib
1627 tree models and reports the deletion when the row is still
1629 if (!gtk_tree_row_reference_valid (priv->next_row_reference)) {
1630 if (gtk_tree_row_reference_valid (priv->row_reference)) {
1631 priv->next_row_reference = gtk_tree_row_reference_copy (priv->row_reference);
1632 select_next_valid_row (priv->header_model, &(priv->next_row_reference), FALSE);
1635 if (priv->next_row_reference)
1636 path = gtk_tree_row_reference_get_path (priv->next_row_reference);
1640 gtk_tree_model_get_iter (priv->header_model,
1644 gtk_tree_model_get (priv->header_model, &tmp_iter,
1645 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
1648 /* Read the message & show it */
1649 if (!message_reader (window, priv, header, path)) {
1651 gtk_tree_path_free (path);
1655 g_object_unref (header);
1661 modest_msg_view_window_select_first_message (ModestMsgViewWindow *self)
1663 ModestMsgViewWindowPrivate *priv = NULL;
1664 TnyHeader *header = NULL;
1668 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self), FALSE);
1669 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
1671 /* Check that the model is not empty */
1672 if (!gtk_tree_model_get_iter_first (priv->header_model, &iter))
1675 /* Get the header */
1676 gtk_tree_model_get (priv->header_model,
1678 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
1680 g_return_val_if_fail (TNY_IS_HEADER (header), FALSE);
1681 if (tny_header_get_flags (header) & TNY_HEADER_FLAG_DELETED) {
1682 g_object_unref (header);
1683 return modest_msg_view_window_select_next_message (self);
1686 path = gtk_tree_model_get_path (priv->header_model, &iter);
1688 /* Read the message & show it */
1689 message_reader (self, priv, header, path);
1692 g_object_unref (header);
1698 modest_msg_view_window_select_previous_message (ModestMsgViewWindow *window)
1700 ModestMsgViewWindowPrivate *priv = NULL;
1703 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), FALSE);
1704 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1706 /* Return inmediatly if there is no header model */
1707 if (!priv->header_model)
1710 path = gtk_tree_row_reference_get_path (priv->row_reference);
1711 while (gtk_tree_path_prev (path)) {
1715 gtk_tree_model_get_iter (priv->header_model, &iter, path);
1716 gtk_tree_model_get (priv->header_model, &iter,
1717 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
1721 if (tny_header_get_flags (header) & TNY_HEADER_FLAG_DELETED) {
1722 g_object_unref (header);
1726 /* Read the message & show it */
1727 if (!message_reader (window, priv, header, path)) {
1728 g_object_unref (header);
1732 g_object_unref (header);
1737 gtk_tree_path_free (path);
1742 view_msg_cb (ModestMailOperation *mail_op,
1747 ModestMsgViewWindow *self = NULL;
1748 ModestMsgViewWindowPrivate *priv = NULL;
1751 /* If there was any error */
1752 path = (GtkTreePath *) user_data;
1753 if (!modest_ui_actions_msg_retrieval_check (mail_op, header, msg)) {
1754 gtk_tree_path_free (path);
1758 /* Get the window */
1759 self = (ModestMsgViewWindow *) modest_mail_operation_get_source (mail_op);
1760 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self));
1761 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
1763 /* Update the row reference */
1764 gtk_tree_row_reference_free (priv->row_reference);
1765 priv->row_reference = gtk_tree_row_reference_new (priv->header_model, path);
1766 priv->next_row_reference = gtk_tree_row_reference_copy (priv->row_reference);
1767 select_next_valid_row (priv->header_model, &(priv->next_row_reference), TRUE);
1768 gtk_tree_path_free (path);
1770 /* Mark header as read */
1771 if (!(tny_header_get_flags (header) & TNY_HEADER_FLAG_SEEN))
1772 tny_header_set_flags (header, TNY_HEADER_FLAG_SEEN);
1774 /* Set new message */
1775 modest_msg_view_set_message (MODEST_MSG_VIEW (priv->msg_view), msg);
1776 modest_msg_view_window_update_priority (self);
1777 update_window_title (MODEST_MSG_VIEW_WINDOW (self));
1778 modest_msg_view_grab_focus (MODEST_MSG_VIEW (priv->msg_view));
1780 /* Set the new message uid of the window */
1781 if (priv->msg_uid) {
1782 g_free (priv->msg_uid);
1783 priv->msg_uid = modest_tny_folder_get_header_unique_id (header);
1786 /* Notify the observers */
1787 g_signal_emit (G_OBJECT (self), signals[MSG_CHANGED_SIGNAL],
1788 0, priv->header_model, priv->row_reference);
1790 /* Free new references */
1791 g_object_unref (self);
1795 modest_msg_view_window_get_folder_type (ModestMsgViewWindow *window)
1797 ModestMsgViewWindowPrivate *priv;
1799 TnyFolderType folder_type;
1801 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1803 folder_type = TNY_FOLDER_TYPE_UNKNOWN;
1805 msg = modest_msg_view_get_message (MODEST_MSG_VIEW (priv->msg_view));
1809 folder = tny_msg_get_folder (msg);
1812 folder_type = tny_folder_get_folder_type (folder);
1814 if (folder_type == TNY_FOLDER_TYPE_NORMAL || folder_type == TNY_FOLDER_TYPE_UNKNOWN) {
1815 const gchar *fname = tny_folder_get_name (folder);
1816 folder_type = modest_tny_folder_guess_folder_type_from_name (fname);
1819 g_object_unref (folder);
1821 g_object_unref (msg);
1829 modest_msg_view_window_update_priority (ModestMsgViewWindow *window)
1831 ModestMsgViewWindowPrivate *priv;
1832 TnyHeaderFlags flags = 0;
1834 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1836 if (priv->header_model) {
1839 GtkTreePath *path = NULL;
1841 path = gtk_tree_row_reference_get_path (priv->row_reference);
1842 g_return_if_fail (path != NULL);
1843 gtk_tree_model_get_iter (priv->header_model,
1845 gtk_tree_row_reference_get_path (priv->row_reference));
1847 gtk_tree_model_get (priv->header_model, &iter, TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
1849 flags = tny_header_get_flags (header);
1850 gtk_tree_path_free (path);
1853 modest_msg_view_set_priority (MODEST_MSG_VIEW(priv->msg_view), flags);
1858 modest_msg_view_window_window_state_event (GtkWidget *widget, GdkEventWindowState *event, gpointer userdata)
1860 if (event->changed_mask & GDK_WINDOW_STATE_FULLSCREEN) {
1861 ModestWindowPrivate *parent_priv;
1862 ModestWindowMgr *mgr;
1863 gboolean is_fullscreen;
1864 GtkAction *fs_toggle_action;
1867 mgr = modest_runtime_get_window_mgr ();
1868 is_fullscreen = (modest_window_mgr_get_fullscreen_mode (mgr))?1:0;
1870 parent_priv = MODEST_WINDOW_GET_PRIVATE (widget);
1872 fs_toggle_action = gtk_ui_manager_get_action (parent_priv->ui_manager, "/MenuBar/ViewMenu/ViewToggleFullscreenMenu");
1873 active = (gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (fs_toggle_action)))?1:0;
1874 if (is_fullscreen != active) {
1875 gtk_toggle_action_set_active (GTK_TOGGLE_ACTION (fs_toggle_action), is_fullscreen);
1884 set_homogeneous (GtkWidget *widget,
1887 if (GTK_IS_TOOL_ITEM (widget)) {
1888 gtk_tool_item_set_expand (GTK_TOOL_ITEM (widget), TRUE);
1889 gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (widget), TRUE);
1894 modest_msg_view_window_show_toolbar (ModestWindow *self,
1895 gboolean show_toolbar)
1897 ModestMsgViewWindowPrivate *priv = NULL;
1898 ModestWindowPrivate *parent_priv;
1899 GtkWidget *reply_button = NULL, *menu = NULL;
1900 GtkWidget *placeholder = NULL;
1902 const gchar *action_name;
1905 parent_priv = MODEST_WINDOW_GET_PRIVATE(self);
1906 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(self);
1908 /* Set optimized view status */
1909 priv->optimized_view = !show_toolbar;
1911 if (!parent_priv->toolbar) {
1912 parent_priv->toolbar = gtk_ui_manager_get_widget (parent_priv->ui_manager,
1914 gtk_widget_set_no_show_all (parent_priv->toolbar, TRUE);
1916 /* Set homogeneous toolbar */
1917 gtk_container_foreach (GTK_CONTAINER (parent_priv->toolbar),
1918 set_homogeneous, NULL);
1920 priv->progress_toolitem = GTK_WIDGET (gtk_tool_item_new ());
1921 priv->cancel_toolitem = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/ToolbarCancel");
1922 priv->next_toolitem = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/ToolbarMessageNext");
1923 priv->prev_toolitem = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/ToolbarMessageBack");
1924 gtk_tool_item_set_expand (GTK_TOOL_ITEM (priv->progress_toolitem), TRUE);
1925 gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (priv->progress_toolitem), TRUE);
1926 gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (priv->cancel_toolitem), FALSE);
1927 gtk_tool_item_set_expand (GTK_TOOL_ITEM (priv->cancel_toolitem), FALSE);
1929 /* Add ProgressBar (Transfer toolbar) */
1930 priv->progress_bar = modest_progress_bar_widget_new ();
1931 gtk_widget_set_no_show_all (priv->progress_bar, TRUE);
1932 placeholder = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/ProgressbarView");
1933 insert_index = gtk_toolbar_get_item_index(GTK_TOOLBAR (parent_priv->toolbar), GTK_TOOL_ITEM(placeholder));
1934 gtk_container_add (GTK_CONTAINER (priv->progress_toolitem), priv->progress_bar);
1935 gtk_toolbar_insert(GTK_TOOLBAR(parent_priv->toolbar), GTK_TOOL_ITEM (priv->progress_toolitem), insert_index);
1937 /* Connect cancel 'clicked' signal to abort progress mode */
1938 g_signal_connect(priv->cancel_toolitem, "clicked",
1939 G_CALLBACK(cancel_progressbar),
1942 /* Add it to the observers list */
1943 priv->progress_widgets = g_slist_prepend(priv->progress_widgets, priv->progress_bar);
1946 hildon_window_add_toolbar (HILDON_WINDOW (self),
1947 GTK_TOOLBAR (parent_priv->toolbar));
1950 /* Set reply button tap and hold menu */
1951 reply_button = gtk_ui_manager_get_widget (parent_priv->ui_manager,
1952 "/ToolBar/ToolbarMessageReply");
1953 menu = gtk_ui_manager_get_widget (parent_priv->ui_manager,
1954 "/ToolbarReplyCSM");
1955 gtk_widget_tap_and_hold_setup (GTK_WIDGET (reply_button), menu, NULL, 0);
1959 /* Quick hack: this prevents toolbar icons "dance" when progress bar show status is changed */
1960 /* TODO: resize mode migth be GTK_RESIZE_QUEUE, in order to avoid unneccesary shows */
1961 gtk_container_set_resize_mode (GTK_CONTAINER(parent_priv->toolbar), GTK_RESIZE_IMMEDIATE);
1963 gtk_widget_show (GTK_WIDGET (parent_priv->toolbar));
1964 set_toolbar_mode (MODEST_MSG_VIEW_WINDOW(self), TOOLBAR_MODE_NORMAL);
1967 gtk_widget_set_no_show_all (parent_priv->toolbar, TRUE);
1968 gtk_widget_hide (GTK_WIDGET (parent_priv->toolbar));
1971 /* Update also the actions (to update the toggles in the
1972 menus), we have to do it manually because some other window
1973 of the same time could have changed it (remember that the
1974 toolbar fullscreen mode is shared by all the windows of the
1976 if (modest_window_mgr_get_fullscreen_mode (modest_runtime_get_window_mgr ()))
1977 action_name = "/MenuBar/ViewMenu/ViewShowToolbarMenu/ViewShowToolbarFullScreenMenu";
1979 action_name = "/MenuBar/ViewMenu/ViewShowToolbarMenu/ViewShowToolbarNormalScreenMenu";
1981 action = gtk_ui_manager_get_action (parent_priv->ui_manager, action_name);
1982 modest_maemo_toggle_action_set_active_block_notify (GTK_TOGGLE_ACTION (action),
1987 modest_msg_view_window_clipboard_owner_change (GtkClipboard *clipboard,
1989 ModestMsgViewWindow *window)
1991 if (!GTK_WIDGET_VISIBLE (window))
1994 modest_window_check_dimming_rules_group (MODEST_WINDOW (window), "ModestClipboardDimmingRules");
1998 modest_msg_view_window_transfer_mode_enabled (ModestMsgViewWindow *self)
2000 ModestMsgViewWindowPrivate *priv;
2002 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self), FALSE);
2003 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(self);
2005 return priv->current_toolbar_mode == TOOLBAR_MODE_TRANSFER;
2009 cancel_progressbar (GtkToolButton *toolbutton,
2010 ModestMsgViewWindow *self)
2013 ModestMsgViewWindowPrivate *priv;
2015 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(self);
2017 /* Get operation observers and cancel its current operation */
2018 tmp = priv->progress_widgets;
2020 modest_progress_object_cancel_current_operation (MODEST_PROGRESS_OBJECT(tmp->data));
2021 tmp=g_slist_next(tmp);
2025 observers_empty (ModestMsgViewWindow *self)
2028 ModestMsgViewWindowPrivate *priv;
2029 gboolean is_empty = TRUE;
2030 guint pending_ops = 0;
2032 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(self);
2033 tmp = priv->progress_widgets;
2035 /* Check all observers */
2036 while (tmp && is_empty) {
2037 pending_ops = modest_progress_object_num_pending_operations (MODEST_PROGRESS_OBJECT(tmp->data));
2038 is_empty = pending_ops == 0;
2040 tmp = g_slist_next(tmp);
2047 on_account_removed (TnyAccountStore *account_store,
2048 TnyAccount *account,
2051 /* Do nothing if it's a transport account, because we only
2052 show the messages of a store account */
2053 if (tny_account_get_account_type(account) == TNY_ACCOUNT_TYPE_STORE) {
2054 const gchar *parent_acc = NULL;
2055 const gchar *our_acc = NULL;
2057 our_acc = modest_window_get_active_account (MODEST_WINDOW (user_data));
2058 parent_acc = modest_tny_account_get_parent_modest_account_name_for_server_account (account);
2060 /* Close this window if I'm showing a message of the removed account */
2061 if (strcmp (parent_acc, our_acc) == 0)
2062 modest_ui_actions_on_close_window (NULL, MODEST_WINDOW (user_data));
2067 on_queue_changed (ModestMailOperationQueue *queue,
2068 ModestMailOperation *mail_op,
2069 ModestMailOperationQueueNotification type,
2070 ModestMsgViewWindow *self)
2073 ModestMsgViewWindowPrivate *priv;
2074 ModestMailOperationTypeOperation op_type;
2075 ModestToolBarModes mode;
2077 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self));
2078 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(self);
2080 /* If this operations was created by another window, do nothing */
2081 if (!modest_mail_operation_is_mine (mail_op, G_OBJECT(self)))
2084 /* Get toolbar mode from operation id*/
2085 op_type = modest_mail_operation_get_type_operation (mail_op);
2087 /* case MODEST_MAIL_OPERATION_TYPE_SEND: */
2088 case MODEST_MAIL_OPERATION_TYPE_RECEIVE:
2089 case MODEST_MAIL_OPERATION_TYPE_OPEN:
2090 mode = TOOLBAR_MODE_TRANSFER;
2093 mode = TOOLBAR_MODE_NORMAL;
2097 /* Add operation observers and change toolbar if neccessary*/
2098 tmp = priv->progress_widgets;
2100 case MODEST_MAIL_OPERATION_QUEUE_OPERATION_ADDED:
2101 if (mode == TOOLBAR_MODE_TRANSFER) {
2102 /* Enable transfer toolbar mode */
2103 set_toolbar_transfer_mode(self);
2105 modest_progress_object_add_operation (MODEST_PROGRESS_OBJECT (tmp->data),
2107 tmp = g_slist_next (tmp);
2112 case MODEST_MAIL_OPERATION_QUEUE_OPERATION_REMOVED:
2113 if (mode == TOOLBAR_MODE_TRANSFER) {
2115 modest_progress_object_remove_operation (MODEST_PROGRESS_OBJECT (tmp->data),
2117 tmp = g_slist_next (tmp);
2121 /* If no more operations are being observed, NORMAL mode is enabled again */
2122 if (observers_empty (self)) {
2123 set_toolbar_mode (self, TOOLBAR_MODE_NORMAL);
2131 modest_msg_view_window_get_attachments (ModestMsgViewWindow *win)
2133 ModestMsgViewWindowPrivate *priv;
2134 GList *selected_attachments = NULL;
2136 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (win), NULL);
2137 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (win);
2139 selected_attachments = modest_msg_view_get_selected_attachments (MODEST_MSG_VIEW (priv->msg_view));
2141 return selected_attachments;
2145 modest_msg_view_window_view_attachment (ModestMsgViewWindow *window, TnyMimePart *mime_part)
2147 ModestMsgViewWindowPrivate *priv;
2148 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window));
2149 g_return_if_fail (TNY_IS_MIME_PART (mime_part) || (mime_part == NULL));
2151 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
2153 if (mime_part == NULL) {
2154 gboolean error = FALSE;
2155 GList *selected_attachments = modest_msg_view_get_selected_attachments (MODEST_MSG_VIEW (priv->msg_view));
2156 if (selected_attachments == NULL) {
2158 } else if (g_list_length (selected_attachments) > 1) {
2159 hildon_banner_show_information (NULL, NULL, _("mcen_ib_unable_to_display_more"));
2162 mime_part = (TnyMimePart *) selected_attachments->data;
2163 g_object_ref (mime_part);
2165 g_list_foreach (selected_attachments, (GFunc) g_object_unref, NULL);
2166 g_list_free (selected_attachments);
2171 g_object_ref (mime_part);
2174 if (tny_mime_part_is_purged (mime_part)) {
2175 g_object_unref (mime_part);
2179 if (!TNY_IS_MSG (mime_part)) {
2180 gchar *filepath = NULL;
2181 const gchar *att_filename = tny_mime_part_get_filename (mime_part);
2182 TnyFsStream *temp_stream = NULL;
2184 temp_stream = modest_maemo_utils_create_temp_stream (att_filename, &filepath);
2187 const gchar *content_type;
2188 content_type = tny_mime_part_get_content_type (mime_part);
2189 tny_mime_part_decode_to_stream (mime_part, TNY_STREAM (temp_stream));
2191 modest_platform_activate_file (filepath, content_type);
2192 g_object_unref (temp_stream);
2194 /* NOTE: files in the temporary area will be automatically
2195 * cleaned after some time if they are no longer in use */
2198 /* message attachment */
2199 TnyHeader *header = NULL;
2200 ModestWindowMgr *mgr;
2201 ModestWindow *msg_win = NULL;
2204 header = tny_msg_get_header (TNY_MSG (mime_part));
2205 mgr = modest_runtime_get_window_mgr ();
2206 found = modest_window_mgr_find_registered_header (mgr, header, &msg_win);
2209 if (msg_win) /* there is already a window for this uid; top it */
2210 gtk_window_present (GTK_WINDOW(msg_win));
2212 /* if it's found, but there is no msg_win, it's probably in the process of being created;
2213 * thus, we don't do anything */
2214 g_warning ("window for is already being created");
2216 /* it's not found, so create a new window for it */
2217 modest_window_mgr_register_header (mgr, header); /* register the uid before building the window */
2218 gchar *account = g_strdup (modest_window_get_active_account (MODEST_WINDOW (window)));
2220 account = modest_account_mgr_get_default_account (modest_runtime_get_account_mgr ());
2221 msg_win = modest_msg_view_window_new_for_attachment (TNY_MSG (mime_part), account, NULL);
2222 modest_window_set_zoom (MODEST_WINDOW (msg_win),
2223 modest_window_get_zoom (MODEST_WINDOW (window)));
2224 modest_window_mgr_register_window (mgr, msg_win);
2225 gtk_window_set_transient_for (GTK_WINDOW (msg_win), GTK_WINDOW (window));
2226 gtk_widget_show_all (GTK_WIDGET (msg_win));
2229 g_object_unref (mime_part);
2245 static void save_mime_part_info_free (SaveMimePartInfo *info, gboolean with_struct);
2246 static gboolean idle_save_mime_part_show_result (SaveMimePartInfo *info);
2247 static gpointer save_mime_part_to_file (SaveMimePartInfo *info);
2248 static void save_mime_parts_to_file_with_checks (SaveMimePartInfo *info);
2251 save_mime_part_info_free (SaveMimePartInfo *info, gboolean with_struct)
2255 for (node = info->pairs; node != NULL; node = g_list_next (node)) {
2256 SaveMimePartPair *pair = (SaveMimePartPair *) node->data;
2257 g_free (pair->filename);
2258 g_object_unref (pair->part);
2259 g_slice_free (SaveMimePartPair, pair);
2261 g_list_free (info->pairs);
2264 gtk_widget_destroy (info->banner);
2265 g_object_unref (info->banner);
2266 g_slice_free (SaveMimePartInfo, info);
2271 idle_save_mime_part_show_result (SaveMimePartInfo *info)
2273 if (info->pairs != NULL) {
2274 save_mime_part_to_file (info);
2277 result = info->result;
2279 /* This is a GDK lock because we are an idle callback and
2280 * hildon_banner_show_information is or does Gtk+ code */
2282 gdk_threads_enter (); /* CHECKED */
2283 save_mime_part_info_free (info, TRUE);
2285 hildon_banner_show_information (NULL, NULL, _CS("sfil_ib_saved"));
2287 hildon_banner_show_information (NULL, NULL, _("mail_ib_file_operation_failed"));
2289 gdk_threads_leave (); /* CHECKED */
2296 save_mime_part_to_file (SaveMimePartInfo *info)
2298 GnomeVFSResult result;
2299 GnomeVFSHandle *handle;
2301 SaveMimePartPair *pair = (SaveMimePartPair *) info->pairs->data;
2303 result = gnome_vfs_create (&handle, pair->filename, GNOME_VFS_OPEN_WRITE, FALSE, 0777);
2304 if (result == GNOME_VFS_OK) {
2305 stream = tny_vfs_stream_new (handle);
2306 tny_mime_part_decode_to_stream (pair->part, stream);
2307 g_object_unref (G_OBJECT (stream));
2308 g_object_unref (pair->part);
2309 g_slice_free (SaveMimePartPair, pair);
2310 info->pairs = g_list_delete_link (info->pairs, info->pairs);
2311 info->result = TRUE;
2313 save_mime_part_info_free (info, FALSE);
2314 info->result = FALSE;
2317 g_idle_add ((GSourceFunc) idle_save_mime_part_show_result, info);
2322 save_mime_parts_to_file_with_checks (SaveMimePartInfo *info)
2324 gboolean is_ok = TRUE;
2325 gint replaced_files = 0;
2326 const GList *files = info->pairs;
2329 for (iter = files; (iter != NULL) && (replaced_files < 2); iter = g_list_next(iter)) {
2330 SaveMimePartPair *pair = iter->data;
2331 if (modest_maemo_utils_file_exists (pair->filename)) {
2335 if (replaced_files) {
2336 GtkWidget *confirm_overwrite_dialog;
2337 const gchar *message = (replaced_files == 1) ?
2338 _FM("docm_nc_replace_file") : _FM("docm_nc_replace_multiple");
2339 confirm_overwrite_dialog = hildon_note_new_confirmation (NULL, message);
2340 if (gtk_dialog_run (GTK_DIALOG (confirm_overwrite_dialog)) != GTK_RESPONSE_OK) {
2343 gtk_widget_destroy (confirm_overwrite_dialog);
2347 save_mime_part_info_free (info, TRUE);
2349 g_thread_create ((GThreadFunc)save_mime_part_to_file, info, FALSE, NULL);
2356 modest_msg_view_window_save_attachments (ModestMsgViewWindow *window, GList *mime_parts)
2358 gboolean clean_list = FALSE;
2359 ModestMsgViewWindowPrivate *priv;
2360 GList *files_to_save = NULL;
2361 GtkWidget *save_dialog = NULL;
2362 gchar *folder = NULL;
2363 gboolean canceled = FALSE;
2364 const gchar *filename = NULL;
2365 gchar *save_multiple_str = NULL;
2367 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window));
2368 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
2370 if (mime_parts == NULL) {
2371 mime_parts = modest_msg_view_get_selected_attachments (MODEST_MSG_VIEW (priv->msg_view));
2372 if (mime_parts == NULL)
2377 /* prepare dialog */
2378 if (mime_parts->next == NULL) {
2379 /* only one attachment selected */
2380 TnyMimePart *mime_part = (TnyMimePart *) mime_parts->data;
2381 if (!TNY_IS_MSG (mime_part) && tny_mime_part_is_attachment (mime_part)) {
2382 filename = tny_mime_part_get_filename (mime_part);
2384 g_warning ("Tried to save a non-file attachment");
2388 save_multiple_str = g_strdup_printf (_FM("sfil_va_number_of_objects_attachments"),
2389 g_list_length (mime_parts));
2392 save_dialog = hildon_file_chooser_dialog_new (GTK_WINDOW (window),
2393 GTK_FILE_CHOOSER_ACTION_SAVE);
2396 folder = g_build_filename (g_get_home_dir (), DEFAULT_FOLDER, NULL);
2397 gtk_file_chooser_set_current_folder (GTK_FILE_CHOOSER (save_dialog), folder);
2401 if (filename != NULL)
2402 gtk_file_chooser_set_current_name (GTK_FILE_CHOOSER (save_dialog),
2405 /* if multiple, set multiple string */
2406 if (save_multiple_str) {
2407 g_object_set (G_OBJECT (save_dialog), "save-multiple", save_multiple_str, NULL);
2411 if (gtk_dialog_run (GTK_DIALOG (save_dialog)) == GTK_RESPONSE_OK) {
2412 gchar *chooser_uri = gtk_file_chooser_get_uri (GTK_FILE_CHOOSER (save_dialog));
2414 if (!modest_maemo_utils_folder_writable (chooser_uri)) {
2415 hildon_banner_show_information
2416 (NULL, NULL, dgettext("hildon-fm", "sfil_ib_readonly_location"));
2420 for (node = mime_parts; node != NULL; node = g_list_next (node)) {
2421 TnyMimePart *mime_part = (TnyMimePart *) node->data;
2423 if (tny_mime_part_is_attachment (mime_part)) {
2424 SaveMimePartPair *pair;
2426 if ((mime_parts->next != NULL) &&
2427 (tny_mime_part_get_filename (mime_part) == NULL))
2430 pair = g_slice_new0 (SaveMimePartPair);
2431 if (mime_parts->next == NULL) {
2432 pair->filename = g_strdup (chooser_uri);
2435 g_build_filename (chooser_uri,
2436 tny_mime_part_get_filename (mime_part), NULL);
2438 pair->part = g_object_ref (mime_part);
2439 files_to_save = g_list_prepend (files_to_save, pair);
2443 g_free (chooser_uri);
2446 gtk_widget_destroy (save_dialog);
2449 g_list_foreach (mime_parts, (GFunc) g_object_unref, NULL);
2450 g_list_free (mime_parts);
2453 if (files_to_save != NULL) {
2454 SaveMimePartInfo *info = g_slice_new0 (SaveMimePartInfo);
2455 GtkWidget *banner = hildon_banner_show_animation (NULL, NULL,
2456 _CS("sfil_ib_saving"));
2457 info->pairs = files_to_save;
2458 info->banner = banner;
2459 info->result = TRUE;
2460 g_object_ref (banner);
2461 save_mime_parts_to_file_with_checks (info);
2466 show_remove_attachment_information (gpointer userdata)
2468 ModestMsgViewWindow *window = (ModestMsgViewWindow *) userdata;
2469 ModestMsgViewWindowPrivate *priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
2471 if (priv->remove_attachment_banner != NULL) {
2472 gtk_widget_destroy (priv->remove_attachment_banner);
2473 g_object_unref (priv->remove_attachment_banner);
2476 priv->remove_attachment_banner = g_object_ref (
2477 hildon_banner_show_animation (NULL, NULL, _("mcen_ib_removing_attachment")));
2483 modest_msg_view_window_remove_attachments (ModestMsgViewWindow *window, gboolean get_all)
2485 ModestMsgViewWindowPrivate *priv;
2486 GList *mime_parts = NULL, *node;
2487 gchar *confirmation_message;
2491 /* TnyFolder *folder; */
2493 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window));
2494 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
2497 mime_parts = modest_msg_view_get_attachments (MODEST_MSG_VIEW (priv->msg_view));
2499 mime_parts = modest_msg_view_get_selected_attachments (MODEST_MSG_VIEW (priv->msg_view));
2501 /* Remove already purged messages from mime parts list */
2503 while (node != NULL) {
2504 TnyMimePart *part = TNY_MIME_PART (node->data);
2505 if (tny_mime_part_is_purged (part)) {
2506 GList *deleted_node = node;
2507 node = g_list_next (node);
2508 g_object_unref (part);
2509 mime_parts = g_list_delete_link (mime_parts, deleted_node);
2511 node = g_list_next (node);
2515 if (mime_parts == NULL)
2518 n_attachments = g_list_length (mime_parts);
2519 if (n_attachments == 1) {
2520 const gchar *filename;
2522 if (TNY_IS_MSG (mime_parts->data)) {
2524 header = tny_msg_get_header (TNY_MSG (mime_parts->data));
2525 filename = tny_header_get_subject (header);
2526 g_object_unref (header);
2527 if (filename == NULL)
2528 filename = _("mail_va_no_subject");
2530 filename = tny_mime_part_get_filename (TNY_MIME_PART (mime_parts->data));
2532 confirmation_message = g_strdup_printf (_("mcen_nc_purge_file_text"), filename);
2534 confirmation_message = g_strdup_printf (ngettext("mcen_nc_purge_file_text",
2535 "mcen_nc_purge_files_text",
2536 n_attachments), n_attachments);
2538 response = modest_platform_run_confirmation_dialog (GTK_WINDOW (window),
2539 confirmation_message);
2540 g_free (confirmation_message);
2542 if (response != GTK_RESPONSE_OK)
2545 priv->purge_timeout = g_timeout_add (2000, show_remove_attachment_information, window);
2546 /* folder = tny_msg_get_folder (msg); */
2547 /* tny_msg_uncache_attachments (msg); */
2548 /* tny_folder_refresh (folder, NULL); */
2549 /* g_object_unref (folder); */
2551 for (node = mime_parts; node != NULL; node = g_list_next (node)) {
2552 tny_mime_part_set_purged (TNY_MIME_PART (node->data));
2553 /* modest_msg_view_remove_attachment (MODEST_MSG_VIEW (priv->msg_view), node->data); */
2556 msg = modest_msg_view_get_message (MODEST_MSG_VIEW (priv->msg_view));
2557 modest_msg_view_set_message (MODEST_MSG_VIEW (priv->msg_view), NULL);
2558 tny_msg_rewrite_cache (msg);
2559 modest_msg_view_set_message (MODEST_MSG_VIEW (priv->msg_view), msg);
2561 g_list_foreach (mime_parts, (GFunc) g_object_unref, NULL);
2562 g_list_free (mime_parts);
2564 if (priv->purge_timeout > 0) {
2565 g_source_remove (priv->purge_timeout);
2566 priv->purge_timeout = 0;
2569 if (priv->remove_attachment_banner) {
2570 gtk_widget_destroy (priv->remove_attachment_banner);
2571 g_object_unref (priv->remove_attachment_banner);
2572 priv->remove_attachment_banner = NULL;
2580 update_window_title (ModestMsgViewWindow *window)
2582 ModestMsgViewWindowPrivate *priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
2584 TnyHeader *header = NULL;
2585 const gchar *subject = NULL;
2587 msg = modest_msg_view_get_message (MODEST_MSG_VIEW (priv->msg_view));
2589 header = tny_msg_get_header (msg);
2590 subject = tny_header_get_subject (header);
2591 g_object_unref (msg);
2594 if ((subject == NULL)||(subject[0] == '\0'))
2595 subject = _("mail_va_no_subject");
2597 gtk_window_set_title (GTK_WINDOW (window), subject);
2600 static void on_move_focus (ModestMsgViewWindow *window,
2601 GtkDirectionType direction,
2604 GtkWidget *current_focus = NULL;
2606 current_focus = gtk_window_get_focus (GTK_WINDOW (window));
2607 if ((current_focus != NULL) &&
2608 MODEST_IS_ATTACHMENTS_VIEW (current_focus)) {
2609 g_signal_stop_emission_by_name (G_OBJECT (window), "move-focus");