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;
1282 modest_msg_view_grab_focus (MODEST_MSG_VIEW (priv->msg_view));
1285 if (!modest_msg_view_search_next (MODEST_MSG_VIEW (priv->msg_view))) {
1286 hildon_banner_show_information (NULL, NULL, dgettext("hildon-libs", "ckct_ib_find_search_complete"));
1287 g_free (priv->last_search);
1288 priv->last_search = NULL;
1290 modest_msg_view_grab_focus (MODEST_MSG_VIEW (priv->msg_view));
1294 g_free (current_search);
1299 modest_msg_view_window_set_zoom (ModestWindow *window,
1302 ModestMsgViewWindowPrivate *priv;
1303 ModestWindowPrivate *parent_priv;
1304 GtkAction *action = NULL;
1305 gint int_zoom = (gint) rint (zoom*100.0+0.1);
1307 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window));
1309 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1310 parent_priv = MODEST_WINDOW_GET_PRIVATE (window);
1311 modest_msg_view_set_zoom (MODEST_MSG_VIEW (priv->msg_view), zoom);
1313 action = gtk_ui_manager_get_action (parent_priv->ui_manager,
1314 "/MenuBar/ViewMenu/ZoomMenu/Zoom50Menu");
1316 gtk_radio_action_set_current_value (GTK_RADIO_ACTION (action), int_zoom);
1320 modest_msg_view_window_get_zoom (ModestWindow *window)
1322 ModestMsgViewWindowPrivate *priv;
1324 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), 1.0);
1326 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1327 return modest_msg_view_get_zoom (MODEST_MSG_VIEW (priv->msg_view));
1331 modest_msg_view_window_zoom_plus (ModestWindow *window)
1333 ModestWindowPrivate *parent_priv;
1334 GtkRadioAction *zoom_radio_action;
1335 GSList *group, *node;
1337 parent_priv = MODEST_WINDOW_GET_PRIVATE (window);
1338 zoom_radio_action = GTK_RADIO_ACTION (gtk_ui_manager_get_action (parent_priv->ui_manager,
1339 "/MenuBar/ViewMenu/ZoomMenu/Zoom50Menu"));
1341 group = gtk_radio_action_get_group (zoom_radio_action);
1343 if (gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (group->data))) {
1344 hildon_banner_show_information (NULL, NULL, dgettext("hildon-common-strings", "ckct_ib_max_zoom_level_reached"));
1348 for (node = group; node != NULL; node = g_slist_next (node)) {
1349 if ((node->next != NULL) && gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (node->next->data))) {
1350 gtk_toggle_action_set_active (GTK_TOGGLE_ACTION (node->data), TRUE);
1358 modest_msg_view_window_zoom_minus (ModestWindow *window)
1360 ModestWindowPrivate *parent_priv;
1361 GtkRadioAction *zoom_radio_action;
1362 GSList *group, *node;
1364 parent_priv = MODEST_WINDOW_GET_PRIVATE (window);
1365 zoom_radio_action = GTK_RADIO_ACTION (gtk_ui_manager_get_action (parent_priv->ui_manager,
1366 "/MenuBar/ViewMenu/ZoomMenu/Zoom50Menu"));
1368 group = gtk_radio_action_get_group (zoom_radio_action);
1370 for (node = group; node != NULL; node = g_slist_next (node)) {
1371 if (gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (node->data))) {
1372 if (node->next != NULL) {
1373 gtk_toggle_action_set_active (GTK_TOGGLE_ACTION (node->next->data), TRUE);
1376 hildon_banner_show_information (NULL, NULL, dgettext("hildon-common-strings", "ckct_ib_min_zoom_level_reached"));
1386 modest_msg_view_window_key_release_event (GtkWidget *window,
1390 if (event->type == GDK_KEY_RELEASE) {
1391 switch (event->keyval) {
1393 modest_msg_view_window_scroll_up (MODEST_WINDOW (window));
1397 modest_msg_view_window_scroll_down (MODEST_WINDOW (window));
1410 modest_msg_view_window_scroll_up (ModestWindow *window)
1412 ModestMsgViewWindowPrivate *priv;
1413 gboolean return_value;
1415 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1416 g_signal_emit_by_name (G_OBJECT (priv->main_scroll), "scroll-child", GTK_SCROLL_STEP_UP, FALSE, &return_value);
1420 modest_msg_view_window_scroll_down (ModestWindow *window)
1422 ModestMsgViewWindowPrivate *priv;
1423 gboolean return_value;
1425 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1426 g_signal_emit_by_name (G_OBJECT (priv->main_scroll), "scroll-child", GTK_SCROLL_STEP_DOWN, FALSE, &return_value);
1430 modest_msg_view_window_last_message_selected (ModestMsgViewWindow *window)
1433 ModestMsgViewWindowPrivate *priv;
1434 GtkTreeIter tmp_iter;
1435 gboolean is_last_selected;
1437 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), TRUE);
1438 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1440 /*if no model (so no rows at all), then virtually we are the last*/
1441 if (!priv->header_model)
1444 path = gtk_tree_row_reference_get_path (priv->row_reference);
1448 is_last_selected = TRUE;
1449 while (is_last_selected) {
1451 gtk_tree_path_next (path);
1452 if (!gtk_tree_model_get_iter (priv->header_model, &tmp_iter, path))
1454 gtk_tree_model_get (priv->header_model, &tmp_iter,
1455 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
1457 if (!(tny_header_get_flags(header) & TNY_HEADER_FLAG_DELETED))
1458 is_last_selected = FALSE;
1460 gtk_tree_path_free (path);
1461 return is_last_selected;
1465 modest_msg_view_window_has_headers_model (ModestMsgViewWindow *window)
1467 ModestMsgViewWindowPrivate *priv;
1469 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), TRUE);
1470 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1472 return priv->header_model != NULL;
1476 modest_msg_view_window_is_search_result (ModestMsgViewWindow *window)
1478 ModestMsgViewWindowPrivate *priv;
1480 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), TRUE);
1481 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1483 return priv->is_search_result;
1487 modest_msg_view_window_first_message_selected (ModestMsgViewWindow *window)
1490 ModestMsgViewWindowPrivate *priv;
1491 gboolean is_first_selected;
1492 GtkTreeIter tmp_iter;
1493 /* gchar * path_string;*/
1495 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), TRUE);
1496 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1498 /*if no model (so no rows at all), then virtually we are the first*/
1499 if (!priv->header_model)
1502 path = gtk_tree_row_reference_get_path (priv->row_reference);
1506 /* path_string = gtk_tree_path_to_string (path);
1507 is_first_selected = strcmp (path_string, "0");
1509 g_free (path_string);
1510 gtk_tree_path_free (path);
1512 return is_first_selected;*/
1514 is_first_selected = TRUE;
1515 while (is_first_selected) {
1517 if(!gtk_tree_path_prev (path))
1519 /* Here the 'if' is needless for logic, but let make sure
1520 * iter is valid for gtk_tree_model_get. */
1521 if (!gtk_tree_model_get_iter (priv->header_model, &tmp_iter, path))
1523 gtk_tree_model_get (priv->header_model, &tmp_iter,
1524 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
1526 if (!(tny_header_get_flags(header) & TNY_HEADER_FLAG_DELETED))
1527 is_first_selected = FALSE;
1529 gtk_tree_path_free (path);
1530 return is_first_selected;
1534 * Reads the message whose summary item is @header. It takes care of
1535 * several things, among others:
1537 * If the message was not previously downloaded then ask the user
1538 * before downloading. If there is no connection launch the connection
1539 * dialog. Update toolbar dimming rules.
1541 * Returns: TRUE if the mail operation was started, otherwise if the
1542 * user do not want to download the message, or if the user do not
1543 * want to connect, then the operation is not issued
1546 message_reader (ModestMsgViewWindow *window,
1547 ModestMsgViewWindowPrivate *priv,
1551 ModestMailOperation *mail_op = NULL;
1552 ModestMailOperationTypeOperation op_type;
1553 gboolean already_showing = FALSE;
1554 ModestWindow *msg_window = NULL;
1555 ModestWindowMgr *mgr;
1557 g_return_val_if_fail (path != NULL, FALSE);
1559 mgr = modest_runtime_get_window_mgr ();
1560 already_showing = modest_window_mgr_find_registered_header (mgr, header, &msg_window);
1561 if (already_showing && (msg_window != MODEST_WINDOW (window))) {
1564 gtk_window_present (GTK_WINDOW (msg_window));
1565 g_signal_emit_by_name (G_OBJECT (window), "delete-event", NULL, &retval);
1569 /* Msg download completed */
1570 if (tny_header_get_flags (header) & TNY_HEADER_FLAG_CACHED) {
1571 op_type = MODEST_MAIL_OPERATION_TYPE_OPEN;
1573 op_type = MODEST_MAIL_OPERATION_TYPE_RECEIVE;
1575 /* Ask the user if he wants to download the message if
1577 if (!tny_device_is_online (modest_runtime_get_device())) {
1578 TnyFolder *folder = NULL;
1579 GtkResponseType response;
1581 response = modest_platform_run_confirmation_dialog (GTK_WINDOW (window),
1582 _("mcen_nc_get_msg"));
1583 if (response == GTK_RESPONSE_CANCEL)
1586 /* Offer the connection dialog if necessary */
1587 folder = tny_header_get_folder (header);
1589 if (!modest_platform_connect_and_wait_if_network_folderstore (NULL,
1590 TNY_FOLDER_STORE (folder))) {
1591 g_object_unref (folder);
1594 g_object_unref (folder);
1599 /* New mail operation */
1600 mail_op = modest_mail_operation_new_with_error_handling (op_type,
1602 modest_ui_actions_get_msgs_full_error_handler,
1605 modest_mail_operation_queue_add (modest_runtime_get_mail_operation_queue (), mail_op);
1606 modest_mail_operation_get_msg (mail_op, header, view_msg_cb, path);
1607 g_object_unref (mail_op);
1609 /* Update toolbar dimming rules */
1610 modest_ui_actions_check_toolbar_dimming_rules (MODEST_WINDOW (window));
1616 modest_msg_view_window_select_next_message (ModestMsgViewWindow *window)
1618 ModestMsgViewWindowPrivate *priv;
1619 GtkTreePath *path= NULL;
1620 GtkTreeIter tmp_iter;
1622 gboolean retval = TRUE;
1624 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), FALSE);
1625 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1627 /* Update the next row reference if it's not valid. This could
1628 happen if for example the header which it was pointing to,
1629 was deleted. The best place to do it is in the row-deleted
1630 handler but the tinymail model do not work like the glib
1631 tree models and reports the deletion when the row is still
1633 if (!gtk_tree_row_reference_valid (priv->next_row_reference)) {
1634 if (gtk_tree_row_reference_valid (priv->row_reference)) {
1635 priv->next_row_reference = gtk_tree_row_reference_copy (priv->row_reference);
1636 select_next_valid_row (priv->header_model, &(priv->next_row_reference), FALSE);
1639 if (priv->next_row_reference)
1640 path = gtk_tree_row_reference_get_path (priv->next_row_reference);
1644 gtk_tree_model_get_iter (priv->header_model,
1648 gtk_tree_model_get (priv->header_model, &tmp_iter,
1649 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
1652 /* Read the message & show it */
1653 if (!message_reader (window, priv, header, path)) {
1655 gtk_tree_path_free (path);
1659 g_object_unref (header);
1665 modest_msg_view_window_select_first_message (ModestMsgViewWindow *self)
1667 ModestMsgViewWindowPrivate *priv = NULL;
1668 TnyHeader *header = NULL;
1672 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self), FALSE);
1673 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
1675 /* Check that the model is not empty */
1676 if (!gtk_tree_model_get_iter_first (priv->header_model, &iter))
1679 /* Get the header */
1680 gtk_tree_model_get (priv->header_model,
1682 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
1684 g_return_val_if_fail (TNY_IS_HEADER (header), FALSE);
1685 if (tny_header_get_flags (header) & TNY_HEADER_FLAG_DELETED) {
1686 g_object_unref (header);
1687 return modest_msg_view_window_select_next_message (self);
1690 path = gtk_tree_model_get_path (priv->header_model, &iter);
1692 /* Read the message & show it */
1693 message_reader (self, priv, header, path);
1696 g_object_unref (header);
1702 modest_msg_view_window_select_previous_message (ModestMsgViewWindow *window)
1704 ModestMsgViewWindowPrivate *priv = NULL;
1707 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), FALSE);
1708 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1710 /* Return inmediatly if there is no header model */
1711 if (!priv->header_model)
1714 path = gtk_tree_row_reference_get_path (priv->row_reference);
1715 while (gtk_tree_path_prev (path)) {
1719 gtk_tree_model_get_iter (priv->header_model, &iter, path);
1720 gtk_tree_model_get (priv->header_model, &iter,
1721 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
1725 if (tny_header_get_flags (header) & TNY_HEADER_FLAG_DELETED) {
1726 g_object_unref (header);
1730 /* Read the message & show it */
1731 if (!message_reader (window, priv, header, path)) {
1732 g_object_unref (header);
1736 g_object_unref (header);
1741 gtk_tree_path_free (path);
1746 view_msg_cb (ModestMailOperation *mail_op,
1751 ModestMsgViewWindow *self = NULL;
1752 ModestMsgViewWindowPrivate *priv = NULL;
1755 /* If there was any error */
1756 path = (GtkTreePath *) user_data;
1757 if (!modest_ui_actions_msg_retrieval_check (mail_op, header, msg)) {
1758 gtk_tree_path_free (path);
1762 /* Get the window */
1763 self = (ModestMsgViewWindow *) modest_mail_operation_get_source (mail_op);
1764 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self));
1765 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
1767 /* Update the row reference */
1768 gtk_tree_row_reference_free (priv->row_reference);
1769 priv->row_reference = gtk_tree_row_reference_new (priv->header_model, path);
1770 priv->next_row_reference = gtk_tree_row_reference_copy (priv->row_reference);
1771 select_next_valid_row (priv->header_model, &(priv->next_row_reference), TRUE);
1772 gtk_tree_path_free (path);
1774 /* Mark header as read */
1775 if (!(tny_header_get_flags (header) & TNY_HEADER_FLAG_SEEN))
1776 tny_header_set_flags (header, TNY_HEADER_FLAG_SEEN);
1778 /* Set new message */
1779 modest_msg_view_set_message (MODEST_MSG_VIEW (priv->msg_view), msg);
1780 modest_msg_view_window_update_priority (self);
1781 update_window_title (MODEST_MSG_VIEW_WINDOW (self));
1782 modest_msg_view_grab_focus (MODEST_MSG_VIEW (priv->msg_view));
1784 /* Set the new message uid of the window */
1785 if (priv->msg_uid) {
1786 g_free (priv->msg_uid);
1787 priv->msg_uid = modest_tny_folder_get_header_unique_id (header);
1790 /* Notify the observers */
1791 g_signal_emit (G_OBJECT (self), signals[MSG_CHANGED_SIGNAL],
1792 0, priv->header_model, priv->row_reference);
1794 /* Free new references */
1795 g_object_unref (self);
1799 modest_msg_view_window_get_folder_type (ModestMsgViewWindow *window)
1801 ModestMsgViewWindowPrivate *priv;
1803 TnyFolderType folder_type;
1805 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1807 folder_type = TNY_FOLDER_TYPE_UNKNOWN;
1809 msg = modest_msg_view_get_message (MODEST_MSG_VIEW (priv->msg_view));
1813 folder = tny_msg_get_folder (msg);
1816 folder_type = tny_folder_get_folder_type (folder);
1818 if (folder_type == TNY_FOLDER_TYPE_NORMAL || folder_type == TNY_FOLDER_TYPE_UNKNOWN) {
1819 const gchar *fname = tny_folder_get_name (folder);
1820 folder_type = modest_tny_folder_guess_folder_type_from_name (fname);
1823 g_object_unref (folder);
1825 g_object_unref (msg);
1833 modest_msg_view_window_update_priority (ModestMsgViewWindow *window)
1835 ModestMsgViewWindowPrivate *priv;
1836 TnyHeaderFlags flags = 0;
1838 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1840 if (priv->header_model) {
1843 GtkTreePath *path = NULL;
1845 path = gtk_tree_row_reference_get_path (priv->row_reference);
1846 g_return_if_fail (path != NULL);
1847 gtk_tree_model_get_iter (priv->header_model,
1849 gtk_tree_row_reference_get_path (priv->row_reference));
1851 gtk_tree_model_get (priv->header_model, &iter, TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
1853 flags = tny_header_get_flags (header);
1854 gtk_tree_path_free (path);
1857 modest_msg_view_set_priority (MODEST_MSG_VIEW(priv->msg_view), flags);
1862 modest_msg_view_window_window_state_event (GtkWidget *widget, GdkEventWindowState *event, gpointer userdata)
1864 if (event->changed_mask & GDK_WINDOW_STATE_FULLSCREEN) {
1865 ModestWindowPrivate *parent_priv;
1866 ModestWindowMgr *mgr;
1867 gboolean is_fullscreen;
1868 GtkAction *fs_toggle_action;
1871 mgr = modest_runtime_get_window_mgr ();
1872 is_fullscreen = (modest_window_mgr_get_fullscreen_mode (mgr))?1:0;
1874 parent_priv = MODEST_WINDOW_GET_PRIVATE (widget);
1876 fs_toggle_action = gtk_ui_manager_get_action (parent_priv->ui_manager, "/MenuBar/ViewMenu/ViewToggleFullscreenMenu");
1877 active = (gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (fs_toggle_action)))?1:0;
1878 if (is_fullscreen != active) {
1879 gtk_toggle_action_set_active (GTK_TOGGLE_ACTION (fs_toggle_action), is_fullscreen);
1888 set_homogeneous (GtkWidget *widget,
1891 if (GTK_IS_TOOL_ITEM (widget)) {
1892 gtk_tool_item_set_expand (GTK_TOOL_ITEM (widget), TRUE);
1893 gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (widget), TRUE);
1898 modest_msg_view_window_show_toolbar (ModestWindow *self,
1899 gboolean show_toolbar)
1901 ModestMsgViewWindowPrivate *priv = NULL;
1902 ModestWindowPrivate *parent_priv;
1903 GtkWidget *reply_button = NULL, *menu = NULL;
1904 GtkWidget *placeholder = NULL;
1906 const gchar *action_name;
1909 parent_priv = MODEST_WINDOW_GET_PRIVATE(self);
1910 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(self);
1912 /* Set optimized view status */
1913 priv->optimized_view = !show_toolbar;
1915 if (!parent_priv->toolbar) {
1916 parent_priv->toolbar = gtk_ui_manager_get_widget (parent_priv->ui_manager,
1918 gtk_widget_set_no_show_all (parent_priv->toolbar, TRUE);
1920 /* Set homogeneous toolbar */
1921 gtk_container_foreach (GTK_CONTAINER (parent_priv->toolbar),
1922 set_homogeneous, NULL);
1924 priv->progress_toolitem = GTK_WIDGET (gtk_tool_item_new ());
1925 priv->cancel_toolitem = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/ToolbarCancel");
1926 priv->next_toolitem = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/ToolbarMessageNext");
1927 priv->prev_toolitem = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/ToolbarMessageBack");
1928 gtk_tool_item_set_expand (GTK_TOOL_ITEM (priv->progress_toolitem), TRUE);
1929 gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (priv->progress_toolitem), TRUE);
1930 gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (priv->cancel_toolitem), FALSE);
1931 gtk_tool_item_set_expand (GTK_TOOL_ITEM (priv->cancel_toolitem), FALSE);
1933 /* Add ProgressBar (Transfer toolbar) */
1934 priv->progress_bar = modest_progress_bar_widget_new ();
1935 gtk_widget_set_no_show_all (priv->progress_bar, TRUE);
1936 placeholder = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/ProgressbarView");
1937 insert_index = gtk_toolbar_get_item_index(GTK_TOOLBAR (parent_priv->toolbar), GTK_TOOL_ITEM(placeholder));
1938 gtk_container_add (GTK_CONTAINER (priv->progress_toolitem), priv->progress_bar);
1939 gtk_toolbar_insert(GTK_TOOLBAR(parent_priv->toolbar), GTK_TOOL_ITEM (priv->progress_toolitem), insert_index);
1941 /* Connect cancel 'clicked' signal to abort progress mode */
1942 g_signal_connect(priv->cancel_toolitem, "clicked",
1943 G_CALLBACK(cancel_progressbar),
1946 /* Add it to the observers list */
1947 priv->progress_widgets = g_slist_prepend(priv->progress_widgets, priv->progress_bar);
1950 hildon_window_add_toolbar (HILDON_WINDOW (self),
1951 GTK_TOOLBAR (parent_priv->toolbar));
1954 /* Set reply button tap and hold menu */
1955 reply_button = gtk_ui_manager_get_widget (parent_priv->ui_manager,
1956 "/ToolBar/ToolbarMessageReply");
1957 menu = gtk_ui_manager_get_widget (parent_priv->ui_manager,
1958 "/ToolbarReplyCSM");
1959 gtk_widget_tap_and_hold_setup (GTK_WIDGET (reply_button), menu, NULL, 0);
1963 /* Quick hack: this prevents toolbar icons "dance" when progress bar show status is changed */
1964 /* TODO: resize mode migth be GTK_RESIZE_QUEUE, in order to avoid unneccesary shows */
1965 gtk_container_set_resize_mode (GTK_CONTAINER(parent_priv->toolbar), GTK_RESIZE_IMMEDIATE);
1967 gtk_widget_show (GTK_WIDGET (parent_priv->toolbar));
1968 set_toolbar_mode (MODEST_MSG_VIEW_WINDOW(self), TOOLBAR_MODE_NORMAL);
1971 gtk_widget_set_no_show_all (parent_priv->toolbar, TRUE);
1972 gtk_widget_hide (GTK_WIDGET (parent_priv->toolbar));
1975 /* Update also the actions (to update the toggles in the
1976 menus), we have to do it manually because some other window
1977 of the same time could have changed it (remember that the
1978 toolbar fullscreen mode is shared by all the windows of the
1980 if (modest_window_mgr_get_fullscreen_mode (modest_runtime_get_window_mgr ()))
1981 action_name = "/MenuBar/ViewMenu/ViewShowToolbarMenu/ViewShowToolbarFullScreenMenu";
1983 action_name = "/MenuBar/ViewMenu/ViewShowToolbarMenu/ViewShowToolbarNormalScreenMenu";
1985 action = gtk_ui_manager_get_action (parent_priv->ui_manager, action_name);
1986 modest_maemo_toggle_action_set_active_block_notify (GTK_TOGGLE_ACTION (action),
1991 modest_msg_view_window_clipboard_owner_change (GtkClipboard *clipboard,
1993 ModestMsgViewWindow *window)
1995 if (!GTK_WIDGET_VISIBLE (window))
1998 modest_window_check_dimming_rules_group (MODEST_WINDOW (window), "ModestClipboardDimmingRules");
2002 modest_msg_view_window_transfer_mode_enabled (ModestMsgViewWindow *self)
2004 ModestMsgViewWindowPrivate *priv;
2006 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self), FALSE);
2007 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(self);
2009 return priv->current_toolbar_mode == TOOLBAR_MODE_TRANSFER;
2013 cancel_progressbar (GtkToolButton *toolbutton,
2014 ModestMsgViewWindow *self)
2017 ModestMsgViewWindowPrivate *priv;
2019 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(self);
2021 /* Get operation observers and cancel its current operation */
2022 tmp = priv->progress_widgets;
2024 modest_progress_object_cancel_current_operation (MODEST_PROGRESS_OBJECT(tmp->data));
2025 tmp=g_slist_next(tmp);
2029 observers_empty (ModestMsgViewWindow *self)
2032 ModestMsgViewWindowPrivate *priv;
2033 gboolean is_empty = TRUE;
2034 guint pending_ops = 0;
2036 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(self);
2037 tmp = priv->progress_widgets;
2039 /* Check all observers */
2040 while (tmp && is_empty) {
2041 pending_ops = modest_progress_object_num_pending_operations (MODEST_PROGRESS_OBJECT(tmp->data));
2042 is_empty = pending_ops == 0;
2044 tmp = g_slist_next(tmp);
2051 on_account_removed (TnyAccountStore *account_store,
2052 TnyAccount *account,
2055 /* Do nothing if it's a transport account, because we only
2056 show the messages of a store account */
2057 if (tny_account_get_account_type(account) == TNY_ACCOUNT_TYPE_STORE) {
2058 const gchar *parent_acc = NULL;
2059 const gchar *our_acc = NULL;
2061 our_acc = modest_window_get_active_account (MODEST_WINDOW (user_data));
2062 parent_acc = modest_tny_account_get_parent_modest_account_name_for_server_account (account);
2064 /* Close this window if I'm showing a message of the removed account */
2065 if (strcmp (parent_acc, our_acc) == 0)
2066 modest_ui_actions_on_close_window (NULL, MODEST_WINDOW (user_data));
2071 on_queue_changed (ModestMailOperationQueue *queue,
2072 ModestMailOperation *mail_op,
2073 ModestMailOperationQueueNotification type,
2074 ModestMsgViewWindow *self)
2077 ModestMsgViewWindowPrivate *priv;
2078 ModestMailOperationTypeOperation op_type;
2079 ModestToolBarModes mode;
2081 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self));
2082 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(self);
2084 /* If this operations was created by another window, do nothing */
2085 if (!modest_mail_operation_is_mine (mail_op, G_OBJECT(self)))
2088 /* Get toolbar mode from operation id*/
2089 op_type = modest_mail_operation_get_type_operation (mail_op);
2091 /* case MODEST_MAIL_OPERATION_TYPE_SEND: */
2092 case MODEST_MAIL_OPERATION_TYPE_RECEIVE:
2093 case MODEST_MAIL_OPERATION_TYPE_OPEN:
2094 mode = TOOLBAR_MODE_TRANSFER;
2097 mode = TOOLBAR_MODE_NORMAL;
2101 /* Add operation observers and change toolbar if neccessary*/
2102 tmp = priv->progress_widgets;
2104 case MODEST_MAIL_OPERATION_QUEUE_OPERATION_ADDED:
2105 if (mode == TOOLBAR_MODE_TRANSFER) {
2106 /* Enable transfer toolbar mode */
2107 set_toolbar_transfer_mode(self);
2109 modest_progress_object_add_operation (MODEST_PROGRESS_OBJECT (tmp->data),
2111 tmp = g_slist_next (tmp);
2116 case MODEST_MAIL_OPERATION_QUEUE_OPERATION_REMOVED:
2117 if (mode == TOOLBAR_MODE_TRANSFER) {
2119 modest_progress_object_remove_operation (MODEST_PROGRESS_OBJECT (tmp->data),
2121 tmp = g_slist_next (tmp);
2125 /* If no more operations are being observed, NORMAL mode is enabled again */
2126 if (observers_empty (self)) {
2127 set_toolbar_mode (self, TOOLBAR_MODE_NORMAL);
2135 modest_msg_view_window_get_attachments (ModestMsgViewWindow *win)
2137 ModestMsgViewWindowPrivate *priv;
2138 GList *selected_attachments = NULL;
2140 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (win), NULL);
2141 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (win);
2143 selected_attachments = modest_msg_view_get_selected_attachments (MODEST_MSG_VIEW (priv->msg_view));
2145 return selected_attachments;
2149 modest_msg_view_window_view_attachment (ModestMsgViewWindow *window, TnyMimePart *mime_part)
2151 ModestMsgViewWindowPrivate *priv;
2152 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window));
2153 g_return_if_fail (TNY_IS_MIME_PART (mime_part) || (mime_part == NULL));
2155 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
2157 if (mime_part == NULL) {
2158 gboolean error = FALSE;
2159 GList *selected_attachments = modest_msg_view_get_selected_attachments (MODEST_MSG_VIEW (priv->msg_view));
2160 if (selected_attachments == NULL) {
2162 } else if (g_list_length (selected_attachments) > 1) {
2163 hildon_banner_show_information (NULL, NULL, _("mcen_ib_unable_to_display_more"));
2166 mime_part = (TnyMimePart *) selected_attachments->data;
2167 g_object_ref (mime_part);
2169 g_list_foreach (selected_attachments, (GFunc) g_object_unref, NULL);
2170 g_list_free (selected_attachments);
2175 g_object_ref (mime_part);
2178 if (tny_mime_part_is_purged (mime_part)) {
2179 g_object_unref (mime_part);
2183 if (!TNY_IS_MSG (mime_part)) {
2184 gchar *filepath = NULL;
2185 const gchar *att_filename = tny_mime_part_get_filename (mime_part);
2186 TnyFsStream *temp_stream = NULL;
2188 temp_stream = modest_maemo_utils_create_temp_stream (att_filename, &filepath);
2191 const gchar *content_type;
2192 content_type = tny_mime_part_get_content_type (mime_part);
2193 tny_mime_part_decode_to_stream (mime_part, TNY_STREAM (temp_stream));
2195 modest_platform_activate_file (filepath, content_type);
2196 g_object_unref (temp_stream);
2198 /* NOTE: files in the temporary area will be automatically
2199 * cleaned after some time if they are no longer in use */
2202 /* message attachment */
2203 TnyHeader *header = NULL;
2204 ModestWindowMgr *mgr;
2205 ModestWindow *msg_win = NULL;
2208 header = tny_msg_get_header (TNY_MSG (mime_part));
2209 mgr = modest_runtime_get_window_mgr ();
2210 found = modest_window_mgr_find_registered_header (mgr, header, &msg_win);
2213 if (msg_win) /* there is already a window for this uid; top it */
2214 gtk_window_present (GTK_WINDOW(msg_win));
2216 /* if it's found, but there is no msg_win, it's probably in the process of being created;
2217 * thus, we don't do anything */
2218 g_warning ("window for is already being created");
2220 /* it's not found, so create a new window for it */
2221 modest_window_mgr_register_header (mgr, header); /* register the uid before building the window */
2222 gchar *account = g_strdup (modest_window_get_active_account (MODEST_WINDOW (window)));
2224 account = modest_account_mgr_get_default_account (modest_runtime_get_account_mgr ());
2225 msg_win = modest_msg_view_window_new_for_attachment (TNY_MSG (mime_part), account, NULL);
2226 modest_window_set_zoom (MODEST_WINDOW (msg_win),
2227 modest_window_get_zoom (MODEST_WINDOW (window)));
2228 modest_window_mgr_register_window (mgr, msg_win);
2229 gtk_window_set_transient_for (GTK_WINDOW (msg_win), GTK_WINDOW (window));
2230 gtk_widget_show_all (GTK_WIDGET (msg_win));
2233 g_object_unref (mime_part);
2249 static void save_mime_part_info_free (SaveMimePartInfo *info, gboolean with_struct);
2250 static gboolean idle_save_mime_part_show_result (SaveMimePartInfo *info);
2251 static gpointer save_mime_part_to_file (SaveMimePartInfo *info);
2252 static void save_mime_parts_to_file_with_checks (SaveMimePartInfo *info);
2255 save_mime_part_info_free (SaveMimePartInfo *info, gboolean with_struct)
2259 for (node = info->pairs; node != NULL; node = g_list_next (node)) {
2260 SaveMimePartPair *pair = (SaveMimePartPair *) node->data;
2261 g_free (pair->filename);
2262 g_object_unref (pair->part);
2263 g_slice_free (SaveMimePartPair, pair);
2265 g_list_free (info->pairs);
2268 gtk_widget_destroy (info->banner);
2269 g_object_unref (info->banner);
2270 g_slice_free (SaveMimePartInfo, info);
2275 idle_save_mime_part_show_result (SaveMimePartInfo *info)
2277 if (info->pairs != NULL) {
2278 save_mime_part_to_file (info);
2281 result = info->result;
2283 /* This is a GDK lock because we are an idle callback and
2284 * hildon_banner_show_information is or does Gtk+ code */
2286 gdk_threads_enter (); /* CHECKED */
2287 save_mime_part_info_free (info, TRUE);
2289 hildon_banner_show_information (NULL, NULL, _CS("sfil_ib_saved"));
2291 hildon_banner_show_information (NULL, NULL, _("mail_ib_file_operation_failed"));
2293 gdk_threads_leave (); /* CHECKED */
2300 save_mime_part_to_file (SaveMimePartInfo *info)
2302 GnomeVFSResult result;
2303 GnomeVFSHandle *handle;
2305 SaveMimePartPair *pair = (SaveMimePartPair *) info->pairs->data;
2307 result = gnome_vfs_create (&handle, pair->filename, GNOME_VFS_OPEN_WRITE, FALSE, 0777);
2308 if (result == GNOME_VFS_OK) {
2309 stream = tny_vfs_stream_new (handle);
2310 tny_mime_part_decode_to_stream (pair->part, stream);
2311 g_object_unref (G_OBJECT (stream));
2312 g_object_unref (pair->part);
2313 g_slice_free (SaveMimePartPair, pair);
2314 info->pairs = g_list_delete_link (info->pairs, info->pairs);
2315 info->result = TRUE;
2317 save_mime_part_info_free (info, FALSE);
2318 info->result = FALSE;
2321 g_idle_add ((GSourceFunc) idle_save_mime_part_show_result, info);
2326 save_mime_parts_to_file_with_checks (SaveMimePartInfo *info)
2328 gboolean is_ok = TRUE;
2329 gint replaced_files = 0;
2330 const GList *files = info->pairs;
2333 for (iter = files; (iter != NULL) && (replaced_files < 2); iter = g_list_next(iter)) {
2334 SaveMimePartPair *pair = iter->data;
2335 if (modest_maemo_utils_file_exists (pair->filename)) {
2339 if (replaced_files) {
2340 GtkWidget *confirm_overwrite_dialog;
2341 const gchar *message = (replaced_files == 1) ?
2342 _FM("docm_nc_replace_file") : _FM("docm_nc_replace_multiple");
2343 confirm_overwrite_dialog = hildon_note_new_confirmation (NULL, message);
2344 if (gtk_dialog_run (GTK_DIALOG (confirm_overwrite_dialog)) != GTK_RESPONSE_OK) {
2347 gtk_widget_destroy (confirm_overwrite_dialog);
2351 save_mime_part_info_free (info, TRUE);
2353 g_thread_create ((GThreadFunc)save_mime_part_to_file, info, FALSE, NULL);
2360 modest_msg_view_window_save_attachments (ModestMsgViewWindow *window, GList *mime_parts)
2362 gboolean clean_list = FALSE;
2363 ModestMsgViewWindowPrivate *priv;
2364 GList *files_to_save = NULL;
2365 GtkWidget *save_dialog = NULL;
2366 gchar *folder = NULL;
2367 gboolean canceled = FALSE;
2368 const gchar *filename = NULL;
2369 gchar *save_multiple_str = NULL;
2371 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window));
2372 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
2374 if (mime_parts == NULL) {
2375 mime_parts = modest_msg_view_get_selected_attachments (MODEST_MSG_VIEW (priv->msg_view));
2376 if (mime_parts == NULL)
2381 /* prepare dialog */
2382 if (mime_parts->next == NULL) {
2383 /* only one attachment selected */
2384 TnyMimePart *mime_part = (TnyMimePart *) mime_parts->data;
2385 if (!TNY_IS_MSG (mime_part) && tny_mime_part_is_attachment (mime_part)) {
2386 filename = tny_mime_part_get_filename (mime_part);
2388 g_warning ("Tried to save a non-file attachment");
2392 save_multiple_str = g_strdup_printf (_FM("sfil_va_number_of_objects_attachments"),
2393 g_list_length (mime_parts));
2396 save_dialog = hildon_file_chooser_dialog_new (GTK_WINDOW (window),
2397 GTK_FILE_CHOOSER_ACTION_SAVE);
2400 folder = g_build_filename (g_get_home_dir (), DEFAULT_FOLDER, NULL);
2401 gtk_file_chooser_set_current_folder (GTK_FILE_CHOOSER (save_dialog), folder);
2405 if (filename != NULL)
2406 gtk_file_chooser_set_current_name (GTK_FILE_CHOOSER (save_dialog),
2409 /* if multiple, set multiple string */
2410 if (save_multiple_str) {
2411 g_object_set (G_OBJECT (save_dialog), "save-multiple", save_multiple_str, NULL);
2415 if (gtk_dialog_run (GTK_DIALOG (save_dialog)) == GTK_RESPONSE_OK) {
2416 gchar *chooser_uri = gtk_file_chooser_get_uri (GTK_FILE_CHOOSER (save_dialog));
2418 if (!modest_maemo_utils_folder_writable (chooser_uri)) {
2419 hildon_banner_show_information
2420 (NULL, NULL, dgettext("hildon-fm", "sfil_ib_readonly_location"));
2424 for (node = mime_parts; node != NULL; node = g_list_next (node)) {
2425 TnyMimePart *mime_part = (TnyMimePart *) node->data;
2427 if (tny_mime_part_is_attachment (mime_part)) {
2428 SaveMimePartPair *pair;
2430 if ((mime_parts->next != NULL) &&
2431 (tny_mime_part_get_filename (mime_part) == NULL))
2434 pair = g_slice_new0 (SaveMimePartPair);
2435 if (mime_parts->next == NULL) {
2436 pair->filename = g_strdup (chooser_uri);
2439 g_build_filename (chooser_uri,
2440 tny_mime_part_get_filename (mime_part), NULL);
2442 pair->part = g_object_ref (mime_part);
2443 files_to_save = g_list_prepend (files_to_save, pair);
2447 g_free (chooser_uri);
2450 gtk_widget_destroy (save_dialog);
2453 g_list_foreach (mime_parts, (GFunc) g_object_unref, NULL);
2454 g_list_free (mime_parts);
2457 if (files_to_save != NULL) {
2458 SaveMimePartInfo *info = g_slice_new0 (SaveMimePartInfo);
2459 GtkWidget *banner = hildon_banner_show_animation (NULL, NULL,
2460 _CS("sfil_ib_saving"));
2461 info->pairs = files_to_save;
2462 info->banner = banner;
2463 info->result = TRUE;
2464 g_object_ref (banner);
2465 save_mime_parts_to_file_with_checks (info);
2470 show_remove_attachment_information (gpointer userdata)
2472 ModestMsgViewWindow *window = (ModestMsgViewWindow *) userdata;
2473 ModestMsgViewWindowPrivate *priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
2475 if (priv->remove_attachment_banner != NULL) {
2476 gtk_widget_destroy (priv->remove_attachment_banner);
2477 g_object_unref (priv->remove_attachment_banner);
2480 priv->remove_attachment_banner = g_object_ref (
2481 hildon_banner_show_animation (NULL, NULL, _("mcen_ib_removing_attachment")));
2487 modest_msg_view_window_remove_attachments (ModestMsgViewWindow *window, gboolean get_all)
2489 ModestMsgViewWindowPrivate *priv;
2490 GList *mime_parts = NULL, *node;
2491 gchar *confirmation_message;
2495 /* TnyFolder *folder; */
2497 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window));
2498 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
2501 mime_parts = modest_msg_view_get_attachments (MODEST_MSG_VIEW (priv->msg_view));
2503 mime_parts = modest_msg_view_get_selected_attachments (MODEST_MSG_VIEW (priv->msg_view));
2505 /* Remove already purged messages from mime parts list */
2507 while (node != NULL) {
2508 TnyMimePart *part = TNY_MIME_PART (node->data);
2509 if (tny_mime_part_is_purged (part)) {
2510 GList *deleted_node = node;
2511 node = g_list_next (node);
2512 g_object_unref (part);
2513 mime_parts = g_list_delete_link (mime_parts, deleted_node);
2515 node = g_list_next (node);
2519 if (mime_parts == NULL)
2522 n_attachments = g_list_length (mime_parts);
2523 if (n_attachments == 1) {
2524 const gchar *filename;
2526 if (TNY_IS_MSG (mime_parts->data)) {
2528 header = tny_msg_get_header (TNY_MSG (mime_parts->data));
2529 filename = tny_header_get_subject (header);
2530 g_object_unref (header);
2531 if (filename == NULL)
2532 filename = _("mail_va_no_subject");
2534 filename = tny_mime_part_get_filename (TNY_MIME_PART (mime_parts->data));
2536 confirmation_message = g_strdup_printf (_("mcen_nc_purge_file_text"), filename);
2538 confirmation_message = g_strdup_printf (ngettext("mcen_nc_purge_file_text",
2539 "mcen_nc_purge_files_text",
2540 n_attachments), n_attachments);
2542 response = modest_platform_run_confirmation_dialog (GTK_WINDOW (window),
2543 confirmation_message);
2544 g_free (confirmation_message);
2546 if (response != GTK_RESPONSE_OK)
2549 priv->purge_timeout = g_timeout_add (2000, show_remove_attachment_information, window);
2550 /* folder = tny_msg_get_folder (msg); */
2551 /* tny_msg_uncache_attachments (msg); */
2552 /* tny_folder_refresh (folder, NULL); */
2553 /* g_object_unref (folder); */
2555 for (node = mime_parts; node != NULL; node = g_list_next (node)) {
2556 tny_mime_part_set_purged (TNY_MIME_PART (node->data));
2557 /* modest_msg_view_remove_attachment (MODEST_MSG_VIEW (priv->msg_view), node->data); */
2560 msg = modest_msg_view_get_message (MODEST_MSG_VIEW (priv->msg_view));
2561 modest_msg_view_set_message (MODEST_MSG_VIEW (priv->msg_view), NULL);
2562 tny_msg_rewrite_cache (msg);
2563 modest_msg_view_set_message (MODEST_MSG_VIEW (priv->msg_view), msg);
2565 g_list_foreach (mime_parts, (GFunc) g_object_unref, NULL);
2566 g_list_free (mime_parts);
2568 if (priv->purge_timeout > 0) {
2569 g_source_remove (priv->purge_timeout);
2570 priv->purge_timeout = 0;
2573 if (priv->remove_attachment_banner) {
2574 gtk_widget_destroy (priv->remove_attachment_banner);
2575 g_object_unref (priv->remove_attachment_banner);
2576 priv->remove_attachment_banner = NULL;
2584 update_window_title (ModestMsgViewWindow *window)
2586 ModestMsgViewWindowPrivate *priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
2588 TnyHeader *header = NULL;
2589 const gchar *subject = NULL;
2591 msg = modest_msg_view_get_message (MODEST_MSG_VIEW (priv->msg_view));
2593 header = tny_msg_get_header (msg);
2594 subject = tny_header_get_subject (header);
2595 g_object_unref (msg);
2598 if ((subject == NULL)||(subject[0] == '\0'))
2599 subject = _("mail_va_no_subject");
2601 gtk_window_set_title (GTK_WINDOW (window), subject);
2604 static void on_move_focus (ModestMsgViewWindow *window,
2605 GtkDirectionType direction,
2608 GtkWidget *current_focus = NULL;
2610 current_focus = gtk_window_get_focus (GTK_WINDOW (window));
2611 if ((current_focus != NULL) &&
2612 MODEST_IS_ATTACHMENTS_VIEW (current_focus)) {
2613 g_signal_stop_emission_by_name (G_OBJECT (window), "move-focus");