1 /* Copyright (c) 2006, Nokia Corporation
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions are
8 * * Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 * * Redistributions in binary form must reproduce the above copyright
11 * notice, this list of conditions and the following disclaimer in the
12 * documentation and/or other materials provided with the distribution.
13 * * Neither the name of the Nokia Corporation nor the names of its
14 * contributors may be used to endorse or promote products derived from
15 * this software without specific prior written permission.
17 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
18 * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
19 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
20 * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
21 * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
22 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
23 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
24 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
25 * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
26 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
27 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29 #include <glib/gi18n.h>
31 #include <tny-account-store.h>
32 #include <tny-simple-list.h>
34 #include <tny-mime-part.h>
35 #include <tny-vfs-stream.h>
36 #include "modest-marshal.h"
37 #include "modest-platform.h"
38 #include <modest-utils.h>
39 #include <modest-maemo-utils.h>
40 #include <modest-tny-msg.h>
41 #include <modest-msg-view-window.h>
42 #include <modest-main-window-ui.h>
43 #include "modest-msg-view-window-ui-dimming.h"
44 #include <modest-widget-memory.h>
45 #include <modest-runtime.h>
46 #include <modest-window-priv.h>
47 #include <modest-tny-folder.h>
48 #include <modest-text-utils.h>
49 #include <modest-account-mgr-helpers.h>
50 #include "modest-progress-bar.h"
51 #include "modest-defs.h"
52 #include "modest-hildon-includes.h"
53 #include "modest-ui-dimming-manager.h"
54 #include <gdk/gdkkeysyms.h>
55 #include <modest-tny-account.h>
56 #include <modest-mime-part-view.h>
57 #include <modest-isearch-view.h>
58 #include <modest-tny-mime-part.h>
61 #include <glib/gstdio.h>
62 #include <modest-debug.h>
64 #define DEFAULT_FOLDER "MyDocs/.documents"
66 static void modest_msg_view_window_class_init (ModestMsgViewWindowClass *klass);
67 static void modest_msg_view_window_init (ModestMsgViewWindow *obj);
68 static void modest_header_view_observer_init(
69 ModestHeaderViewObserverIface *iface_class);
70 static void modest_msg_view_window_finalize (GObject *obj);
71 static void modest_msg_view_window_toggle_find_toolbar (GtkToggleAction *obj,
73 static void modest_msg_view_window_find_toolbar_close (GtkWidget *widget,
74 ModestMsgViewWindow *obj);
75 static void modest_msg_view_window_find_toolbar_search (GtkWidget *widget,
76 ModestMsgViewWindow *obj);
78 static void modest_msg_view_window_disconnect_signals (ModestWindow *self);
79 static void modest_msg_view_window_set_zoom (ModestWindow *window,
81 static gdouble modest_msg_view_window_get_zoom (ModestWindow *window);
82 static gboolean modest_msg_view_window_zoom_minus (ModestWindow *window);
83 static gboolean modest_msg_view_window_zoom_plus (ModestWindow *window);
84 static gboolean modest_msg_view_window_key_event (GtkWidget *window,
87 static gboolean modest_msg_view_window_window_state_event (GtkWidget *widget,
88 GdkEventWindowState *event,
90 static void modest_msg_view_window_update_priority (ModestMsgViewWindow *window);
92 static void modest_msg_view_window_show_toolbar (ModestWindow *window,
93 gboolean show_toolbar);
95 static void modest_msg_view_window_clipboard_owner_change (GtkClipboard *clipboard,
97 ModestMsgViewWindow *window);
99 static void modest_msg_view_window_on_row_changed (GtkTreeModel *header_model,
102 ModestMsgViewWindow *window);
104 static void modest_msg_view_window_on_row_deleted (GtkTreeModel *header_model,
106 ModestMsgViewWindow *window);
108 static void modest_msg_view_window_on_row_inserted (GtkTreeModel *header_model,
109 GtkTreePath *tree_path,
110 GtkTreeIter *tree_iter,
111 ModestMsgViewWindow *window);
113 static void modest_msg_view_window_on_row_reordered (GtkTreeModel *header_model,
117 ModestMsgViewWindow *window);
119 static void modest_msg_view_window_update_model_replaced (ModestHeaderViewObserver *window,
121 const gchar *tny_folder_id);
123 static void cancel_progressbar (GtkToolButton *toolbutton,
124 ModestMsgViewWindow *self);
126 static void on_queue_changed (ModestMailOperationQueue *queue,
127 ModestMailOperation *mail_op,
128 ModestMailOperationQueueNotification type,
129 ModestMsgViewWindow *self);
131 static void on_account_removed (TnyAccountStore *account_store,
135 static void on_move_focus (GtkWidget *widget,
136 GtkDirectionType direction,
139 static void view_msg_cb (ModestMailOperation *mail_op,
146 static void set_toolbar_mode (ModestMsgViewWindow *self,
147 ModestToolBarModes mode);
149 static void update_window_title (ModestMsgViewWindow *window);
151 static gboolean set_toolbar_transfer_mode (ModestMsgViewWindow *self);
152 static void init_window (ModestMsgViewWindow *obj);
154 static gboolean msg_is_visible (TnyHeader *header, gboolean check_outbox);
156 static void check_dimming_rules_after_change (ModestMsgViewWindow *window);
158 static gboolean modest_msg_view_window_scroll_child (ModestMsgViewWindow *self,
159 GtkScrollType scroll_type,
163 /* list my signals */
170 static const GtkToggleActionEntry msg_view_toggle_action_entries [] = {
171 { "FindInMessage", MODEST_TOOLBAR_ICON_FIND, N_("qgn_toolb_gene_find"), NULL, NULL, G_CALLBACK (modest_msg_view_window_toggle_find_toolbar), FALSE },
172 { "ToolsFindInMessage", NULL, N_("mcen_me_viewer_find"), "<CTRL>F", NULL, G_CALLBACK (modest_msg_view_window_toggle_find_toolbar), FALSE },
175 static const GtkRadioActionEntry msg_view_zoom_action_entries [] = {
176 { "Zoom50", NULL, N_("mcen_me_viewer_50"), NULL, NULL, 50 },
177 { "Zoom80", NULL, N_("mcen_me_viewer_80"), NULL, NULL, 80 },
178 { "Zoom100", NULL, N_("mcen_me_viewer_100"), NULL, NULL, 100 },
179 { "Zoom120", NULL, N_("mcen_me_viewer_120"), NULL, NULL, 120 },
180 { "Zoom150", NULL, N_("mcen_me_viewer_150"), NULL, NULL, 150 },
181 { "Zoom200", NULL, N_("mcen_me_viewer_200"), NULL, NULL, 200 }
184 typedef struct _ModestMsgViewWindowPrivate ModestMsgViewWindowPrivate;
185 struct _ModestMsgViewWindowPrivate {
188 GtkWidget *main_scroll;
189 GtkWidget *find_toolbar;
192 /* Progress observers */
193 GtkWidget *progress_bar;
194 GSList *progress_widgets;
197 GtkWidget *progress_toolitem;
198 GtkWidget *cancel_toolitem;
199 GtkWidget *prev_toolitem;
200 GtkWidget *next_toolitem;
201 ModestToolBarModes current_toolbar_mode;
203 /* Optimized view enabled */
204 gboolean optimized_view;
206 /* Whether this was created via the *_new_for_search_result() function. */
207 gboolean is_search_result;
209 /* Whether the message is in outbox */
212 /* A reference to the @model of the header view
213 * to allow selecting previous/next messages,
214 * if the message is currently selected in the header view.
216 const gchar *header_folder_id;
217 GtkTreeModel *header_model;
218 GtkTreeRowReference *row_reference;
219 GtkTreeRowReference *next_row_reference;
221 gulong clipboard_change_handler;
222 gulong queue_change_handler;
223 gulong account_removed_handler;
224 gulong row_changed_handler;
225 gulong row_deleted_handler;
226 gulong row_inserted_handler;
227 gulong rows_reordered_handler;
230 GtkWidget *remove_attachment_banner;
232 guint progress_bar_timeout;
239 #define MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE((o), \
240 MODEST_TYPE_MSG_VIEW_WINDOW, \
241 ModestMsgViewWindowPrivate))
243 static GtkWindowClass *parent_class = NULL;
245 /* uncomment the following if you have defined any signals */
246 static guint signals[LAST_SIGNAL] = {0};
249 modest_msg_view_window_get_type (void)
251 static GType my_type = 0;
253 static const GTypeInfo my_info = {
254 sizeof(ModestMsgViewWindowClass),
255 NULL, /* base init */
256 NULL, /* base finalize */
257 (GClassInitFunc) modest_msg_view_window_class_init,
258 NULL, /* class finalize */
259 NULL, /* class data */
260 sizeof(ModestMsgViewWindow),
262 (GInstanceInitFunc) modest_msg_view_window_init,
265 my_type = g_type_register_static (MODEST_TYPE_WINDOW,
266 "ModestMsgViewWindow",
269 static const GInterfaceInfo modest_header_view_observer_info =
271 (GInterfaceInitFunc) modest_header_view_observer_init,
272 NULL, /* interface_finalize */
273 NULL /* interface_data */
276 g_type_add_interface_static (my_type,
277 MODEST_TYPE_HEADER_VIEW_OBSERVER,
278 &modest_header_view_observer_info);
284 save_state (ModestWindow *self)
286 modest_widget_memory_save (modest_runtime_get_conf (),
288 MODEST_CONF_MSG_VIEW_WINDOW_KEY);
293 restore_settings (ModestMsgViewWindow *self)
296 ModestWindowPrivate *parent_priv = MODEST_WINDOW_GET_PRIVATE (self);
299 conf = modest_runtime_get_conf ();
300 action = gtk_ui_manager_get_action (parent_priv->ui_manager,
301 "/MenuBar/ViewMenu/ViewShowToolbarMenu/ViewShowToolbarNormalScreenMenu");
302 gtk_toggle_action_set_active (GTK_TOGGLE_ACTION (action),
303 modest_conf_get_bool (conf, MODEST_CONF_MSG_VIEW_WINDOW_SHOW_TOOLBAR, NULL));
304 action = gtk_ui_manager_get_action (parent_priv->ui_manager,
305 "/MenuBar/ViewMenu/ViewShowToolbarMenu/ViewShowToolbarFullScreenMenu");
306 gtk_toggle_action_set_active (GTK_TOGGLE_ACTION (action),
307 modest_conf_get_bool (conf, MODEST_CONF_MSG_VIEW_WINDOW_SHOW_TOOLBAR_FULLSCREEN, NULL));
308 modest_widget_memory_restore (conf,
310 MODEST_CONF_MSG_VIEW_WINDOW_KEY);
313 static gboolean modest_msg_view_window_scroll_child (ModestMsgViewWindow *self,
314 GtkScrollType scroll_type,
318 ModestMsgViewWindowPrivate *priv;
319 gboolean return_value;
321 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(self);
322 g_signal_emit_by_name (priv->main_scroll, "scroll-child", scroll_type, horizontal, &return_value);
327 add_scroll_binding (GtkBindingSet *binding_set,
329 GtkScrollType scroll)
331 guint keypad_keyval = keyval - GDK_Left + GDK_KP_Left;
333 gtk_binding_entry_add_signal (binding_set, keyval, 0,
335 GTK_TYPE_SCROLL_TYPE, scroll,
336 G_TYPE_BOOLEAN, FALSE);
337 gtk_binding_entry_add_signal (binding_set, keypad_keyval, 0,
339 GTK_TYPE_SCROLL_TYPE, scroll,
340 G_TYPE_BOOLEAN, FALSE);
344 modest_msg_view_window_class_init (ModestMsgViewWindowClass *klass)
346 GObjectClass *gobject_class;
347 ModestWindowClass *modest_window_class;
348 GtkBindingSet *binding_set;
350 gobject_class = (GObjectClass*) klass;
351 modest_window_class = (ModestWindowClass *) klass;
353 parent_class = g_type_class_peek_parent (klass);
354 gobject_class->finalize = modest_msg_view_window_finalize;
356 modest_window_class->set_zoom_func = modest_msg_view_window_set_zoom;
357 modest_window_class->get_zoom_func = modest_msg_view_window_get_zoom;
358 modest_window_class->zoom_minus_func = modest_msg_view_window_zoom_minus;
359 modest_window_class->zoom_plus_func = modest_msg_view_window_zoom_plus;
360 modest_window_class->show_toolbar_func = modest_msg_view_window_show_toolbar;
361 modest_window_class->disconnect_signals_func = modest_msg_view_window_disconnect_signals;
363 modest_window_class->save_state_func = save_state;
365 klass->scroll_child = modest_msg_view_window_scroll_child;
367 signals[MSG_CHANGED_SIGNAL] =
368 g_signal_new ("msg-changed",
369 G_TYPE_FROM_CLASS (gobject_class),
371 G_STRUCT_OFFSET (ModestMsgViewWindowClass, msg_changed),
373 modest_marshal_VOID__POINTER_POINTER,
374 G_TYPE_NONE, 2, G_TYPE_POINTER, G_TYPE_POINTER);
376 signals[SCROLL_CHILD_SIGNAL] =
377 g_signal_new ("scroll-child",
378 G_TYPE_FROM_CLASS (gobject_class),
379 G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
380 G_STRUCT_OFFSET (ModestMsgViewWindowClass, scroll_child),
382 modest_marshal_BOOLEAN__ENUM_BOOLEAN,
383 G_TYPE_BOOLEAN, 2, GTK_TYPE_SCROLL_TYPE, G_TYPE_BOOLEAN);
385 binding_set = gtk_binding_set_by_class (klass);
386 add_scroll_binding (binding_set, GDK_Up, GTK_SCROLL_STEP_UP);
387 add_scroll_binding (binding_set, GDK_Down, GTK_SCROLL_STEP_DOWN);
388 add_scroll_binding (binding_set, GDK_Page_Up, GTK_SCROLL_PAGE_UP);
389 add_scroll_binding (binding_set, GDK_Page_Down, GTK_SCROLL_PAGE_DOWN);
390 add_scroll_binding (binding_set, GDK_Home, GTK_SCROLL_START);
391 add_scroll_binding (binding_set, GDK_End, GTK_SCROLL_END);
393 g_type_class_add_private (gobject_class, sizeof(ModestMsgViewWindowPrivate));
397 static void modest_header_view_observer_init(
398 ModestHeaderViewObserverIface *iface_class)
400 iface_class->update_func = modest_msg_view_window_update_model_replaced;
404 modest_msg_view_window_init (ModestMsgViewWindow *obj)
406 ModestMsgViewWindowPrivate *priv;
407 ModestWindowPrivate *parent_priv = NULL;
408 GtkActionGroup *action_group = NULL;
409 GError *error = NULL;
410 GdkPixbuf *window_icon;
412 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(obj);
413 parent_priv = MODEST_WINDOW_GET_PRIVATE(obj);
414 parent_priv->ui_manager = gtk_ui_manager_new();
416 action_group = gtk_action_group_new ("ModestMsgViewWindowActions");
417 gtk_action_group_set_translation_domain (action_group, GETTEXT_PACKAGE);
419 /* Add common actions */
420 gtk_action_group_add_actions (action_group,
421 modest_action_entries,
422 G_N_ELEMENTS (modest_action_entries),
424 gtk_action_group_add_toggle_actions (action_group,
425 modest_toggle_action_entries,
426 G_N_ELEMENTS (modest_toggle_action_entries),
428 gtk_action_group_add_toggle_actions (action_group,
429 msg_view_toggle_action_entries,
430 G_N_ELEMENTS (msg_view_toggle_action_entries),
432 gtk_action_group_add_radio_actions (action_group,
433 msg_view_zoom_action_entries,
434 G_N_ELEMENTS (msg_view_zoom_action_entries),
436 G_CALLBACK (modest_ui_actions_on_change_zoom),
439 gtk_ui_manager_insert_action_group (parent_priv->ui_manager, action_group, 0);
440 g_object_unref (action_group);
442 /* Load the UI definition */
443 gtk_ui_manager_add_ui_from_file (parent_priv->ui_manager, MODEST_UIDIR "modest-msg-view-window-ui.xml",
446 g_printerr ("modest: could not merge modest-msg-view-window-ui.xml: %s\n", error->message);
447 g_error_free (error);
452 /* Add accelerators */
453 gtk_window_add_accel_group (GTK_WINDOW (obj),
454 gtk_ui_manager_get_accel_group (parent_priv->ui_manager));
456 priv->is_search_result = FALSE;
457 priv->is_outbox = FALSE;
459 priv->msg_view = NULL;
460 priv->header_model = NULL;
461 priv->header_folder_id = NULL;
462 priv->clipboard_change_handler = 0;
463 priv->queue_change_handler = 0;
464 priv->account_removed_handler = 0;
465 priv->row_changed_handler = 0;
466 priv->row_deleted_handler = 0;
467 priv->row_inserted_handler = 0;
468 priv->rows_reordered_handler = 0;
469 priv->current_toolbar_mode = TOOLBAR_MODE_NORMAL;
471 priv->optimized_view = FALSE;
472 priv->progress_bar_timeout = 0;
473 priv->purge_timeout = 0;
474 priv->remove_attachment_banner = NULL;
475 priv->msg_uid = NULL;
477 priv->sighandlers = NULL;
480 init_window (MODEST_MSG_VIEW_WINDOW(obj));
482 /* Set window icon */
483 window_icon = modest_platform_get_icon (MODEST_APP_MSG_VIEW_ICON, MODEST_ICON_SIZE_BIG);
485 gtk_window_set_icon (GTK_WINDOW (obj), window_icon);
486 g_object_unref (window_icon);
489 hildon_program_add_window (hildon_program_get_instance(),
492 modest_window_mgr_register_help_id (modest_runtime_get_window_mgr(),
493 GTK_WINDOW(obj),"applications_email_viewer");
498 set_toolbar_transfer_mode (ModestMsgViewWindow *self)
500 ModestMsgViewWindowPrivate *priv = NULL;
502 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self), FALSE);
504 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(self);
506 set_toolbar_mode (self, TOOLBAR_MODE_TRANSFER);
508 if (priv->progress_bar_timeout > 0) {
509 g_source_remove (priv->progress_bar_timeout);
510 priv->progress_bar_timeout = 0;
517 set_toolbar_mode (ModestMsgViewWindow *self,
518 ModestToolBarModes mode)
520 ModestWindowPrivate *parent_priv;
521 ModestMsgViewWindowPrivate *priv;
522 /* GtkWidget *widget = NULL; */
524 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self));
526 parent_priv = MODEST_WINDOW_GET_PRIVATE(self);
527 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(self);
529 /* Sets current toolbar mode */
530 priv->current_toolbar_mode = mode;
532 /* Update toolbar dimming state */
533 modest_ui_actions_check_toolbar_dimming_rules (MODEST_WINDOW (self));
536 case TOOLBAR_MODE_NORMAL:
537 if (priv->progress_toolitem) {
538 gtk_tool_item_set_expand (GTK_TOOL_ITEM (priv->progress_toolitem), FALSE);
539 gtk_widget_hide (priv->progress_toolitem);
542 if (priv->progress_bar)
543 gtk_widget_hide (priv->progress_bar);
545 if (priv->cancel_toolitem)
546 gtk_widget_hide (priv->cancel_toolitem);
548 if (priv->prev_toolitem)
549 gtk_widget_show (priv->prev_toolitem);
551 if (priv->next_toolitem)
552 gtk_widget_show (priv->next_toolitem);
554 /* Hide toolbar if optimized view is enabled */
555 if (priv->optimized_view) {
556 gtk_widget_set_no_show_all (parent_priv->toolbar, TRUE);
557 gtk_widget_hide (GTK_WIDGET(parent_priv->toolbar));
561 case TOOLBAR_MODE_TRANSFER:
562 if (priv->prev_toolitem)
563 gtk_widget_hide (priv->prev_toolitem);
565 if (priv->next_toolitem)
566 gtk_widget_hide (priv->next_toolitem);
568 if (priv->progress_bar)
569 gtk_widget_show (priv->progress_bar);
571 if (priv->progress_toolitem) {
572 gtk_tool_item_set_expand (GTK_TOOL_ITEM (priv->progress_toolitem), TRUE);
573 gtk_widget_show (priv->progress_toolitem);
576 if (priv->cancel_toolitem)
577 gtk_widget_show (priv->cancel_toolitem);
579 /* Show toolbar if it's hiden (optimized view ) */
580 if (priv->optimized_view) {
581 gtk_widget_set_no_show_all (parent_priv->toolbar, FALSE);
582 gtk_widget_show (GTK_WIDGET (parent_priv->toolbar));
587 g_return_if_reached ();
594 init_window (ModestMsgViewWindow *obj)
596 GtkWidget *main_vbox;
597 ModestMsgViewWindowPrivate *priv;
599 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(obj);
601 priv->msg_view = GTK_WIDGET (tny_platform_factory_new_msg_view (modest_tny_platform_factory_get_instance ()));
602 modest_msg_view_set_shadow_type (MODEST_MSG_VIEW (priv->msg_view), GTK_SHADOW_NONE);
603 main_vbox = gtk_vbox_new (FALSE, 6);
605 #ifdef MODEST_USE_MOZEMBED
606 priv->main_scroll = priv->msg_view;
607 gtk_widget_set_size_request (priv->msg_view, -1, 1600);
609 priv->main_scroll = gtk_scrolled_window_new (NULL, NULL);
610 gtk_container_add (GTK_CONTAINER (priv->main_scroll), priv->msg_view);
612 gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (priv->main_scroll), GTK_POLICY_NEVER, GTK_POLICY_AUTOMATIC);
613 gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW (priv->main_scroll), GTK_SHADOW_NONE);
614 modest_maemo_set_thumbable_scrollbar (GTK_SCROLLED_WINDOW(priv->main_scroll), TRUE);
616 gtk_box_pack_start (GTK_BOX(main_vbox), priv->main_scroll, TRUE, TRUE, 0);
617 gtk_container_add (GTK_CONTAINER(obj), main_vbox);
619 priv->find_toolbar = hildon_find_toolbar_new (NULL);
620 hildon_window_add_toolbar (HILDON_WINDOW (obj), GTK_TOOLBAR (priv->find_toolbar));
621 gtk_widget_set_no_show_all (priv->find_toolbar, TRUE);
623 gtk_widget_show_all (GTK_WIDGET(main_vbox));
627 modest_msg_view_window_disconnect_signals (ModestWindow *self)
629 ModestMsgViewWindowPrivate *priv;
630 ModestHeaderView *header_view = NULL;
631 ModestWindow *main_window = NULL;
633 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
635 if (gtk_clipboard_get (GDK_SELECTION_PRIMARY) &&
636 g_signal_handler_is_connected (gtk_clipboard_get (GDK_SELECTION_PRIMARY),
637 priv->clipboard_change_handler))
638 g_signal_handler_disconnect (gtk_clipboard_get (GDK_SELECTION_PRIMARY),
639 priv->clipboard_change_handler);
641 if (g_signal_handler_is_connected (G_OBJECT (modest_runtime_get_mail_operation_queue ()),
642 priv->queue_change_handler))
643 g_signal_handler_disconnect (G_OBJECT (modest_runtime_get_mail_operation_queue ()),
644 priv->queue_change_handler);
646 if (g_signal_handler_is_connected (G_OBJECT (modest_runtime_get_account_store ()),
647 priv->account_removed_handler))
648 g_signal_handler_disconnect (G_OBJECT (modest_runtime_get_account_store ()),
649 priv->account_removed_handler);
651 if (priv->header_model) {
652 if (g_signal_handler_is_connected(G_OBJECT (priv->header_model),
653 priv->row_changed_handler))
654 g_signal_handler_disconnect(G_OBJECT (priv->header_model),
655 priv->row_changed_handler);
657 if (g_signal_handler_is_connected(G_OBJECT (priv->header_model),
658 priv->row_deleted_handler))
659 g_signal_handler_disconnect(G_OBJECT (priv->header_model),
660 priv->row_deleted_handler);
662 if (g_signal_handler_is_connected(G_OBJECT (priv->header_model),
663 priv->row_inserted_handler))
664 g_signal_handler_disconnect(G_OBJECT (priv->header_model),
665 priv->row_inserted_handler);
667 if (g_signal_handler_is_connected(G_OBJECT (priv->header_model),
668 priv->rows_reordered_handler))
669 g_signal_handler_disconnect(G_OBJECT (priv->header_model),
670 priv->rows_reordered_handler);
673 modest_signal_mgr_disconnect_all_and_destroy (priv->sighandlers);
674 priv->sighandlers = NULL;
676 main_window = modest_window_mgr_get_main_window (modest_runtime_get_window_mgr(),
677 FALSE); /* don't create */
681 header_view = MODEST_HEADER_VIEW(
682 modest_main_window_get_child_widget(
683 MODEST_MAIN_WINDOW(main_window),
684 MODEST_MAIN_WINDOW_WIDGET_TYPE_HEADER_VIEW));
685 if (header_view == NULL)
688 modest_header_view_remove_observer(header_view,
689 MODEST_HEADER_VIEW_OBSERVER(self));
693 modest_msg_view_window_finalize (GObject *obj)
695 ModestMsgViewWindowPrivate *priv;
697 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (obj);
699 /* Sanity check: shouldn't be needed, the window mgr should
700 call this function before */
701 modest_msg_view_window_disconnect_signals (MODEST_WINDOW (obj));
703 if (priv->header_model != NULL) {
704 g_object_unref (priv->header_model);
705 priv->header_model = NULL;
708 if (priv->progress_bar_timeout > 0) {
709 g_source_remove (priv->progress_bar_timeout);
710 priv->progress_bar_timeout = 0;
713 if (priv->remove_attachment_banner) {
714 gtk_widget_destroy (priv->remove_attachment_banner);
715 g_object_unref (priv->remove_attachment_banner);
716 priv->remove_attachment_banner = NULL;
719 if (priv->purge_timeout > 0) {
720 g_source_remove (priv->purge_timeout);
721 priv->purge_timeout = 0;
724 if (priv->row_reference) {
725 gtk_tree_row_reference_free (priv->row_reference);
726 priv->row_reference = NULL;
729 if (priv->next_row_reference) {
730 gtk_tree_row_reference_free (priv->next_row_reference);
731 priv->next_row_reference = NULL;
735 g_free (priv->msg_uid);
736 priv->msg_uid = NULL;
739 G_OBJECT_CLASS(parent_class)->finalize (obj);
743 select_next_valid_row (GtkTreeModel *model,
744 GtkTreeRowReference **row_reference,
747 GtkTreeIter tmp_iter;
749 GtkTreePath *next = NULL;
750 gboolean retval = FALSE;
752 g_return_val_if_fail (gtk_tree_row_reference_valid (*row_reference), FALSE);
754 path = gtk_tree_row_reference_get_path (*row_reference);
755 gtk_tree_model_get_iter (model, &tmp_iter, path);
756 gtk_tree_row_reference_free (*row_reference);
757 *row_reference = NULL;
759 if (gtk_tree_model_iter_next (model, &tmp_iter)) {
760 next = gtk_tree_model_get_path (model, &tmp_iter);
761 *row_reference = gtk_tree_row_reference_new (model, next);
763 } else if (cycle && gtk_tree_model_get_iter_first (model, &tmp_iter)) {
764 next = gtk_tree_model_get_path (model, &tmp_iter);
766 /* Ensure that we are not selecting the same */
767 if (gtk_tree_path_compare (path, next) != 0) {
768 *row_reference = gtk_tree_row_reference_new (model, next);
774 gtk_tree_path_free (path);
776 gtk_tree_path_free (next);
781 /* TODO: This should be in _init(), with the parameters as properties. */
783 modest_msg_view_window_construct (ModestMsgViewWindow *self,
784 const gchar *modest_account_name,
785 const gchar *msg_uid)
788 ModestMsgViewWindowPrivate *priv = NULL;
789 ModestWindowPrivate *parent_priv = NULL;
790 ModestDimmingRulesGroup *menu_rules_group = NULL;
791 ModestDimmingRulesGroup *toolbar_rules_group = NULL;
792 ModestDimmingRulesGroup *clipboard_rules_group = NULL;
794 obj = G_OBJECT (self);
795 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(obj);
796 parent_priv = MODEST_WINDOW_GET_PRIVATE(obj);
798 priv->msg_uid = g_strdup (msg_uid);
801 parent_priv->menubar = modest_maemo_utils_get_manager_menubar_as_menu (parent_priv->ui_manager, "/MenuBar");
802 hildon_window_set_menu (HILDON_WINDOW(obj), GTK_MENU(parent_priv->menubar));
803 gtk_widget_show (parent_priv->menubar);
804 parent_priv->ui_dimming_manager = modest_ui_dimming_manager_new();
806 menu_rules_group = modest_dimming_rules_group_new (MODEST_DIMMING_RULES_MENU, FALSE);
807 toolbar_rules_group = modest_dimming_rules_group_new (MODEST_DIMMING_RULES_TOOLBAR, TRUE);
808 clipboard_rules_group = modest_dimming_rules_group_new (MODEST_DIMMING_RULES_CLIPBOARD, FALSE);
810 /* Add common dimming rules */
811 modest_dimming_rules_group_add_rules (menu_rules_group,
812 modest_msg_view_menu_dimming_entries,
813 G_N_ELEMENTS (modest_msg_view_menu_dimming_entries),
814 MODEST_WINDOW (self));
815 modest_dimming_rules_group_add_rules (toolbar_rules_group,
816 modest_msg_view_toolbar_dimming_entries,
817 G_N_ELEMENTS (modest_msg_view_toolbar_dimming_entries),
818 MODEST_WINDOW (self));
819 modest_dimming_rules_group_add_rules (clipboard_rules_group,
820 modest_msg_view_clipboard_dimming_entries,
821 G_N_ELEMENTS (modest_msg_view_clipboard_dimming_entries),
822 MODEST_WINDOW (self));
824 /* Insert dimming rules group for this window */
825 modest_ui_dimming_manager_insert_rules_group (parent_priv->ui_dimming_manager, menu_rules_group);
826 modest_ui_dimming_manager_insert_rules_group (parent_priv->ui_dimming_manager, toolbar_rules_group);
827 modest_ui_dimming_manager_insert_rules_group (parent_priv->ui_dimming_manager, clipboard_rules_group);
828 g_object_unref (menu_rules_group);
829 g_object_unref (toolbar_rules_group);
830 g_object_unref (clipboard_rules_group);
832 restore_settings (MODEST_MSG_VIEW_WINDOW(obj));
834 /* g_signal_connect (G_OBJECT(obj), "delete-event", G_CALLBACK(on_delete_event), obj); */
836 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);
837 g_signal_connect (G_OBJECT(priv->msg_view), "activate_link",
838 G_CALLBACK (modest_ui_actions_on_msg_link_clicked), obj);
839 g_signal_connect (G_OBJECT(priv->msg_view), "link_hover",
840 G_CALLBACK (modest_ui_actions_on_msg_link_hover), obj);
841 g_signal_connect (G_OBJECT(priv->msg_view), "attachment_clicked",
842 G_CALLBACK (modest_ui_actions_on_msg_attachment_clicked), obj);
843 g_signal_connect (G_OBJECT(priv->msg_view), "recpt_activated",
844 G_CALLBACK (modest_ui_actions_on_msg_recpt_activated), obj);
845 g_signal_connect (G_OBJECT(priv->msg_view), "link_contextual",
846 G_CALLBACK (modest_ui_actions_on_msg_link_contextual), obj);
848 g_signal_connect (G_OBJECT (obj), "key-release-event",
849 G_CALLBACK (modest_msg_view_window_key_event),
852 g_signal_connect (G_OBJECT (obj), "key-press-event",
853 G_CALLBACK (modest_msg_view_window_key_event),
856 g_signal_connect (G_OBJECT (obj), "window-state-event",
857 G_CALLBACK (modest_msg_view_window_window_state_event),
860 g_signal_connect (G_OBJECT (obj), "move-focus",
861 G_CALLBACK (on_move_focus), obj);
863 /* Mail Operation Queue */
864 priv->queue_change_handler = g_signal_connect (G_OBJECT (modest_runtime_get_mail_operation_queue ()),
866 G_CALLBACK (on_queue_changed),
869 /* Account manager */
870 priv->account_removed_handler = g_signal_connect (G_OBJECT (modest_runtime_get_account_store ()),
872 G_CALLBACK(on_account_removed),
875 modest_window_set_active_account (MODEST_WINDOW(obj), modest_account_name);
877 g_signal_connect (G_OBJECT (priv->find_toolbar), "close", G_CALLBACK (modest_msg_view_window_find_toolbar_close), obj);
878 g_signal_connect (G_OBJECT (priv->find_toolbar), "search", G_CALLBACK (modest_msg_view_window_find_toolbar_search), obj);
879 priv->last_search = NULL;
881 /* Init the clipboard actions dim status */
882 modest_msg_view_grab_focus(MODEST_MSG_VIEW (priv->msg_view));
884 update_window_title (MODEST_MSG_VIEW_WINDOW (obj));
889 /* FIXME: parameter checks */
891 modest_msg_view_window_new_with_header_model (TnyMsg *msg,
892 const gchar *modest_account_name,
893 const gchar *msg_uid,
895 GtkTreeRowReference *row_reference)
897 ModestMsgViewWindow *window = NULL;
898 ModestMsgViewWindowPrivate *priv = NULL;
899 TnyFolder *header_folder = NULL;
900 ModestHeaderView *header_view = NULL;
901 ModestWindow *main_window = NULL;
902 ModestWindowMgr *mgr = NULL;
905 modest_tny_mime_part_to_string (TNY_MIME_PART (msg), 0);
908 mgr = modest_runtime_get_window_mgr ();
909 window = MODEST_MSG_VIEW_WINDOW (modest_window_mgr_get_msg_view_window (mgr));
910 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), NULL);
912 modest_msg_view_window_construct (window, modest_account_name, msg_uid);
914 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
916 /* Remember the message list's TreeModel so we can detect changes
917 * and change the list selection when necessary: */
919 main_window = modest_window_mgr_get_main_window(mgr, FALSE); /* don't create */
921 header_view = MODEST_HEADER_VIEW(modest_main_window_get_child_widget(
922 MODEST_MAIN_WINDOW(main_window),
923 MODEST_MAIN_WINDOW_WIDGET_TYPE_HEADER_VIEW));
926 if (header_view != NULL){
927 header_folder = modest_header_view_get_folder(header_view);
928 /* This could happen if the header folder was
929 unseleted before opening this msg window (for
930 example if the user selects an account in the
931 folder view of the main window */
933 priv->is_outbox = (modest_tny_folder_guess_folder_type (header_folder) == TNY_FOLDER_TYPE_OUTBOX);
934 priv->header_folder_id = tny_folder_get_id(header_folder);
935 g_assert(priv->header_folder_id != NULL);
936 g_object_unref(header_folder);
940 /* Setup row references and connect signals */
941 priv->header_model = g_object_ref (model);
944 priv->row_reference = gtk_tree_row_reference_copy (row_reference);
945 priv->next_row_reference = gtk_tree_row_reference_copy (row_reference);
946 select_next_valid_row (model, &(priv->next_row_reference), TRUE);
948 priv->row_reference = NULL;
949 priv->next_row_reference = NULL;
952 /* Connect signals */
953 priv->row_changed_handler =
954 g_signal_connect (GTK_TREE_MODEL(model), "row-changed",
955 G_CALLBACK(modest_msg_view_window_on_row_changed),
957 priv->row_deleted_handler =
958 g_signal_connect (GTK_TREE_MODEL(model), "row-deleted",
959 G_CALLBACK(modest_msg_view_window_on_row_deleted),
961 priv->row_inserted_handler =
962 g_signal_connect (GTK_TREE_MODEL(model), "row-inserted",
963 G_CALLBACK(modest_msg_view_window_on_row_inserted),
965 priv->rows_reordered_handler =
966 g_signal_connect(GTK_TREE_MODEL(model), "rows-reordered",
967 G_CALLBACK(modest_msg_view_window_on_row_reordered),
970 if (header_view != NULL){
971 modest_header_view_add_observer(header_view,
972 MODEST_HEADER_VIEW_OBSERVER(window));
975 tny_msg_view_set_msg (TNY_MSG_VIEW (priv->msg_view), msg);
976 update_window_title (MODEST_MSG_VIEW_WINDOW (window));
977 gtk_widget_show_all (GTK_WIDGET (window));
978 modest_msg_view_window_update_priority (window);
980 /* Check dimming rules */
981 modest_ui_actions_check_toolbar_dimming_rules (MODEST_WINDOW (window));
982 modest_ui_actions_check_menu_dimming_rules (MODEST_WINDOW (window));
983 modest_window_check_dimming_rules_group (MODEST_WINDOW (window), MODEST_DIMMING_RULES_CLIPBOARD);
985 return MODEST_WINDOW(window);
989 modest_msg_view_window_new_for_search_result (TnyMsg *msg,
990 const gchar *modest_account_name,
991 const gchar *msg_uid)
993 ModestMsgViewWindow *window = NULL;
994 ModestMsgViewWindowPrivate *priv = NULL;
995 ModestWindowMgr *mgr = NULL;
997 mgr = modest_runtime_get_window_mgr ();
998 window = MODEST_MSG_VIEW_WINDOW (modest_window_mgr_get_msg_view_window (mgr));
999 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), NULL);
1000 modest_msg_view_window_construct (window, modest_account_name, msg_uid);
1002 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1004 /* Remember that this is a search result,
1005 * so we can disable some UI appropriately: */
1006 priv->is_search_result = TRUE;
1008 tny_msg_view_set_msg (TNY_MSG_VIEW (priv->msg_view), msg);
1010 update_window_title (window);
1011 gtk_widget_show_all (GTK_WIDGET (window));
1012 modest_msg_view_window_update_priority (window);
1014 /* Check dimming rules */
1015 modest_ui_actions_check_toolbar_dimming_rules (MODEST_WINDOW (window));
1016 modest_ui_actions_check_menu_dimming_rules (MODEST_WINDOW (window));
1017 modest_window_check_dimming_rules_group (MODEST_WINDOW (window), MODEST_DIMMING_RULES_CLIPBOARD);
1019 return MODEST_WINDOW(window);
1023 modest_msg_view_window_new_for_attachment (TnyMsg *msg,
1024 const gchar *modest_account_name,
1025 const gchar *msg_uid)
1027 GObject *obj = NULL;
1028 ModestMsgViewWindowPrivate *priv;
1029 ModestWindowMgr *mgr = NULL;
1031 g_return_val_if_fail (msg, NULL);
1032 mgr = modest_runtime_get_window_mgr ();
1033 obj = G_OBJECT (modest_window_mgr_get_msg_view_window (mgr));
1034 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (obj);
1035 modest_msg_view_window_construct (MODEST_MSG_VIEW_WINDOW (obj),
1036 modest_account_name, msg_uid);
1038 tny_msg_view_set_msg (TNY_MSG_VIEW (priv->msg_view), msg);
1039 update_window_title (MODEST_MSG_VIEW_WINDOW (obj));
1041 gtk_widget_show_all (GTK_WIDGET (obj));
1043 /* Check dimming rules */
1044 modest_ui_actions_check_toolbar_dimming_rules (MODEST_WINDOW (obj));
1045 modest_ui_actions_check_menu_dimming_rules (MODEST_WINDOW (obj));
1046 modest_window_check_dimming_rules_group (MODEST_WINDOW (obj), MODEST_DIMMING_RULES_CLIPBOARD);
1048 return MODEST_WINDOW(obj);
1052 modest_msg_view_window_on_row_changed (GtkTreeModel *header_model,
1055 ModestMsgViewWindow *window)
1057 check_dimming_rules_after_change (window);
1061 modest_msg_view_window_on_row_deleted(GtkTreeModel *header_model,
1063 ModestMsgViewWindow *window)
1065 check_dimming_rules_after_change (window);
1069 check_dimming_rules_after_change_in_idle (gpointer data)
1071 /* The window could have dissapeared */
1072 if (MODEST_IS_WINDOW (data)) {
1073 ModestWindow *win = MODEST_WINDOW (data);
1074 gdk_threads_enter ();
1075 modest_ui_actions_check_menu_dimming_rules (win);
1076 modest_ui_actions_check_toolbar_dimming_rules (win);
1077 gdk_threads_leave ();
1084 check_dimming_rules_after_change (ModestMsgViewWindow *window)
1086 static guint dimming_delayer = 0;
1088 if (dimming_delayer > 0)
1089 g_source_remove (dimming_delayer);
1091 /* We're expecting a lot of changes at the same time so don't
1092 need to check dimming rules for every change that
1094 dimming_delayer = g_timeout_add (100, check_dimming_rules_after_change_in_idle, window);
1098 /* On insertions we check if the folder still has the message we are
1099 * showing or do not. If do not, we do nothing. Which means we are still
1100 * not attached to any header folder and thus next/prev buttons are
1101 * still dimmed. Once the message that is shown by msg-view is found, the
1102 * new model of header-view will be attached and the references will be set.
1103 * On each further insertions dimming rules will be checked. However
1104 * this requires extra CPU time at least works.
1105 * (An message might be deleted from TnyFolder and thus will not be
1106 * inserted into the model again for example if it is removed by the
1107 * imap server and the header view is refreshed.)
1110 modest_msg_view_window_on_row_inserted (GtkTreeModel *model,
1111 GtkTreePath *tree_path,
1112 GtkTreeIter *tree_iter,
1113 ModestMsgViewWindow *window)
1115 ModestMsgViewWindowPrivate *priv = NULL;
1116 TnyHeader *header = NULL;
1118 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window));
1119 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1121 g_assert (model == priv->header_model);
1123 /* Check if the newly inserted message is the same we are actually
1124 * showing. IF not, we should remain detached from the header model
1125 * and thus prev and next toolbar buttons should remain dimmed. */
1126 gtk_tree_model_get (model, tree_iter,
1127 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
1130 if (TNY_IS_HEADER (header)) {
1133 uid = modest_tny_folder_get_header_unique_id (header);
1134 if (!g_str_equal(priv->msg_uid, uid)) {
1135 check_dimming_rules_after_change (window);
1137 g_object_unref (G_OBJECT(header));
1141 g_object_unref(G_OBJECT(header));
1144 if (priv->row_reference) {
1145 gtk_tree_row_reference_free (priv->row_reference);
1148 /* Setup row_reference for the actual msg. */
1149 priv->row_reference = gtk_tree_row_reference_new (priv->header_model, tree_path);
1150 if (priv->row_reference == NULL) {
1151 g_warning("No reference for msg header item.");
1155 /* Now set up next_row_reference. */
1156 if (priv->next_row_reference) {
1157 gtk_tree_row_reference_free (priv->next_row_reference);
1160 priv->next_row_reference = gtk_tree_row_reference_copy (priv->row_reference);
1161 select_next_valid_row (priv->header_model,
1162 &(priv->next_row_reference), FALSE);
1164 /* Connect the remaining callbacks to become able to detect
1165 * changes in header-view. */
1166 priv->row_changed_handler =
1167 g_signal_connect (priv->header_model, "row-changed",
1168 G_CALLBACK (modest_msg_view_window_on_row_changed),
1170 priv->row_deleted_handler =
1171 g_signal_connect (priv->header_model, "row-deleted",
1172 G_CALLBACK (modest_msg_view_window_on_row_deleted),
1174 priv->rows_reordered_handler =
1175 g_signal_connect (priv->header_model, "rows-reordered",
1176 G_CALLBACK (modest_msg_view_window_on_row_reordered),
1179 check_dimming_rules_after_change (window);
1183 modest_msg_view_window_on_row_reordered (GtkTreeModel *header_model,
1187 ModestMsgViewWindow *window)
1189 ModestMsgViewWindowPrivate *priv = NULL;
1190 gboolean already_changed = FALSE;
1192 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(window);
1194 /* If the current row was reordered select the proper next
1195 valid row. The same if the next row reference changes */
1196 if (priv->row_reference &&
1197 gtk_tree_row_reference_valid (priv->row_reference)) {
1199 path = gtk_tree_row_reference_get_path (priv->row_reference);
1200 if (gtk_tree_path_compare (path, arg1) == 0) {
1201 if (priv->next_row_reference) {
1202 gtk_tree_row_reference_free (priv->next_row_reference);
1204 priv->next_row_reference = gtk_tree_row_reference_copy (priv->row_reference);
1205 select_next_valid_row (header_model, &(priv->next_row_reference), FALSE);
1206 already_changed = TRUE;
1208 gtk_tree_path_free (path);
1210 if (!already_changed &&
1211 priv->next_row_reference &&
1212 gtk_tree_row_reference_valid (priv->next_row_reference)) {
1214 path = gtk_tree_row_reference_get_path (priv->next_row_reference);
1215 if (gtk_tree_path_compare (path, arg1) == 0) {
1216 if (priv->next_row_reference) {
1217 gtk_tree_row_reference_free (priv->next_row_reference);
1219 priv->next_row_reference = gtk_tree_row_reference_copy (priv->row_reference);
1220 select_next_valid_row (header_model, &(priv->next_row_reference), FALSE);
1222 gtk_tree_path_free (path);
1224 check_dimming_rules_after_change (window);
1227 /* The modest_msg_view_window_update_model_replaced implements update
1228 * function for ModestHeaderViewObserver. Checks whether the TnyFolder
1229 * actually belongs to the header-view is the same as the TnyFolder of
1230 * the message of msg-view or not. If they are different, there is
1231 * nothing to do. If they are the same, then the model has replaced and
1232 * the reference in msg-view shall be replaced from the old model to
1233 * the new model. In this case the view will be detached from it's
1234 * header folder. From this point the next/prev buttons are dimmed.
1237 modest_msg_view_window_update_model_replaced (ModestHeaderViewObserver *observer,
1238 GtkTreeModel *model,
1239 const gchar *tny_folder_id)
1241 ModestMsgViewWindowPrivate *priv = NULL;
1242 ModestMsgViewWindow *window = NULL;
1244 g_assert(MODEST_IS_HEADER_VIEW_OBSERVER(observer));
1245 g_assert(MODEST_IS_MSG_VIEW_WINDOW(observer));
1247 window = MODEST_MSG_VIEW_WINDOW(observer);
1248 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(window);
1250 /* If there is an other folder in the header-view then we do
1251 * not care about it's model (msg list). Else if the
1252 * header-view shows the folder the msg shown by us is in, we
1253 * shall replace our model reference and make some check. */
1254 if(model == NULL || tny_folder_id == NULL ||
1255 (priv->header_folder_id && !g_str_equal(tny_folder_id, priv->header_folder_id)))
1258 /* Model is changed(replaced), so we should forget the old
1259 * one. Because there might be other references and there
1260 * might be some change on the model even if we unreferenced
1261 * it, we need to disconnect our signals here. */
1262 if (priv->header_model) {
1263 if (g_signal_handler_is_connected(G_OBJECT (priv->header_model),
1264 priv->row_changed_handler))
1265 g_signal_handler_disconnect(G_OBJECT (priv->header_model),
1266 priv->row_changed_handler);
1267 if (g_signal_handler_is_connected(G_OBJECT (priv->header_model),
1268 priv->row_deleted_handler))
1269 g_signal_handler_disconnect(G_OBJECT (priv->header_model),
1270 priv->row_deleted_handler);
1271 if (g_signal_handler_is_connected(G_OBJECT (priv->header_model),
1272 priv->row_inserted_handler))
1273 g_signal_handler_disconnect(G_OBJECT (priv->header_model),
1274 priv->row_inserted_handler);
1275 if (g_signal_handler_is_connected(G_OBJECT (priv->header_model),
1276 priv->rows_reordered_handler))
1277 g_signal_handler_disconnect(G_OBJECT (priv->header_model),
1278 priv->rows_reordered_handler);
1281 if (priv->row_reference)
1282 gtk_tree_row_reference_free (priv->row_reference);
1283 if (priv->next_row_reference)
1284 gtk_tree_row_reference_free (priv->next_row_reference);
1285 g_object_unref(priv->header_model);
1288 priv->row_changed_handler = 0;
1289 priv->row_deleted_handler = 0;
1290 priv->row_inserted_handler = 0;
1291 priv->rows_reordered_handler = 0;
1292 priv->next_row_reference = NULL;
1293 priv->row_reference = NULL;
1294 priv->header_model = NULL;
1297 priv->header_model = g_object_ref (model);
1299 /* Also we must connect to the new model for row insertions.
1300 * Only for insertions now. We will need other ones only after
1301 * the msg is show by msg-view is added to the new model. */
1302 priv->row_inserted_handler =
1303 g_signal_connect (priv->header_model, "row-inserted",
1304 G_CALLBACK(modest_msg_view_window_on_row_inserted),
1307 modest_ui_actions_check_menu_dimming_rules(MODEST_WINDOW(window));
1308 modest_ui_actions_check_toolbar_dimming_rules(MODEST_WINDOW(window));
1312 modest_msg_view_window_toolbar_on_transfer_mode (ModestMsgViewWindow *self)
1314 ModestMsgViewWindowPrivate *priv= NULL;
1316 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self), FALSE);
1317 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
1319 return priv->current_toolbar_mode == TOOLBAR_MODE_TRANSFER;
1323 modest_msg_view_window_get_header (ModestMsgViewWindow *self)
1325 ModestMsgViewWindowPrivate *priv= NULL;
1327 TnyHeader *header = NULL;
1328 GtkTreePath *path = NULL;
1331 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self), NULL);
1332 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
1334 /* If the message was not obtained from a treemodel,
1335 * for instance if it was opened directly by the search UI:
1337 if (priv->header_model == NULL ||
1338 priv->row_reference == NULL ||
1339 !gtk_tree_row_reference_valid (priv->row_reference)) {
1340 msg = modest_msg_view_window_get_message (self);
1342 header = tny_msg_get_header (msg);
1343 g_object_unref (msg);
1348 /* Get iter of the currently selected message in the header view: */
1349 path = gtk_tree_row_reference_get_path (priv->row_reference);
1350 g_return_val_if_fail (path != NULL, NULL);
1351 gtk_tree_model_get_iter (priv->header_model,
1355 /* Get current message header */
1356 gtk_tree_model_get (priv->header_model, &iter,
1357 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
1360 gtk_tree_path_free (path);
1365 modest_msg_view_window_get_message (ModestMsgViewWindow *self)
1367 ModestMsgViewWindowPrivate *priv;
1369 g_return_val_if_fail (self, NULL);
1371 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(self);
1373 return tny_msg_view_get_msg (TNY_MSG_VIEW (priv->msg_view));
1377 modest_msg_view_window_get_message_uid (ModestMsgViewWindow *self)
1379 ModestMsgViewWindowPrivate *priv;
1381 g_return_val_if_fail (self, NULL);
1383 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
1385 return (const gchar*) priv->msg_uid;
1389 modest_msg_view_window_toggle_find_toolbar (GtkToggleAction *toggle,
1392 ModestMsgViewWindow *window = MODEST_MSG_VIEW_WINDOW (data);
1393 ModestMsgViewWindowPrivate *priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1394 ModestWindowPrivate *parent_priv = MODEST_WINDOW_GET_PRIVATE (window);
1398 is_active = gtk_toggle_action_get_active (toggle);
1401 gtk_widget_show (priv->find_toolbar);
1402 hildon_find_toolbar_highlight_entry (HILDON_FIND_TOOLBAR (priv->find_toolbar), TRUE);
1404 gtk_widget_hide (priv->find_toolbar);
1407 /* update the toggle buttons status */
1408 action = gtk_ui_manager_get_action (parent_priv->ui_manager, "/ToolBar/FindInMessage");
1409 modest_utils_toggle_action_set_active_block_notify (GTK_TOGGLE_ACTION (action), is_active);
1410 action = gtk_ui_manager_get_action (parent_priv->ui_manager, "/MenuBar/ToolsMenu/ToolsFindInMessageMenu");
1411 modest_utils_toggle_action_set_active_block_notify (GTK_TOGGLE_ACTION (action), is_active);
1416 modest_msg_view_window_find_toolbar_close (GtkWidget *widget,
1417 ModestMsgViewWindow *obj)
1419 GtkToggleAction *toggle;
1420 ModestWindowPrivate *parent_priv;
1421 ModestMsgViewWindowPrivate *priv;
1423 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (obj);
1424 parent_priv = MODEST_WINDOW_GET_PRIVATE (obj);
1426 toggle = GTK_TOGGLE_ACTION (gtk_ui_manager_get_action (parent_priv->ui_manager, "/ToolBar/FindInMessage"));
1427 gtk_toggle_action_set_active (toggle, FALSE);
1428 modest_msg_view_grab_focus (MODEST_MSG_VIEW (priv->msg_view));
1432 modest_msg_view_window_find_toolbar_search (GtkWidget *widget,
1433 ModestMsgViewWindow *obj)
1435 gchar *current_search;
1436 ModestMsgViewWindowPrivate *priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (obj);
1438 if (modest_mime_part_view_is_empty (MODEST_MIME_PART_VIEW (priv->msg_view))) {
1439 hildon_banner_show_information (NULL, NULL, _("mail_ib_nothing_to_find"));
1443 g_object_get (G_OBJECT (widget), "prefix", ¤t_search, NULL);
1445 if ((current_search == NULL) || (strcmp (current_search, "") == 0)) {
1446 g_free (current_search);
1447 hildon_banner_show_information (NULL, NULL, dgettext("hildon-common-strings", "ecdg_ib_find_rep_enter_text"));
1451 if ((priv->last_search == NULL) || (strcmp (priv->last_search, current_search) != 0)) {
1453 g_free (priv->last_search);
1454 priv->last_search = g_strdup (current_search);
1455 result = modest_isearch_view_search (MODEST_ISEARCH_VIEW (priv->msg_view),
1458 hildon_banner_show_information (NULL, NULL, dgettext("hildon-libs", "ckct_ib_find_no_matches"));
1459 g_free (priv->last_search);
1460 priv->last_search = NULL;
1462 modest_msg_view_grab_focus (MODEST_MSG_VIEW (priv->msg_view));
1463 hildon_find_toolbar_highlight_entry (HILDON_FIND_TOOLBAR (priv->find_toolbar), TRUE);
1466 if (!modest_isearch_view_search_next (MODEST_ISEARCH_VIEW (priv->msg_view))) {
1467 hildon_banner_show_information (NULL, NULL, dgettext("hildon-libs", "ckct_ib_find_search_complete"));
1468 g_free (priv->last_search);
1469 priv->last_search = NULL;
1471 modest_msg_view_grab_focus (MODEST_MSG_VIEW (priv->msg_view));
1472 hildon_find_toolbar_highlight_entry (HILDON_FIND_TOOLBAR (priv->find_toolbar), TRUE);
1476 g_free (current_search);
1481 modest_msg_view_window_set_zoom (ModestWindow *window,
1484 ModestMsgViewWindowPrivate *priv;
1485 ModestWindowPrivate *parent_priv;
1486 GtkAction *action = NULL;
1487 gint int_zoom = (gint) rint (zoom*100.0+0.1);
1489 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window));
1491 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1492 parent_priv = MODEST_WINDOW_GET_PRIVATE (window);
1493 modest_zoomable_set_zoom (MODEST_ZOOMABLE (priv->msg_view), zoom);
1495 action = gtk_ui_manager_get_action (parent_priv->ui_manager,
1496 "/MenuBar/ViewMenu/ZoomMenu/Zoom50Menu");
1498 gtk_radio_action_set_current_value (GTK_RADIO_ACTION (action), int_zoom);
1502 modest_msg_view_window_get_zoom (ModestWindow *window)
1504 ModestMsgViewWindowPrivate *priv;
1506 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), 1.0);
1508 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1509 return modest_zoomable_get_zoom (MODEST_ZOOMABLE (priv->msg_view));
1513 modest_msg_view_window_zoom_plus (ModestWindow *window)
1515 ModestWindowPrivate *parent_priv;
1516 GtkRadioAction *zoom_radio_action;
1517 GSList *group, *node;
1519 parent_priv = MODEST_WINDOW_GET_PRIVATE (window);
1520 zoom_radio_action = GTK_RADIO_ACTION (gtk_ui_manager_get_action (parent_priv->ui_manager,
1521 "/MenuBar/ViewMenu/ZoomMenu/Zoom50Menu"));
1523 group = gtk_radio_action_get_group (zoom_radio_action);
1525 if (gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (group->data))) {
1526 hildon_banner_show_information (NULL, NULL, dgettext("hildon-common-strings", "ckct_ib_max_zoom_level_reached"));
1530 for (node = group; node != NULL; node = g_slist_next (node)) {
1531 if ((node->next != NULL) && gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (node->next->data))) {
1532 gtk_toggle_action_set_active (GTK_TOGGLE_ACTION (node->data), TRUE);
1540 modest_msg_view_window_zoom_minus (ModestWindow *window)
1542 ModestWindowPrivate *parent_priv;
1543 GtkRadioAction *zoom_radio_action;
1544 GSList *group, *node;
1546 parent_priv = MODEST_WINDOW_GET_PRIVATE (window);
1547 zoom_radio_action = GTK_RADIO_ACTION (gtk_ui_manager_get_action (parent_priv->ui_manager,
1548 "/MenuBar/ViewMenu/ZoomMenu/Zoom50Menu"));
1550 group = gtk_radio_action_get_group (zoom_radio_action);
1552 for (node = group; node != NULL; node = g_slist_next (node)) {
1553 if (gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (node->data))) {
1554 if (node->next != NULL) {
1555 gtk_toggle_action_set_active (GTK_TOGGLE_ACTION (node->next->data), TRUE);
1558 hildon_banner_show_information (NULL, NULL, dgettext("hildon-common-strings", "ckct_ib_min_zoom_level_reached"));
1568 modest_msg_view_window_key_event (GtkWidget *window,
1574 focus = gtk_window_get_focus (GTK_WINDOW (window));
1576 /* for the find toolbar case */
1577 if (focus && GTK_IS_ENTRY (focus)) {
1578 if (event->keyval == GDK_BackSpace) {
1580 copy = gdk_event_copy ((GdkEvent *) event);
1581 gtk_widget_event (focus, copy);
1582 gdk_event_free (copy);
1587 if (event->keyval == GDK_Up || event->keyval == GDK_KP_Up ||
1588 event->keyval == GDK_Down || event->keyval == GDK_KP_Down ||
1589 event->keyval == GDK_Page_Up || event->keyval == GDK_KP_Page_Up ||
1590 event->keyval == GDK_Page_Down || event->keyval == GDK_KP_Page_Down ||
1591 event->keyval == GDK_Home || event->keyval == GDK_KP_Home ||
1592 event->keyval == GDK_End || event->keyval == GDK_KP_End) {
1593 /* ModestMsgViewWindowPrivate *priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window); */
1594 /* gboolean return_value; */
1596 if (event->type == GDK_KEY_PRESS) {
1597 GtkScrollType scroll_type;
1599 switch (event->keyval) {
1602 scroll_type = GTK_SCROLL_STEP_UP; break;
1605 scroll_type = GTK_SCROLL_STEP_DOWN; break;
1607 case GDK_KP_Page_Up:
1608 scroll_type = GTK_SCROLL_PAGE_UP; break;
1610 case GDK_KP_Page_Down:
1611 scroll_type = GTK_SCROLL_PAGE_DOWN; break;
1614 scroll_type = GTK_SCROLL_START; break;
1617 scroll_type = GTK_SCROLL_END; break;
1618 default: scroll_type = GTK_SCROLL_NONE;
1621 /* g_signal_emit_by_name (G_OBJECT (priv->main_scroll), "scroll-child", */
1622 /* scroll_type, FALSE, &return_value); */
1633 modest_msg_view_window_last_message_selected (ModestMsgViewWindow *window)
1636 ModestMsgViewWindowPrivate *priv;
1637 GtkTreeIter tmp_iter;
1638 gboolean is_last_selected;
1640 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), TRUE);
1641 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1643 /*if no model (so no rows at all), then virtually we are the last*/
1644 if (!priv->header_model || !priv->row_reference)
1647 path = gtk_tree_row_reference_get_path (priv->row_reference);
1651 is_last_selected = TRUE;
1652 while (is_last_selected) {
1654 gtk_tree_path_next (path);
1655 if (!gtk_tree_model_get_iter (priv->header_model, &tmp_iter, path))
1657 gtk_tree_model_get (priv->header_model, &tmp_iter,
1658 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
1661 if (msg_is_visible (header, priv->is_outbox))
1662 is_last_selected = FALSE;
1663 g_object_unref(G_OBJECT(header));
1666 gtk_tree_path_free (path);
1667 return is_last_selected;
1671 modest_msg_view_window_has_headers_model (ModestMsgViewWindow *window)
1673 ModestMsgViewWindowPrivate *priv;
1675 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), TRUE);
1676 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1678 return priv->header_model != NULL;
1682 modest_msg_view_window_is_search_result (ModestMsgViewWindow *window)
1684 ModestMsgViewWindowPrivate *priv;
1686 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), TRUE);
1687 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1689 return priv->is_search_result;
1693 msg_is_visible (TnyHeader *header, gboolean check_outbox)
1695 return (!(tny_header_get_flags(header) & TNY_HEADER_FLAG_DELETED)) &&
1696 ( (!check_outbox) || (modest_tny_all_send_queues_get_msg_status (header) != MODEST_TNY_SEND_QUEUE_FAILED)) ;
1701 modest_msg_view_window_first_message_selected (ModestMsgViewWindow *window)
1704 ModestMsgViewWindowPrivate *priv;
1705 gboolean is_first_selected;
1706 GtkTreeIter tmp_iter;
1707 /* gchar * path_string;*/
1709 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), TRUE);
1710 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1712 /*if no model (so no rows at all), then virtually we are the first*/
1713 if (!priv->header_model || !priv->row_reference)
1716 path = gtk_tree_row_reference_get_path (priv->row_reference);
1720 /* path_string = gtk_tree_path_to_string (path);
1721 is_first_selected = strcmp (path_string, "0");
1723 g_free (path_string);
1724 gtk_tree_path_free (path);
1726 return is_first_selected;*/
1728 is_first_selected = TRUE;
1729 while (is_first_selected) {
1731 if(!gtk_tree_path_prev (path))
1733 /* Here the 'if' is needless for logic, but let make sure
1734 * iter is valid for gtk_tree_model_get. */
1735 if (!gtk_tree_model_get_iter (priv->header_model, &tmp_iter, path))
1737 gtk_tree_model_get (priv->header_model, &tmp_iter,
1738 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
1741 if (msg_is_visible (header, priv->is_outbox))
1742 is_first_selected = FALSE;
1743 g_object_unref(G_OBJECT(header));
1746 gtk_tree_path_free (path);
1747 return is_first_selected;
1752 GtkTreeRowReference *row_reference;
1756 message_reader_performer (gboolean canceled,
1758 GtkWindow *parent_window,
1759 TnyAccount *account,
1762 ModestMailOperation *mail_op = NULL;
1763 MsgReaderInfo *info;
1765 info = (MsgReaderInfo *) user_data;
1766 if (canceled || err) {
1770 /* Register the header - it'll be unregistered in the callback */
1771 modest_window_mgr_register_header (modest_runtime_get_window_mgr (), info->header, NULL);
1773 /* New mail operation */
1774 mail_op = modest_mail_operation_new_with_error_handling (G_OBJECT(parent_window),
1775 modest_ui_actions_disk_operations_error_handler,
1778 modest_mail_operation_queue_add (modest_runtime_get_mail_operation_queue (), mail_op);
1779 modest_mail_operation_get_msg (mail_op, info->header, TRUE, view_msg_cb, info->row_reference);
1780 g_object_unref (mail_op);
1782 /* Update dimming rules */
1783 modest_ui_actions_check_toolbar_dimming_rules (MODEST_WINDOW (parent_window));
1784 modest_ui_actions_check_menu_dimming_rules (MODEST_WINDOW (parent_window));
1787 /* Frees. The row_reference will be freed by the view_msg_cb callback */
1788 g_object_unref (info->header);
1789 g_slice_free (MsgReaderInfo, info);
1794 * Reads the message whose summary item is @header. It takes care of
1795 * several things, among others:
1797 * If the message was not previously downloaded then ask the user
1798 * before downloading. If there is no connection launch the connection
1799 * dialog. Update toolbar dimming rules.
1801 * Returns: TRUE if the mail operation was started, otherwise if the
1802 * user do not want to download the message, or if the user do not
1803 * want to connect, then the operation is not issued
1806 message_reader (ModestMsgViewWindow *window,
1807 ModestMsgViewWindowPrivate *priv,
1809 GtkTreeRowReference *row_reference)
1811 gboolean already_showing = FALSE;
1812 ModestWindow *msg_window = NULL;
1813 ModestWindowMgr *mgr;
1814 TnyAccount *account;
1816 MsgReaderInfo *info;
1818 g_return_val_if_fail (row_reference != NULL, FALSE);
1820 mgr = modest_runtime_get_window_mgr ();
1821 already_showing = modest_window_mgr_find_registered_header (mgr, header, &msg_window);
1822 if (already_showing && (msg_window != MODEST_WINDOW (window))) {
1825 gtk_window_present (GTK_WINDOW (msg_window));
1826 g_signal_emit_by_name (G_OBJECT (window), "delete-event", NULL, &retval);
1830 /* Msg download completed */
1831 if (!(tny_header_get_flags (header) & TNY_HEADER_FLAG_CACHED)) {
1832 /* Ask the user if he wants to download the message if
1834 if (!tny_device_is_online (modest_runtime_get_device())) {
1835 GtkResponseType response;
1837 response = modest_platform_run_confirmation_dialog (GTK_WINDOW (window),
1838 _("mcen_nc_get_msg"));
1839 if (response == GTK_RESPONSE_CANCEL)
1842 folder = tny_header_get_folder (header);
1843 info = g_slice_new (MsgReaderInfo);
1844 info->header = g_object_ref (header);
1845 info->row_reference = gtk_tree_row_reference_copy (row_reference);
1847 /* Offer the connection dialog if necessary */
1848 modest_platform_connect_if_remote_and_perform ((GtkWindow *) window,
1850 TNY_FOLDER_STORE (folder),
1851 message_reader_performer,
1853 g_object_unref (folder);
1858 folder = tny_header_get_folder (header);
1859 account = tny_folder_get_account (folder);
1860 info = g_slice_new (MsgReaderInfo);
1861 info->header = g_object_ref (header);
1862 info->row_reference = gtk_tree_row_reference_copy (row_reference);
1864 message_reader_performer (FALSE, NULL, (GtkWindow *) window, account, info);
1865 g_object_unref (account);
1866 g_object_unref (folder);
1872 modest_msg_view_window_select_next_message (ModestMsgViewWindow *window)
1874 ModestMsgViewWindowPrivate *priv;
1875 GtkTreePath *path= NULL;
1876 GtkTreeIter tmp_iter;
1878 gboolean retval = TRUE;
1879 GtkTreeRowReference *row_reference = NULL;
1881 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), FALSE);
1882 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1884 if (!priv->row_reference)
1887 /* Update the next row reference if it's not valid. This could
1888 happen if for example the header which it was pointing to,
1889 was deleted. The best place to do it is in the row-deleted
1890 handler but the tinymail model do not work like the glib
1891 tree models and reports the deletion when the row is still
1893 if (!gtk_tree_row_reference_valid (priv->next_row_reference)) {
1894 if (gtk_tree_row_reference_valid (priv->row_reference)) {
1895 priv->next_row_reference = gtk_tree_row_reference_copy (priv->row_reference);
1896 select_next_valid_row (priv->header_model, &(priv->next_row_reference), FALSE);
1899 if (priv->next_row_reference)
1900 path = gtk_tree_row_reference_get_path (priv->next_row_reference);
1904 row_reference = gtk_tree_row_reference_copy (priv->next_row_reference);
1906 gtk_tree_model_get_iter (priv->header_model,
1909 gtk_tree_path_free (path);
1911 gtk_tree_model_get (priv->header_model, &tmp_iter,
1912 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
1915 /* Read the message & show it */
1916 if (!message_reader (window, priv, header, row_reference)) {
1919 gtk_tree_row_reference_free (row_reference);
1922 g_object_unref (header);
1928 modest_msg_view_window_select_previous_message (ModestMsgViewWindow *window)
1930 ModestMsgViewWindowPrivate *priv = NULL;
1932 gboolean finished = FALSE;
1933 gboolean retval = FALSE;
1935 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), FALSE);
1936 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1938 /* Return inmediatly if there is no header model */
1939 if (!priv->header_model || !priv->row_reference)
1942 path = gtk_tree_row_reference_get_path (priv->row_reference);
1943 while (!finished && gtk_tree_path_prev (path)) {
1947 gtk_tree_model_get_iter (priv->header_model, &iter, path);
1948 gtk_tree_model_get (priv->header_model, &iter,
1949 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
1953 if (msg_is_visible (header, priv->is_outbox)) {
1954 GtkTreeRowReference *row_reference;
1955 row_reference = gtk_tree_row_reference_new (priv->header_model, path);
1956 /* Read the message & show it */
1957 retval = message_reader (window, priv, header, row_reference);
1958 gtk_tree_row_reference_free (row_reference);
1962 g_object_unref (header);
1966 gtk_tree_path_free (path);
1971 view_msg_cb (ModestMailOperation *mail_op,
1978 ModestMsgViewWindow *self = NULL;
1979 ModestMsgViewWindowPrivate *priv = NULL;
1980 GtkTreeRowReference *row_reference = NULL;
1982 /* Unregister the header (it was registered before creating the mail operation) */
1983 modest_window_mgr_unregister_header (modest_runtime_get_window_mgr (), header);
1985 row_reference = (GtkTreeRowReference *) user_data;
1987 gtk_tree_row_reference_free (row_reference);
1991 /* If there was any error */
1992 if (!modest_ui_actions_msg_retrieval_check (mail_op, header, msg)) {
1993 gtk_tree_row_reference_free (row_reference);
1997 /* Get the window */
1998 self = (ModestMsgViewWindow *) modest_mail_operation_get_source (mail_op);
1999 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self));
2000 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
2002 /* Update the row reference */
2003 if (priv->row_reference != NULL) {
2004 gtk_tree_row_reference_free (priv->row_reference);
2005 priv->row_reference = gtk_tree_row_reference_copy (row_reference);
2006 if (priv->next_row_reference != NULL) {
2007 gtk_tree_row_reference_free (priv->next_row_reference);
2009 priv->next_row_reference = gtk_tree_row_reference_copy (priv->row_reference);
2010 select_next_valid_row (priv->header_model, &(priv->next_row_reference), TRUE);
2013 /* Mark header as read */
2014 if (!(tny_header_get_flags (header) & TNY_HEADER_FLAG_SEEN))
2015 tny_header_set_flag (header, TNY_HEADER_FLAG_SEEN);
2017 /* Set new message */
2018 if (priv->msg_view != NULL && TNY_IS_MSG_VIEW (priv->msg_view)) {
2019 tny_msg_view_set_msg (TNY_MSG_VIEW (priv->msg_view), msg);
2020 modest_msg_view_window_update_priority (self);
2021 update_window_title (MODEST_MSG_VIEW_WINDOW (self));
2022 modest_msg_view_grab_focus (MODEST_MSG_VIEW (priv->msg_view));
2025 /* Set the new message uid of the window */
2026 if (priv->msg_uid) {
2027 g_free (priv->msg_uid);
2028 priv->msg_uid = modest_tny_folder_get_header_unique_id (header);
2031 /* Notify the observers */
2032 g_signal_emit (G_OBJECT (self), signals[MSG_CHANGED_SIGNAL],
2033 0, priv->header_model, priv->row_reference);
2036 g_object_unref (self);
2037 gtk_tree_row_reference_free (row_reference);
2041 modest_msg_view_window_get_folder_type (ModestMsgViewWindow *window)
2043 ModestMsgViewWindowPrivate *priv;
2045 TnyFolderType folder_type;
2047 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
2049 folder_type = TNY_FOLDER_TYPE_UNKNOWN;
2051 msg = tny_msg_view_get_msg (TNY_MSG_VIEW (priv->msg_view));
2055 folder = tny_msg_get_folder (msg);
2057 folder_type = modest_tny_folder_guess_folder_type (folder);
2058 g_object_unref (folder);
2060 g_object_unref (msg);
2068 modest_msg_view_window_update_priority (ModestMsgViewWindow *window)
2070 ModestMsgViewWindowPrivate *priv;
2071 TnyHeader *header = NULL;
2072 TnyHeaderFlags flags = 0;
2074 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
2076 if (priv->header_model && priv->row_reference) {
2078 GtkTreePath *path = NULL;
2080 path = gtk_tree_row_reference_get_path (priv->row_reference);
2081 g_return_if_fail (path != NULL);
2082 gtk_tree_model_get_iter (priv->header_model,
2084 gtk_tree_row_reference_get_path (priv->row_reference));
2086 gtk_tree_model_get (priv->header_model, &iter, TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
2088 gtk_tree_path_free (path);
2091 msg = tny_msg_view_get_msg (TNY_MSG_VIEW (priv->msg_view));
2093 header = tny_msg_get_header (msg);
2094 g_object_unref (msg);
2099 flags = tny_header_get_flags (header);
2100 g_object_unref(G_OBJECT(header));
2103 modest_msg_view_set_priority (MODEST_MSG_VIEW(priv->msg_view), flags);
2108 toolbar_resize (ModestMsgViewWindow *self)
2110 ModestMsgViewWindowPrivate *priv = NULL;
2111 ModestWindowPrivate *parent_priv = NULL;
2113 gint static_button_size;
2114 ModestWindowMgr *mgr;
2116 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self));
2117 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
2118 parent_priv = MODEST_WINDOW_GET_PRIVATE(self);
2120 mgr = modest_runtime_get_window_mgr ();
2121 static_button_size = modest_window_mgr_get_fullscreen_mode (mgr)?118:108;
2123 if (parent_priv->toolbar) {
2124 /* left size buttons */
2125 widget = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/ToolbarMessageReply");
2126 gtk_tool_item_set_expand (GTK_TOOL_ITEM (widget), FALSE);
2127 gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (widget), FALSE);
2128 gtk_widget_set_size_request (GTK_WIDGET (widget), static_button_size, -1);
2129 widget = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/ToolbarMessageMoveTo");
2130 gtk_tool_item_set_expand (GTK_TOOL_ITEM (widget), FALSE);
2131 gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (widget), FALSE);
2132 gtk_widget_set_size_request (GTK_WIDGET (widget), static_button_size, -1);
2133 widget = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/ToolbarDeleteMessage");
2134 gtk_tool_item_set_expand (GTK_TOOL_ITEM (widget), FALSE);
2135 gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (widget), FALSE);
2136 gtk_widget_set_size_request (GTK_WIDGET (widget), static_button_size, -1);
2137 widget = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/FindInMessage");
2138 gtk_tool_item_set_expand (GTK_TOOL_ITEM (widget), FALSE);
2139 gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (widget), FALSE);
2140 gtk_widget_set_size_request (GTK_WIDGET (widget), static_button_size, -1);
2142 gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (priv->progress_toolitem), FALSE);
2143 gtk_tool_item_set_expand (GTK_TOOL_ITEM (priv->progress_toolitem), TRUE);
2144 gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (priv->cancel_toolitem), FALSE);
2145 gtk_tool_item_set_expand (GTK_TOOL_ITEM (priv->cancel_toolitem), FALSE);
2146 gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (priv->next_toolitem), TRUE);
2147 gtk_tool_item_set_expand (GTK_TOOL_ITEM (priv->next_toolitem), TRUE);
2148 gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (priv->prev_toolitem), TRUE);
2149 gtk_tool_item_set_expand (GTK_TOOL_ITEM (priv->prev_toolitem), TRUE);
2155 modest_msg_view_window_window_state_event (GtkWidget *widget, GdkEventWindowState *event, gpointer userdata)
2157 if (event->changed_mask & GDK_WINDOW_STATE_FULLSCREEN) {
2158 ModestWindowPrivate *parent_priv;
2159 ModestWindowMgr *mgr;
2160 gboolean is_fullscreen;
2161 GtkAction *fs_toggle_action;
2164 mgr = modest_runtime_get_window_mgr ();
2165 is_fullscreen = (modest_window_mgr_get_fullscreen_mode (mgr))?1:0;
2167 parent_priv = MODEST_WINDOW_GET_PRIVATE (widget);
2169 fs_toggle_action = gtk_ui_manager_get_action (parent_priv->ui_manager, "/MenuBar/ViewMenu/ViewToggleFullscreenMenu");
2170 active = (gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (fs_toggle_action)))?1:0;
2171 if (is_fullscreen != active) {
2172 gtk_toggle_action_set_active (GTK_TOGGLE_ACTION (fs_toggle_action), is_fullscreen);
2174 toolbar_resize (MODEST_MSG_VIEW_WINDOW (widget));
2182 modest_msg_view_window_show_toolbar (ModestWindow *self,
2183 gboolean show_toolbar)
2185 ModestMsgViewWindowPrivate *priv = NULL;
2186 ModestWindowPrivate *parent_priv;
2187 GtkWidget *reply_button = NULL, *menu = NULL;
2188 GtkWidget *placeholder = NULL;
2190 const gchar *action_name;
2193 parent_priv = MODEST_WINDOW_GET_PRIVATE(self);
2194 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(self);
2196 /* Set optimized view status */
2197 priv->optimized_view = !show_toolbar;
2199 if (!parent_priv->toolbar) {
2200 parent_priv->toolbar = gtk_ui_manager_get_widget (parent_priv->ui_manager,
2202 gtk_widget_set_no_show_all (parent_priv->toolbar, TRUE);
2204 priv->progress_toolitem = GTK_WIDGET (gtk_tool_item_new ());
2205 priv->cancel_toolitem = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/ToolbarCancel");
2206 priv->next_toolitem = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/ToolbarMessageNext");
2207 priv->prev_toolitem = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/ToolbarMessageBack");
2208 toolbar_resize (MODEST_MSG_VIEW_WINDOW (self));
2210 /* Add ProgressBar (Transfer toolbar) */
2211 priv->progress_bar = modest_progress_bar_new ();
2212 gtk_widget_set_no_show_all (priv->progress_bar, TRUE);
2213 placeholder = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/ProgressbarView");
2214 insert_index = gtk_toolbar_get_item_index(GTK_TOOLBAR (parent_priv->toolbar), GTK_TOOL_ITEM(placeholder));
2215 gtk_container_add (GTK_CONTAINER (priv->progress_toolitem), priv->progress_bar);
2216 gtk_toolbar_insert(GTK_TOOLBAR(parent_priv->toolbar), GTK_TOOL_ITEM (priv->progress_toolitem), insert_index);
2218 /* Connect cancel 'clicked' signal to abort progress mode */
2219 g_signal_connect(priv->cancel_toolitem, "clicked",
2220 G_CALLBACK(cancel_progressbar),
2223 /* Add it to the observers list */
2224 priv->progress_widgets = g_slist_prepend(priv->progress_widgets, priv->progress_bar);
2227 hildon_window_add_toolbar (HILDON_WINDOW (self),
2228 GTK_TOOLBAR (parent_priv->toolbar));
2230 /* Set reply button tap and hold menu */
2231 reply_button = gtk_ui_manager_get_widget (parent_priv->ui_manager,
2232 "/ToolBar/ToolbarMessageReply");
2233 menu = gtk_ui_manager_get_widget (parent_priv->ui_manager,
2234 "/ToolbarReplyCSM");
2235 gtk_widget_tap_and_hold_setup (GTK_WIDGET (reply_button), menu, NULL, 0);
2239 /* Quick hack: this prevents toolbar icons "dance" when progress bar show status is changed */
2240 /* TODO: resize mode migth be GTK_RESIZE_QUEUE, in order to avoid unneccesary shows */
2241 gtk_container_set_resize_mode (GTK_CONTAINER(parent_priv->toolbar), GTK_RESIZE_IMMEDIATE);
2243 gtk_widget_show (GTK_WIDGET (parent_priv->toolbar));
2244 if (modest_msg_view_window_transfer_mode_enabled (MODEST_MSG_VIEW_WINDOW (self)))
2245 set_toolbar_mode (MODEST_MSG_VIEW_WINDOW (self), TOOLBAR_MODE_TRANSFER);
2247 set_toolbar_mode (MODEST_MSG_VIEW_WINDOW (self), TOOLBAR_MODE_NORMAL);
2250 gtk_widget_set_no_show_all (parent_priv->toolbar, TRUE);
2251 gtk_widget_hide (GTK_WIDGET (parent_priv->toolbar));
2254 /* Update also the actions (to update the toggles in the
2255 menus), we have to do it manually because some other window
2256 of the same time could have changed it (remember that the
2257 toolbar fullscreen mode is shared by all the windows of the
2259 if (modest_window_mgr_get_fullscreen_mode (modest_runtime_get_window_mgr ()))
2260 action_name = "/MenuBar/ViewMenu/ViewShowToolbarMenu/ViewShowToolbarFullScreenMenu";
2262 action_name = "/MenuBar/ViewMenu/ViewShowToolbarMenu/ViewShowToolbarNormalScreenMenu";
2264 action = gtk_ui_manager_get_action (parent_priv->ui_manager, action_name);
2265 modest_utils_toggle_action_set_active_block_notify (GTK_TOGGLE_ACTION (action),
2270 modest_msg_view_window_clipboard_owner_change (GtkClipboard *clipboard,
2272 ModestMsgViewWindow *window)
2274 if (!GTK_WIDGET_VISIBLE (window))
2277 modest_window_check_dimming_rules_group (MODEST_WINDOW (window), MODEST_DIMMING_RULES_CLIPBOARD);
2281 modest_msg_view_window_transfer_mode_enabled (ModestMsgViewWindow *self)
2283 ModestMsgViewWindowPrivate *priv;
2285 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self), FALSE);
2286 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(self);
2288 return priv->current_toolbar_mode == TOOLBAR_MODE_TRANSFER;
2292 cancel_progressbar (GtkToolButton *toolbutton,
2293 ModestMsgViewWindow *self)
2296 ModestMsgViewWindowPrivate *priv;
2298 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(self);
2300 /* Get operation observers and cancel its current operation */
2301 tmp = priv->progress_widgets;
2303 modest_progress_object_cancel_current_operation (MODEST_PROGRESS_OBJECT(tmp->data));
2304 tmp=g_slist_next(tmp);
2308 observers_empty (ModestMsgViewWindow *self)
2311 ModestMsgViewWindowPrivate *priv;
2312 gboolean is_empty = TRUE;
2313 guint pending_ops = 0;
2315 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(self);
2316 tmp = priv->progress_widgets;
2318 /* Check all observers */
2319 while (tmp && is_empty) {
2320 pending_ops = modest_progress_object_num_pending_operations (MODEST_PROGRESS_OBJECT(tmp->data));
2321 is_empty = pending_ops == 0;
2323 tmp = g_slist_next(tmp);
2330 on_account_removed (TnyAccountStore *account_store,
2331 TnyAccount *account,
2334 /* Do nothing if it's a transport account, because we only
2335 show the messages of a store account */
2336 if (tny_account_get_account_type(account) == TNY_ACCOUNT_TYPE_STORE) {
2337 const gchar *parent_acc = NULL;
2338 const gchar *our_acc = NULL;
2340 our_acc = modest_window_get_active_account (MODEST_WINDOW (user_data));
2341 parent_acc = modest_tny_account_get_parent_modest_account_name_for_server_account (account);
2343 /* Close this window if I'm showing a message of the removed account */
2344 if (strcmp (parent_acc, our_acc) == 0)
2345 modest_ui_actions_on_close_window (NULL, MODEST_WINDOW (user_data));
2350 on_mail_operation_started (ModestMailOperation *mail_op,
2353 ModestMsgViewWindow *self;
2354 ModestMailOperationTypeOperation op_type;
2356 ModestMsgViewWindowPrivate *priv;
2357 GObject *source = NULL;
2359 self = MODEST_MSG_VIEW_WINDOW (user_data);
2360 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
2361 op_type = modest_mail_operation_get_type_operation (mail_op);
2362 tmp = priv->progress_widgets;
2363 source = modest_mail_operation_get_source(mail_op);
2364 if (G_OBJECT (self) == source) {
2365 if (op_type == MODEST_MAIL_OPERATION_TYPE_RECEIVE || op_type == MODEST_MAIL_OPERATION_TYPE_OPEN ) {
2366 set_toolbar_transfer_mode(self);
2368 modest_progress_object_add_operation (
2369 MODEST_PROGRESS_OBJECT (tmp->data),
2371 tmp = g_slist_next (tmp);
2375 g_object_unref (source);
2379 on_mail_operation_finished (ModestMailOperation *mail_op,
2382 ModestMsgViewWindow *self;
2383 ModestMailOperationTypeOperation op_type;
2385 ModestMsgViewWindowPrivate *priv;
2387 self = MODEST_MSG_VIEW_WINDOW (user_data);
2388 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
2389 op_type = modest_mail_operation_get_type_operation (mail_op);
2390 tmp = priv->progress_widgets;
2392 if (op_type == MODEST_MAIL_OPERATION_TYPE_RECEIVE || op_type == MODEST_MAIL_OPERATION_TYPE_OPEN ) {
2394 modest_progress_object_remove_operation (MODEST_PROGRESS_OBJECT (tmp->data),
2396 tmp = g_slist_next (tmp);
2399 /* If no more operations are being observed, NORMAL mode is enabled again */
2400 if (observers_empty (self)) {
2401 set_toolbar_mode (self, TOOLBAR_MODE_NORMAL);
2404 /* Update dimming rules. We have to do this right here
2405 and not in view_msg_cb because at that point the
2406 transfer mode is still enabled so the dimming rule
2407 won't let the user delete the message that has been
2408 readed for example */
2409 modest_ui_actions_check_toolbar_dimming_rules (MODEST_WINDOW (self));
2410 modest_ui_actions_check_menu_dimming_rules (MODEST_WINDOW (self));
2415 on_queue_changed (ModestMailOperationQueue *queue,
2416 ModestMailOperation *mail_op,
2417 ModestMailOperationQueueNotification type,
2418 ModestMsgViewWindow *self)
2420 ModestMsgViewWindowPrivate *priv;
2422 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
2424 /* If this operations was created by another window, do nothing */
2425 if (!modest_mail_operation_is_mine (mail_op, G_OBJECT(self)))
2428 if (type == MODEST_MAIL_OPERATION_QUEUE_OPERATION_ADDED) {
2429 priv->sighandlers = modest_signal_mgr_connect (priv->sighandlers,
2431 "operation-started",
2432 G_CALLBACK (on_mail_operation_started),
2434 priv->sighandlers = modest_signal_mgr_connect (priv->sighandlers,
2436 "operation-finished",
2437 G_CALLBACK (on_mail_operation_finished),
2439 } else if (type == MODEST_MAIL_OPERATION_QUEUE_OPERATION_REMOVED) {
2440 priv->sighandlers = modest_signal_mgr_disconnect (priv->sighandlers,
2442 "operation-started");
2443 priv->sighandlers = modest_signal_mgr_disconnect (priv->sighandlers,
2445 "operation-finished");
2450 modest_msg_view_window_get_attachments (ModestMsgViewWindow *win)
2452 ModestMsgViewWindowPrivate *priv;
2453 TnyList *selected_attachments = NULL;
2455 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (win), NULL);
2456 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (win);
2458 selected_attachments = modest_msg_view_get_selected_attachments (MODEST_MSG_VIEW (priv->msg_view));
2460 return selected_attachments;
2466 guint banner_idle_id;
2467 } DecodeAsyncHelper;
2470 decode_async_banner_idle (gpointer user_data)
2472 DecodeAsyncHelper *helper = (DecodeAsyncHelper *) user_data;
2474 helper->banner_idle_id = 0;
2475 helper->banner = hildon_banner_show_animation (NULL, NULL, _("mail_me_opening"));
2476 g_object_ref (helper->banner);
2482 on_decode_to_stream_async_handler (TnyMimePart *mime_part,
2488 DecodeAsyncHelper *helper = (DecodeAsyncHelper *) user_data;
2490 if (helper->banner_idle_id > 0) {
2491 g_source_remove (helper->banner_idle_id);
2492 helper->banner_idle_id = 0;
2494 if (helper->banner) {
2495 gtk_widget_destroy (helper->banner);
2497 if (cancelled || err) {
2498 modest_platform_information_banner (NULL, NULL,
2499 _("mail_ib_file_operation_failed"));
2503 /* make the file read-only */
2504 g_chmod(helper->filepath, 0444);
2506 /* Activate the file */
2507 modest_platform_activate_file (helper->filepath, tny_mime_part_get_content_type (mime_part));
2511 g_free (helper->filepath);
2512 g_object_unref (helper->banner);
2513 g_slice_free (DecodeAsyncHelper, helper);
2517 modest_msg_view_window_view_attachment (ModestMsgViewWindow *window, TnyMimePart *mime_part)
2519 ModestMsgViewWindowPrivate *priv;
2520 const gchar *msg_uid;
2521 gchar *attachment_uid = NULL;
2522 gint attachment_index = 0;
2523 TnyList *attachments;
2525 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window));
2526 g_return_if_fail (TNY_IS_MIME_PART (mime_part) || (mime_part == NULL));
2527 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
2529 msg_uid = modest_msg_view_window_get_message_uid (MODEST_MSG_VIEW_WINDOW (window));
2530 attachments = modest_msg_view_get_attachments (MODEST_MSG_VIEW (priv->msg_view));
2531 attachment_index = modest_list_index (attachments, (GObject *) mime_part);
2532 g_object_unref (attachments);
2534 if (msg_uid && attachment_index >= 0) {
2535 attachment_uid = g_strdup_printf ("%s/%d", msg_uid, attachment_index);
2538 if (mime_part == NULL) {
2539 gboolean error = FALSE;
2540 TnyList *selected_attachments = modest_msg_view_get_selected_attachments (MODEST_MSG_VIEW (priv->msg_view));
2541 if (selected_attachments == NULL || tny_list_get_length (selected_attachments) == 0) {
2543 } else if (tny_list_get_length (selected_attachments) > 1) {
2544 hildon_banner_show_information (NULL, NULL, _("mcen_ib_unable_to_display_more"));
2548 iter = tny_list_create_iterator (selected_attachments);
2549 mime_part = (TnyMimePart *) tny_iterator_get_current (iter);
2550 g_object_unref (iter);
2552 g_object_unref (selected_attachments);
2557 g_object_ref (mime_part);
2560 if (tny_mime_part_is_purged (mime_part)) {
2561 g_object_unref (mime_part);
2565 if (!modest_tny_mime_part_is_msg (mime_part)) {
2566 gchar *filepath = NULL;
2567 const gchar *att_filename = tny_mime_part_get_filename (mime_part);
2568 gboolean show_error_banner = FALSE;
2569 TnyFsStream *temp_stream = NULL;
2570 temp_stream = modest_utils_create_temp_stream (att_filename, attachment_uid,
2573 if (temp_stream != NULL) {
2574 DecodeAsyncHelper *helper = g_slice_new (DecodeAsyncHelper);
2575 helper->filepath = g_strdup (filepath);
2576 helper->banner = NULL;
2577 helper->banner_idle_id = g_timeout_add (1000, decode_async_banner_idle, helper);
2578 tny_mime_part_decode_to_stream_async (mime_part, TNY_STREAM (temp_stream),
2579 on_decode_to_stream_async_handler,
2582 g_object_unref (temp_stream);
2583 /* NOTE: files in the temporary area will be automatically
2584 * cleaned after some time if they are no longer in use */
2586 if (filepath != NULL) {
2587 const gchar *content_type;
2588 /* the file may already exist but it isn't writable,
2589 * let's try to open it anyway */
2590 content_type = tny_mime_part_get_content_type (mime_part);
2591 modest_platform_activate_file (filepath, content_type);
2594 g_warning ("%s: modest_utils_create_temp_stream failed", __FUNCTION__);
2595 show_error_banner = TRUE;
2598 if (show_error_banner)
2599 modest_platform_information_banner (NULL, NULL, _("mail_ib_file_operation_failed"));
2601 /* message attachment */
2602 TnyHeader *header = NULL;
2603 ModestWindowMgr *mgr;
2604 ModestWindow *msg_win = NULL;
2607 header = tny_msg_get_header (TNY_MSG (mime_part));
2608 mgr = modest_runtime_get_window_mgr ();
2609 found = modest_window_mgr_find_registered_header (mgr, header, &msg_win);
2612 if (msg_win) /* there is already a window for this uid; top it */
2613 gtk_window_present (GTK_WINDOW(msg_win));
2615 /* if it's found, but there is no msg_win, it's probably in the process of being created;
2616 * thus, we don't do anything */
2617 g_warning ("window for is already being created");
2619 /* it's not found, so create a new window for it */
2620 modest_window_mgr_register_header (mgr, header, attachment_uid); /* register the uid before building the window */
2621 gchar *account = g_strdup (modest_window_get_active_account (MODEST_WINDOW (window)));
2623 account = modest_account_mgr_get_default_account (modest_runtime_get_account_mgr ());
2624 msg_win = modest_msg_view_window_new_for_attachment (TNY_MSG (mime_part), account, attachment_uid);
2625 modest_window_set_zoom (MODEST_WINDOW (msg_win),
2626 modest_window_get_zoom (MODEST_WINDOW (window)));
2627 modest_window_mgr_register_window (mgr, msg_win);
2628 gtk_widget_show_all (GTK_WIDGET (msg_win));
2631 g_object_unref (mime_part);
2644 GnomeVFSResult result;
2647 static void save_mime_part_info_free (SaveMimePartInfo *info, gboolean with_struct);
2648 static gboolean idle_save_mime_part_show_result (SaveMimePartInfo *info);
2649 static gpointer save_mime_part_to_file (SaveMimePartInfo *info);
2650 static void save_mime_parts_to_file_with_checks (SaveMimePartInfo *info);
2653 save_mime_part_info_free (SaveMimePartInfo *info, gboolean with_struct)
2657 for (node = info->pairs; node != NULL; node = g_list_next (node)) {
2658 SaveMimePartPair *pair = (SaveMimePartPair *) node->data;
2659 g_free (pair->filename);
2660 g_object_unref (pair->part);
2661 g_slice_free (SaveMimePartPair, pair);
2663 g_list_free (info->pairs);
2666 gtk_widget_destroy (info->banner);
2667 g_slice_free (SaveMimePartInfo, info);
2672 idle_save_mime_part_show_result (SaveMimePartInfo *info)
2674 if (info->pairs != NULL) {
2675 save_mime_part_to_file (info);
2677 /* This is a GDK lock because we are an idle callback and
2678 * hildon_banner_show_information is or does Gtk+ code */
2680 gdk_threads_enter (); /* CHECKED */
2681 save_mime_part_info_free (info, TRUE);
2682 if (info->result == GNOME_VFS_OK) {
2683 hildon_banner_show_information (NULL, NULL, _CS("sfil_ib_saved"));
2684 } else if (info->result == GNOME_VFS_ERROR_NO_SPACE) {
2685 hildon_banner_show_information (NULL, NULL, dgettext("ke-recv",
2686 "cerm_device_memory_full"));
2688 hildon_banner_show_information (NULL, NULL, _("mail_ib_file_operation_failed"));
2690 gdk_threads_leave (); /* CHECKED */
2697 save_mime_part_to_file (SaveMimePartInfo *info)
2699 GnomeVFSHandle *handle;
2701 SaveMimePartPair *pair = (SaveMimePartPair *) info->pairs->data;
2703 info->result = gnome_vfs_create (&handle, pair->filename, GNOME_VFS_OPEN_WRITE, FALSE, 0644);
2704 if (info->result == GNOME_VFS_OK) {
2705 GError *error = NULL;
2706 stream = tny_vfs_stream_new (handle);
2707 if (tny_mime_part_decode_to_stream (pair->part, stream, &error) < 0) {
2708 g_warning ("modest: could not save attachment %s: %d (%s)\n", pair->filename, error?error->code:-1, error?error->message:"Unknown error");
2710 info->result = GNOME_VFS_ERROR_IO;
2712 g_object_unref (G_OBJECT (stream));
2713 g_object_unref (pair->part);
2714 g_slice_free (SaveMimePartPair, pair);
2715 info->pairs = g_list_delete_link (info->pairs, info->pairs);
2717 g_warning ("modest: could not create save attachment %s: %s\n", pair->filename, gnome_vfs_result_to_string (info->result));
2718 save_mime_part_info_free (info, FALSE);
2721 g_idle_add ((GSourceFunc) idle_save_mime_part_show_result, info);
2726 save_mime_parts_to_file_with_checks (SaveMimePartInfo *info)
2728 gboolean is_ok = TRUE;
2729 gint replaced_files = 0;
2730 const GList *files = info->pairs;
2733 for (iter = files; (iter != NULL) && (replaced_files < 2); iter = g_list_next(iter)) {
2734 SaveMimePartPair *pair = iter->data;
2735 if (modest_utils_file_exists (pair->filename)) {
2739 if (replaced_files) {
2740 GtkWidget *confirm_overwrite_dialog;
2741 const gchar *message = (replaced_files == 1) ?
2742 _FM("docm_nc_replace_file") : _FM("docm_nc_replace_multiple");
2743 confirm_overwrite_dialog = hildon_note_new_confirmation (NULL, message);
2744 if (gtk_dialog_run (GTK_DIALOG (confirm_overwrite_dialog)) != GTK_RESPONSE_OK) {
2747 gtk_widget_destroy (confirm_overwrite_dialog);
2751 save_mime_part_info_free (info, TRUE);
2753 GtkWidget *banner = hildon_banner_show_animation (NULL, NULL,
2754 _CS("sfil_ib_saving"));
2755 info->banner = banner;
2756 g_thread_create ((GThreadFunc)save_mime_part_to_file, info, FALSE, NULL);
2762 modest_msg_view_window_save_attachments (ModestMsgViewWindow *window, TnyList *mime_parts)
2764 ModestMsgViewWindowPrivate *priv;
2765 GList *files_to_save = NULL;
2766 GtkWidget *save_dialog = NULL;
2767 gchar *folder = NULL;
2768 const gchar *filename = NULL;
2769 gchar *save_multiple_str = NULL;
2771 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window));
2772 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
2774 if (mime_parts == NULL) {
2775 mime_parts = modest_msg_view_get_selected_attachments (MODEST_MSG_VIEW (priv->msg_view));
2776 if (mime_parts == NULL || tny_list_get_length (mime_parts) == 0)
2779 g_object_ref (mime_parts);
2782 /* prepare dialog */
2783 if (tny_list_get_length (mime_parts) == 1) {
2785 /* only one attachment selected */
2786 iter = tny_list_create_iterator (mime_parts);
2787 TnyMimePart *mime_part = (TnyMimePart *) tny_iterator_get_current (iter);
2788 g_object_unref (iter);
2789 if (!modest_tny_mime_part_is_msg (mime_part) &&
2790 modest_tny_mime_part_is_attachment_for_modest (mime_part) &&
2791 !tny_mime_part_is_purged (mime_part)) {
2792 filename = tny_mime_part_get_filename (mime_part);
2794 /* TODO: show any error? */
2795 g_warning ("Tried to save a non-file attachment");
2796 g_object_unref (mime_parts);
2799 g_object_unref (mime_part);
2801 save_multiple_str = g_strdup_printf (_FM("sfil_va_number_of_objects_attachments"),
2802 tny_list_get_length (mime_parts));
2805 save_dialog = hildon_file_chooser_dialog_new (GTK_WINDOW (window),
2806 GTK_FILE_CHOOSER_ACTION_SAVE);
2809 folder = g_build_filename (g_get_home_dir (), DEFAULT_FOLDER, NULL);
2810 gtk_file_chooser_set_current_folder (GTK_FILE_CHOOSER (save_dialog), folder);
2814 if (filename != NULL)
2815 gtk_file_chooser_set_current_name (GTK_FILE_CHOOSER (save_dialog),
2818 /* if multiple, set multiple string */
2819 if (save_multiple_str) {
2820 g_object_set (G_OBJECT (save_dialog), "save-multiple", save_multiple_str, NULL);
2821 gtk_window_set_title (GTK_WINDOW (save_dialog), _FM("sfil_ti_save_objects_files"));
2825 if (gtk_dialog_run (GTK_DIALOG (save_dialog)) == GTK_RESPONSE_OK) {
2826 gchar *chooser_uri = gtk_file_chooser_get_uri (GTK_FILE_CHOOSER (save_dialog));
2828 if (!modest_utils_folder_writable (chooser_uri)) {
2829 hildon_banner_show_information
2830 (NULL, NULL, dgettext("hildon-fm", "sfil_ib_readonly_location"));
2834 iter = tny_list_create_iterator (mime_parts);
2835 while (!tny_iterator_is_done (iter)) {
2836 TnyMimePart *mime_part = (TnyMimePart *) tny_iterator_get_current (iter);
2838 if ((modest_tny_mime_part_is_attachment_for_modest (mime_part)) &&
2839 !tny_mime_part_is_purged (mime_part) &&
2840 (tny_mime_part_get_filename (mime_part) != NULL)) {
2841 SaveMimePartPair *pair;
2843 pair = g_slice_new0 (SaveMimePartPair);
2844 if (save_multiple_str) {
2845 gchar *escaped = gnome_vfs_escape_slashes (
2846 tny_mime_part_get_filename (mime_part));
2847 pair->filename = g_build_filename (chooser_uri, escaped, NULL);
2850 pair->filename = g_strdup (chooser_uri);
2852 pair->part = mime_part;
2853 files_to_save = g_list_prepend (files_to_save, pair);
2855 tny_iterator_next (iter);
2857 g_object_unref (iter);
2859 g_free (chooser_uri);
2862 gtk_widget_destroy (save_dialog);
2864 g_object_unref (mime_parts);
2866 if (files_to_save != NULL) {
2867 SaveMimePartInfo *info = g_slice_new0 (SaveMimePartInfo);
2868 info->pairs = files_to_save;
2869 info->result = TRUE;
2870 save_mime_parts_to_file_with_checks (info);
2875 show_remove_attachment_information (gpointer userdata)
2877 ModestMsgViewWindow *window = (ModestMsgViewWindow *) userdata;
2878 ModestMsgViewWindowPrivate *priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
2880 /* We're outside the main lock */
2881 gdk_threads_enter ();
2883 if (priv->remove_attachment_banner != NULL) {
2884 gtk_widget_destroy (priv->remove_attachment_banner);
2885 g_object_unref (priv->remove_attachment_banner);
2888 priv->remove_attachment_banner = g_object_ref (
2889 hildon_banner_show_animation (NULL, NULL, _("mcen_ib_removing_attachment")));
2891 gdk_threads_leave ();
2897 modest_msg_view_window_remove_attachments (ModestMsgViewWindow *window, gboolean get_all)
2899 ModestMsgViewWindowPrivate *priv;
2900 TnyList *mime_parts = NULL;
2901 gchar *confirmation_message;
2907 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window));
2908 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
2911 mime_parts = modest_msg_view_get_attachments (MODEST_MSG_VIEW (priv->msg_view));
2913 mime_parts = modest_msg_view_get_selected_attachments (MODEST_MSG_VIEW (priv->msg_view));
2915 /* Remove already purged messages from mime parts list */
2916 iter = tny_list_create_iterator (mime_parts);
2917 while (!tny_iterator_is_done (iter)) {
2918 TnyMimePart *part = TNY_MIME_PART (tny_iterator_get_current (iter));
2919 tny_iterator_next (iter);
2920 if (tny_mime_part_is_purged (part)) {
2921 tny_list_remove (mime_parts, (GObject *) part);
2923 g_object_unref (part);
2925 g_object_unref (iter);
2927 if (tny_list_get_length (mime_parts) == 0) {
2928 g_object_unref (mime_parts);
2932 n_attachments = tny_list_get_length (mime_parts);
2933 if (n_attachments == 1) {
2937 iter = tny_list_create_iterator (mime_parts);
2938 part = (TnyMimePart *) tny_iterator_get_current (iter);
2939 g_object_unref (iter);
2940 if (modest_tny_mime_part_is_msg (part)) {
2942 header = tny_msg_get_header (TNY_MSG (part));
2943 filename = tny_header_dup_subject (header);
2944 g_object_unref (header);
2945 if (filename == NULL)
2946 filename = g_strdup (_("mail_va_no_subject"));
2948 filename = g_strdup (tny_mime_part_get_filename (TNY_MIME_PART (part)));
2950 confirmation_message = g_strdup_printf (_("mcen_nc_purge_file_text"), filename);
2952 g_object_unref (part);
2954 confirmation_message = g_strdup_printf (ngettext("mcen_nc_purge_file_text",
2955 "mcen_nc_purge_files_text",
2956 n_attachments), n_attachments);
2958 response = modest_platform_run_confirmation_dialog (GTK_WINDOW (window),
2959 confirmation_message);
2960 g_free (confirmation_message);
2962 if (response != GTK_RESPONSE_OK) {
2963 g_object_unref (mime_parts);
2967 priv->purge_timeout = g_timeout_add (2000, show_remove_attachment_information, window);
2969 iter = tny_list_create_iterator (mime_parts);
2970 while (!tny_iterator_is_done (iter)) {
2973 part = (TnyMimePart *) tny_iterator_get_current (iter);
2974 tny_mime_part_set_purged (TNY_MIME_PART (part));
2975 g_object_unref (part);
2976 tny_iterator_next (iter);
2978 g_object_unref (iter);
2980 msg = tny_msg_view_get_msg (TNY_MSG_VIEW (priv->msg_view));
2981 tny_msg_view_clear (TNY_MSG_VIEW (priv->msg_view));
2982 tny_msg_rewrite_cache (msg);
2983 tny_msg_view_set_msg (TNY_MSG_VIEW (priv->msg_view), msg);
2984 g_object_unref (msg);
2986 g_object_unref (mime_parts);
2988 if (priv->purge_timeout > 0) {
2989 g_source_remove (priv->purge_timeout);
2990 priv->purge_timeout = 0;
2993 if (priv->remove_attachment_banner) {
2994 gtk_widget_destroy (priv->remove_attachment_banner);
2995 g_object_unref (priv->remove_attachment_banner);
2996 priv->remove_attachment_banner = NULL;
3004 update_window_title (ModestMsgViewWindow *window)
3006 ModestMsgViewWindowPrivate *priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
3008 TnyHeader *header = NULL;
3009 gchar *subject = NULL;
3011 msg = tny_msg_view_get_msg (TNY_MSG_VIEW (priv->msg_view));
3014 header = tny_msg_get_header (msg);
3015 subject = tny_header_dup_subject (header);
3016 g_object_unref (header);
3017 g_object_unref (msg);
3020 if ((subject == NULL)||(subject[0] == '\0')) {
3022 subject = g_strdup (_("mail_va_no_subject"));
3025 gtk_window_set_title (GTK_WINDOW (window), subject);
3029 static void on_move_focus (GtkWidget *widget,
3030 GtkDirectionType direction,
3033 g_signal_stop_emission_by_name (G_OBJECT (widget), "move-focus");