1 /* Copyright (c) 2006, Nokia Corporation
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions are
8 * * Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 * * Redistributions in binary form must reproduce the above copyright
11 * notice, this list of conditions and the following disclaimer in the
12 * documentation and/or other materials provided with the distribution.
13 * * Neither the name of the Nokia Corporation nor the names of its
14 * contributors may be used to endorse or promote products derived from
15 * this software without specific prior written permission.
17 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
18 * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
19 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
20 * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
21 * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
22 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
23 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
24 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
25 * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
26 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
27 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29 #include <glib/gi18n.h>
31 #include <tny-account-store.h>
32 #include <tny-simple-list.h>
34 #include <tny-mime-part.h>
35 #include <tny-vfs-stream.h>
36 #include "modest-marshal.h"
37 #include "modest-platform.h"
38 #include <modest-utils.h>
39 #include <modest-maemo-utils.h>
40 #include <modest-tny-msg.h>
41 #include <modest-msg-view-window.h>
42 #include <modest-main-window-ui.h>
43 #include "modest-msg-view-window-ui-dimming.h"
44 #include <modest-widget-memory.h>
45 #include <modest-runtime.h>
46 #include <modest-window-priv.h>
47 #include <modest-tny-folder.h>
48 #include <modest-text-utils.h>
49 #include <modest-account-mgr-helpers.h>
50 #include "modest-progress-bar.h"
51 #include "modest-defs.h"
52 #include "modest-hildon-includes.h"
53 #include "modest-ui-dimming-manager.h"
54 #include <gdk/gdkkeysyms.h>
55 #include <modest-tny-account.h>
56 #include <modest-mime-part-view.h>
57 #include <modest-isearch-view.h>
58 #include <modest-tny-mime-part.h>
61 #include <glib/gstdio.h>
62 #include <modest-debug.h>
64 #define DEFAULT_FOLDER "MyDocs/.documents"
66 static void modest_msg_view_window_class_init (ModestMsgViewWindowClass *klass);
67 static void modest_msg_view_window_init (ModestMsgViewWindow *obj);
68 static void modest_header_view_observer_init(
69 ModestHeaderViewObserverIface *iface_class);
70 static void modest_msg_view_window_finalize (GObject *obj);
71 static void modest_msg_view_window_toggle_find_toolbar (GtkToggleAction *obj,
73 static void modest_msg_view_window_find_toolbar_close (GtkWidget *widget,
74 ModestMsgViewWindow *obj);
75 static void modest_msg_view_window_find_toolbar_search (GtkWidget *widget,
76 ModestMsgViewWindow *obj);
78 static void modest_msg_view_window_disconnect_signals (ModestWindow *self);
79 static void modest_msg_view_window_set_zoom (ModestWindow *window,
81 static gdouble modest_msg_view_window_get_zoom (ModestWindow *window);
82 static gboolean modest_msg_view_window_zoom_minus (ModestWindow *window);
83 static gboolean modest_msg_view_window_zoom_plus (ModestWindow *window);
84 static gboolean modest_msg_view_window_key_event (GtkWidget *window,
87 static gboolean modest_msg_view_window_window_state_event (GtkWidget *widget,
88 GdkEventWindowState *event,
90 static void modest_msg_view_window_update_priority (ModestMsgViewWindow *window);
92 static void modest_msg_view_window_show_toolbar (ModestWindow *window,
93 gboolean show_toolbar);
95 static void modest_msg_view_window_clipboard_owner_change (GtkClipboard *clipboard,
97 ModestMsgViewWindow *window);
99 static void modest_msg_view_window_on_row_changed (GtkTreeModel *header_model,
102 ModestMsgViewWindow *window);
104 static void modest_msg_view_window_on_row_deleted (GtkTreeModel *header_model,
106 ModestMsgViewWindow *window);
108 static void modest_msg_view_window_on_row_inserted (GtkTreeModel *header_model,
109 GtkTreePath *tree_path,
110 GtkTreeIter *tree_iter,
111 ModestMsgViewWindow *window);
113 static void modest_msg_view_window_on_row_reordered (GtkTreeModel *header_model,
117 ModestMsgViewWindow *window);
119 static void modest_msg_view_window_update_model_replaced (ModestHeaderViewObserver *window,
121 const gchar *tny_folder_id);
123 static void cancel_progressbar (GtkToolButton *toolbutton,
124 ModestMsgViewWindow *self);
126 static void on_queue_changed (ModestMailOperationQueue *queue,
127 ModestMailOperation *mail_op,
128 ModestMailOperationQueueNotification type,
129 ModestMsgViewWindow *self);
131 static void on_account_removed (TnyAccountStore *account_store,
135 static void on_move_focus (GtkWidget *widget,
136 GtkDirectionType direction,
139 static void view_msg_cb (ModestMailOperation *mail_op,
146 static void set_toolbar_mode (ModestMsgViewWindow *self,
147 ModestToolBarModes mode);
149 static void update_window_title (ModestMsgViewWindow *window);
151 static gboolean set_toolbar_transfer_mode (ModestMsgViewWindow *self);
152 static void init_window (ModestMsgViewWindow *obj);
154 static gboolean msg_is_visible (TnyHeader *header, gboolean check_outbox);
156 static void check_dimming_rules_after_change (ModestMsgViewWindow *window);
158 static gboolean modest_msg_view_window_scroll_child (ModestMsgViewWindow *self,
159 GtkScrollType scroll_type,
163 /* list my signals */
170 static const GtkToggleActionEntry msg_view_toggle_action_entries [] = {
171 { "FindInMessage", MODEST_TOOLBAR_ICON_FIND, N_("qgn_toolb_gene_find"), NULL, NULL, G_CALLBACK (modest_msg_view_window_toggle_find_toolbar), FALSE },
172 { "ToolsFindInMessage", NULL, N_("mcen_me_viewer_find"), "<CTRL>F", NULL, G_CALLBACK (modest_msg_view_window_toggle_find_toolbar), FALSE },
175 static const GtkRadioActionEntry msg_view_zoom_action_entries [] = {
176 { "Zoom50", NULL, N_("mcen_me_viewer_50"), NULL, NULL, 50 },
177 { "Zoom80", NULL, N_("mcen_me_viewer_80"), NULL, NULL, 80 },
178 { "Zoom100", NULL, N_("mcen_me_viewer_100"), NULL, NULL, 100 },
179 { "Zoom120", NULL, N_("mcen_me_viewer_120"), NULL, NULL, 120 },
180 { "Zoom150", NULL, N_("mcen_me_viewer_150"), NULL, NULL, 150 },
181 { "Zoom200", NULL, N_("mcen_me_viewer_200"), NULL, NULL, 200 }
184 typedef struct _ModestMsgViewWindowPrivate ModestMsgViewWindowPrivate;
185 struct _ModestMsgViewWindowPrivate {
188 GtkWidget *main_scroll;
189 GtkWidget *find_toolbar;
192 /* Progress observers */
193 GtkWidget *progress_bar;
194 GSList *progress_widgets;
197 GtkWidget *progress_toolitem;
198 GtkWidget *cancel_toolitem;
199 GtkWidget *prev_toolitem;
200 GtkWidget *next_toolitem;
201 ModestToolBarModes current_toolbar_mode;
203 /* Optimized view enabled */
204 gboolean optimized_view;
206 /* Whether this was created via the *_new_for_search_result() function. */
207 gboolean is_search_result;
209 /* Whether the message is in outbox */
212 /* A reference to the @model of the header view
213 * to allow selecting previous/next messages,
214 * if the message is currently selected in the header view.
216 const gchar *header_folder_id;
217 GtkTreeModel *header_model;
218 GtkTreeRowReference *row_reference;
219 GtkTreeRowReference *next_row_reference;
221 gulong clipboard_change_handler;
222 gulong queue_change_handler;
223 gulong account_removed_handler;
224 gulong row_changed_handler;
225 gulong row_deleted_handler;
226 gulong row_inserted_handler;
227 gulong rows_reordered_handler;
230 GtkWidget *remove_attachment_banner;
232 guint progress_bar_timeout;
239 #define MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE((o), \
240 MODEST_TYPE_MSG_VIEW_WINDOW, \
241 ModestMsgViewWindowPrivate))
243 static GtkWindowClass *parent_class = NULL;
245 /* uncomment the following if you have defined any signals */
246 static guint signals[LAST_SIGNAL] = {0};
249 modest_msg_view_window_get_type (void)
251 static GType my_type = 0;
253 static const GTypeInfo my_info = {
254 sizeof(ModestMsgViewWindowClass),
255 NULL, /* base init */
256 NULL, /* base finalize */
257 (GClassInitFunc) modest_msg_view_window_class_init,
258 NULL, /* class finalize */
259 NULL, /* class data */
260 sizeof(ModestMsgViewWindow),
262 (GInstanceInitFunc) modest_msg_view_window_init,
265 my_type = g_type_register_static (MODEST_TYPE_WINDOW,
266 "ModestMsgViewWindow",
269 static const GInterfaceInfo modest_header_view_observer_info =
271 (GInterfaceInitFunc) modest_header_view_observer_init,
272 NULL, /* interface_finalize */
273 NULL /* interface_data */
276 g_type_add_interface_static (my_type,
277 MODEST_TYPE_HEADER_VIEW_OBSERVER,
278 &modest_header_view_observer_info);
284 save_state (ModestWindow *self)
286 modest_widget_memory_save (modest_runtime_get_conf (),
288 MODEST_CONF_MSG_VIEW_WINDOW_KEY);
293 restore_settings (ModestMsgViewWindow *self)
296 ModestWindowPrivate *parent_priv = MODEST_WINDOW_GET_PRIVATE (self);
299 conf = modest_runtime_get_conf ();
300 action = gtk_ui_manager_get_action (parent_priv->ui_manager,
301 "/MenuBar/ViewMenu/ViewShowToolbarMenu/ViewShowToolbarNormalScreenMenu");
302 gtk_toggle_action_set_active (GTK_TOGGLE_ACTION (action),
303 modest_conf_get_bool (conf, MODEST_CONF_MSG_VIEW_WINDOW_SHOW_TOOLBAR, NULL));
304 action = gtk_ui_manager_get_action (parent_priv->ui_manager,
305 "/MenuBar/ViewMenu/ViewShowToolbarMenu/ViewShowToolbarFullScreenMenu");
306 gtk_toggle_action_set_active (GTK_TOGGLE_ACTION (action),
307 modest_conf_get_bool (conf, MODEST_CONF_MSG_VIEW_WINDOW_SHOW_TOOLBAR_FULLSCREEN, NULL));
308 modest_widget_memory_restore (conf,
310 MODEST_CONF_MSG_VIEW_WINDOW_KEY);
313 static gboolean modest_msg_view_window_scroll_child (ModestMsgViewWindow *self,
314 GtkScrollType scroll_type,
318 ModestMsgViewWindowPrivate *priv;
319 gboolean return_value;
321 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(self);
322 g_signal_emit_by_name (priv->main_scroll, "scroll-child", scroll_type, horizontal, &return_value);
327 add_scroll_binding (GtkBindingSet *binding_set,
329 GtkScrollType scroll)
331 guint keypad_keyval = keyval - GDK_Left + GDK_KP_Left;
333 gtk_binding_entry_add_signal (binding_set, keyval, 0,
335 GTK_TYPE_SCROLL_TYPE, scroll,
336 G_TYPE_BOOLEAN, FALSE);
337 gtk_binding_entry_add_signal (binding_set, keypad_keyval, 0,
339 GTK_TYPE_SCROLL_TYPE, scroll,
340 G_TYPE_BOOLEAN, FALSE);
344 modest_msg_view_window_class_init (ModestMsgViewWindowClass *klass)
346 GObjectClass *gobject_class;
347 ModestWindowClass *modest_window_class;
348 GtkBindingSet *binding_set;
350 gobject_class = (GObjectClass*) klass;
351 modest_window_class = (ModestWindowClass *) klass;
353 parent_class = g_type_class_peek_parent (klass);
354 gobject_class->finalize = modest_msg_view_window_finalize;
356 modest_window_class->set_zoom_func = modest_msg_view_window_set_zoom;
357 modest_window_class->get_zoom_func = modest_msg_view_window_get_zoom;
358 modest_window_class->zoom_minus_func = modest_msg_view_window_zoom_minus;
359 modest_window_class->zoom_plus_func = modest_msg_view_window_zoom_plus;
360 modest_window_class->show_toolbar_func = modest_msg_view_window_show_toolbar;
361 modest_window_class->disconnect_signals_func = modest_msg_view_window_disconnect_signals;
363 modest_window_class->save_state_func = save_state;
365 klass->scroll_child = modest_msg_view_window_scroll_child;
367 signals[MSG_CHANGED_SIGNAL] =
368 g_signal_new ("msg-changed",
369 G_TYPE_FROM_CLASS (gobject_class),
371 G_STRUCT_OFFSET (ModestMsgViewWindowClass, msg_changed),
373 modest_marshal_VOID__POINTER_POINTER,
374 G_TYPE_NONE, 2, G_TYPE_POINTER, G_TYPE_POINTER);
376 signals[SCROLL_CHILD_SIGNAL] =
377 g_signal_new ("scroll-child",
378 G_TYPE_FROM_CLASS (gobject_class),
379 G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
380 G_STRUCT_OFFSET (ModestMsgViewWindowClass, scroll_child),
382 modest_marshal_BOOLEAN__ENUM_BOOLEAN,
383 G_TYPE_BOOLEAN, 2, GTK_TYPE_SCROLL_TYPE, G_TYPE_BOOLEAN);
385 binding_set = gtk_binding_set_by_class (klass);
386 add_scroll_binding (binding_set, GDK_Up, GTK_SCROLL_STEP_UP);
387 add_scroll_binding (binding_set, GDK_Down, GTK_SCROLL_STEP_DOWN);
388 add_scroll_binding (binding_set, GDK_Page_Up, GTK_SCROLL_PAGE_UP);
389 add_scroll_binding (binding_set, GDK_Page_Down, GTK_SCROLL_PAGE_DOWN);
390 add_scroll_binding (binding_set, GDK_Home, GTK_SCROLL_START);
391 add_scroll_binding (binding_set, GDK_End, GTK_SCROLL_END);
393 g_type_class_add_private (gobject_class, sizeof(ModestMsgViewWindowPrivate));
397 static void modest_header_view_observer_init(
398 ModestHeaderViewObserverIface *iface_class)
400 iface_class->update_func = modest_msg_view_window_update_model_replaced;
404 modest_msg_view_window_init (ModestMsgViewWindow *obj)
406 ModestMsgViewWindowPrivate *priv;
407 ModestWindowPrivate *parent_priv = NULL;
408 GtkActionGroup *action_group = NULL;
409 GError *error = NULL;
410 GdkPixbuf *window_icon;
412 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(obj);
413 parent_priv = MODEST_WINDOW_GET_PRIVATE(obj);
414 parent_priv->ui_manager = gtk_ui_manager_new();
416 action_group = gtk_action_group_new ("ModestMsgViewWindowActions");
417 gtk_action_group_set_translation_domain (action_group, GETTEXT_PACKAGE);
419 /* Add common actions */
420 gtk_action_group_add_actions (action_group,
421 modest_action_entries,
422 G_N_ELEMENTS (modest_action_entries),
424 gtk_action_group_add_toggle_actions (action_group,
425 modest_toggle_action_entries,
426 G_N_ELEMENTS (modest_toggle_action_entries),
428 gtk_action_group_add_toggle_actions (action_group,
429 msg_view_toggle_action_entries,
430 G_N_ELEMENTS (msg_view_toggle_action_entries),
432 gtk_action_group_add_radio_actions (action_group,
433 msg_view_zoom_action_entries,
434 G_N_ELEMENTS (msg_view_zoom_action_entries),
436 G_CALLBACK (modest_ui_actions_on_change_zoom),
439 gtk_ui_manager_insert_action_group (parent_priv->ui_manager, action_group, 0);
440 g_object_unref (action_group);
442 /* Load the UI definition */
443 gtk_ui_manager_add_ui_from_file (parent_priv->ui_manager, MODEST_UIDIR "modest-msg-view-window-ui.xml",
446 g_printerr ("modest: could not merge modest-msg-view-window-ui.xml: %s\n", error->message);
447 g_error_free (error);
452 /* Add accelerators */
453 gtk_window_add_accel_group (GTK_WINDOW (obj),
454 gtk_ui_manager_get_accel_group (parent_priv->ui_manager));
456 priv->is_search_result = FALSE;
457 priv->is_outbox = FALSE;
459 priv->msg_view = NULL;
460 priv->header_model = NULL;
461 priv->header_folder_id = NULL;
462 priv->clipboard_change_handler = 0;
463 priv->queue_change_handler = 0;
464 priv->account_removed_handler = 0;
465 priv->row_changed_handler = 0;
466 priv->row_deleted_handler = 0;
467 priv->row_inserted_handler = 0;
468 priv->rows_reordered_handler = 0;
469 priv->current_toolbar_mode = TOOLBAR_MODE_NORMAL;
471 priv->optimized_view = FALSE;
472 priv->progress_bar_timeout = 0;
473 priv->purge_timeout = 0;
474 priv->remove_attachment_banner = NULL;
475 priv->msg_uid = NULL;
477 priv->sighandlers = NULL;
480 init_window (MODEST_MSG_VIEW_WINDOW(obj));
482 /* Set window icon */
483 window_icon = modest_platform_get_icon (MODEST_APP_MSG_VIEW_ICON, MODEST_ICON_SIZE_BIG);
485 gtk_window_set_icon (GTK_WINDOW (obj), window_icon);
486 g_object_unref (window_icon);
489 hildon_program_add_window (hildon_program_get_instance(),
492 modest_window_mgr_register_help_id (modest_runtime_get_window_mgr(),
493 GTK_WINDOW(obj),"applications_email_viewer");
498 set_toolbar_transfer_mode (ModestMsgViewWindow *self)
500 ModestMsgViewWindowPrivate *priv = NULL;
502 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self), FALSE);
504 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(self);
506 set_toolbar_mode (self, TOOLBAR_MODE_TRANSFER);
508 if (priv->progress_bar_timeout > 0) {
509 g_source_remove (priv->progress_bar_timeout);
510 priv->progress_bar_timeout = 0;
517 set_toolbar_mode (ModestMsgViewWindow *self,
518 ModestToolBarModes mode)
520 ModestWindowPrivate *parent_priv;
521 ModestMsgViewWindowPrivate *priv;
522 /* GtkWidget *widget = NULL; */
524 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self));
526 parent_priv = MODEST_WINDOW_GET_PRIVATE(self);
527 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(self);
529 /* Sets current toolbar mode */
530 priv->current_toolbar_mode = mode;
532 /* Update toolbar dimming state */
533 modest_ui_actions_check_toolbar_dimming_rules (MODEST_WINDOW (self));
536 case TOOLBAR_MODE_NORMAL:
537 if (priv->progress_toolitem) {
538 gtk_tool_item_set_expand (GTK_TOOL_ITEM (priv->progress_toolitem), FALSE);
539 gtk_widget_hide (priv->progress_toolitem);
542 if (priv->progress_bar)
543 gtk_widget_hide (priv->progress_bar);
545 if (priv->cancel_toolitem)
546 gtk_widget_hide (priv->cancel_toolitem);
548 if (priv->prev_toolitem)
549 gtk_widget_show (priv->prev_toolitem);
551 if (priv->next_toolitem)
552 gtk_widget_show (priv->next_toolitem);
554 /* Hide toolbar if optimized view is enabled */
555 if (priv->optimized_view) {
556 gtk_widget_set_no_show_all (parent_priv->toolbar, TRUE);
557 gtk_widget_hide (GTK_WIDGET(parent_priv->toolbar));
561 case TOOLBAR_MODE_TRANSFER:
562 if (priv->prev_toolitem)
563 gtk_widget_hide (priv->prev_toolitem);
565 if (priv->next_toolitem)
566 gtk_widget_hide (priv->next_toolitem);
568 if (priv->progress_bar)
569 gtk_widget_show (priv->progress_bar);
571 if (priv->progress_toolitem) {
572 gtk_tool_item_set_expand (GTK_TOOL_ITEM (priv->progress_toolitem), TRUE);
573 gtk_widget_show (priv->progress_toolitem);
576 if (priv->cancel_toolitem)
577 gtk_widget_show (priv->cancel_toolitem);
579 /* Show toolbar if it's hiden (optimized view ) */
580 if (priv->optimized_view) {
581 gtk_widget_set_no_show_all (parent_priv->toolbar, FALSE);
582 gtk_widget_show (GTK_WIDGET (parent_priv->toolbar));
587 g_return_if_reached ();
594 init_window (ModestMsgViewWindow *obj)
596 GtkWidget *main_vbox;
597 ModestMsgViewWindowPrivate *priv;
599 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(obj);
601 priv->msg_view = GTK_WIDGET (tny_platform_factory_new_msg_view (modest_tny_platform_factory_get_instance ()));
602 modest_msg_view_set_shadow_type (MODEST_MSG_VIEW (priv->msg_view), GTK_SHADOW_NONE);
603 main_vbox = gtk_vbox_new (FALSE, 6);
605 #ifdef MODEST_USE_MOZEMBED
606 priv->main_scroll = priv->msg_view;
607 gtk_widget_set_size_request (priv->msg_view, -1, 1600);
609 priv->main_scroll = gtk_scrolled_window_new (NULL, NULL);
610 gtk_container_add (GTK_CONTAINER (priv->main_scroll), priv->msg_view);
612 gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (priv->main_scroll), GTK_POLICY_NEVER, GTK_POLICY_AUTOMATIC);
613 gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW (priv->main_scroll), GTK_SHADOW_NONE);
614 modest_maemo_set_thumbable_scrollbar (GTK_SCROLLED_WINDOW(priv->main_scroll), TRUE);
616 gtk_box_pack_start (GTK_BOX(main_vbox), priv->main_scroll, TRUE, TRUE, 0);
617 gtk_container_add (GTK_CONTAINER(obj), main_vbox);
619 priv->find_toolbar = hildon_find_toolbar_new (NULL);
620 hildon_window_add_toolbar (HILDON_WINDOW (obj), GTK_TOOLBAR (priv->find_toolbar));
621 gtk_widget_set_no_show_all (priv->find_toolbar, TRUE);
623 gtk_widget_show_all (GTK_WIDGET(main_vbox));
627 modest_msg_view_window_disconnect_signals (ModestWindow *self)
629 ModestMsgViewWindowPrivate *priv;
630 ModestHeaderView *header_view = NULL;
631 ModestWindow *main_window = NULL;
633 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
635 if (gtk_clipboard_get (GDK_SELECTION_PRIMARY) &&
636 g_signal_handler_is_connected (gtk_clipboard_get (GDK_SELECTION_PRIMARY),
637 priv->clipboard_change_handler))
638 g_signal_handler_disconnect (gtk_clipboard_get (GDK_SELECTION_PRIMARY),
639 priv->clipboard_change_handler);
641 if (g_signal_handler_is_connected (G_OBJECT (modest_runtime_get_mail_operation_queue ()),
642 priv->queue_change_handler))
643 g_signal_handler_disconnect (G_OBJECT (modest_runtime_get_mail_operation_queue ()),
644 priv->queue_change_handler);
646 if (g_signal_handler_is_connected (G_OBJECT (modest_runtime_get_account_store ()),
647 priv->account_removed_handler))
648 g_signal_handler_disconnect (G_OBJECT (modest_runtime_get_account_store ()),
649 priv->account_removed_handler);
651 if (priv->header_model) {
652 if (g_signal_handler_is_connected(G_OBJECT (priv->header_model),
653 priv->row_changed_handler))
654 g_signal_handler_disconnect(G_OBJECT (priv->header_model),
655 priv->row_changed_handler);
657 if (g_signal_handler_is_connected(G_OBJECT (priv->header_model),
658 priv->row_deleted_handler))
659 g_signal_handler_disconnect(G_OBJECT (priv->header_model),
660 priv->row_deleted_handler);
662 if (g_signal_handler_is_connected(G_OBJECT (priv->header_model),
663 priv->row_inserted_handler))
664 g_signal_handler_disconnect(G_OBJECT (priv->header_model),
665 priv->row_inserted_handler);
667 if (g_signal_handler_is_connected(G_OBJECT (priv->header_model),
668 priv->rows_reordered_handler))
669 g_signal_handler_disconnect(G_OBJECT (priv->header_model),
670 priv->rows_reordered_handler);
673 modest_signal_mgr_disconnect_all_and_destroy (priv->sighandlers);
674 priv->sighandlers = NULL;
676 main_window = modest_window_mgr_get_main_window (modest_runtime_get_window_mgr(),
677 FALSE); /* don't create */
681 header_view = MODEST_HEADER_VIEW(
682 modest_main_window_get_child_widget(
683 MODEST_MAIN_WINDOW(main_window),
684 MODEST_MAIN_WINDOW_WIDGET_TYPE_HEADER_VIEW));
685 if (header_view == NULL)
688 modest_header_view_remove_observer(header_view,
689 MODEST_HEADER_VIEW_OBSERVER(self));
693 modest_msg_view_window_finalize (GObject *obj)
695 ModestMsgViewWindowPrivate *priv;
697 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (obj);
699 /* Sanity check: shouldn't be needed, the window mgr should
700 call this function before */
701 modest_msg_view_window_disconnect_signals (MODEST_WINDOW (obj));
703 if (priv->header_model != NULL) {
704 g_object_unref (priv->header_model);
705 priv->header_model = NULL;
708 if (priv->progress_bar_timeout > 0) {
709 g_source_remove (priv->progress_bar_timeout);
710 priv->progress_bar_timeout = 0;
713 if (priv->remove_attachment_banner) {
714 gtk_widget_destroy (priv->remove_attachment_banner);
715 g_object_unref (priv->remove_attachment_banner);
716 priv->remove_attachment_banner = NULL;
719 if (priv->purge_timeout > 0) {
720 g_source_remove (priv->purge_timeout);
721 priv->purge_timeout = 0;
724 if (priv->row_reference) {
725 gtk_tree_row_reference_free (priv->row_reference);
726 priv->row_reference = NULL;
729 if (priv->next_row_reference) {
730 gtk_tree_row_reference_free (priv->next_row_reference);
731 priv->next_row_reference = NULL;
735 g_free (priv->msg_uid);
736 priv->msg_uid = NULL;
739 G_OBJECT_CLASS(parent_class)->finalize (obj);
743 select_next_valid_row (GtkTreeModel *model,
744 GtkTreeRowReference **row_reference,
748 GtkTreeIter tmp_iter;
750 GtkTreePath *next = NULL;
751 gboolean retval = FALSE, finished;
753 g_return_val_if_fail (gtk_tree_row_reference_valid (*row_reference), FALSE);
755 path = gtk_tree_row_reference_get_path (*row_reference);
756 gtk_tree_model_get_iter (model, &tmp_iter, path);
757 gtk_tree_row_reference_free (*row_reference);
758 *row_reference = NULL;
762 TnyHeader *header = NULL;
764 if (gtk_tree_model_iter_next (model, &tmp_iter)) {
765 gtk_tree_model_get (model, &tmp_iter,
766 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
770 if (msg_is_visible (header, is_outbox)) {
771 next = gtk_tree_model_get_path (model, &tmp_iter);
772 *row_reference = gtk_tree_row_reference_new (model, next);
773 gtk_tree_path_free (next);
777 g_object_unref (header);
780 } else if (cycle && gtk_tree_model_get_iter_first (model, &tmp_iter)) {
781 next = gtk_tree_model_get_path (model, &tmp_iter);
783 /* Ensure that we are not selecting the same */
784 if (gtk_tree_path_compare (path, next) != 0) {
785 gtk_tree_model_get (model, &tmp_iter,
786 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
789 if (msg_is_visible (header, is_outbox)) {
790 *row_reference = gtk_tree_row_reference_new (model, next);
794 g_object_unref (header);
798 /* If we ended up in the same message
799 then there is no valid next
803 gtk_tree_path_free (next);
805 /* If there are no more messages and we don't
806 want to start again in the first one then
807 there is no valid next message */
813 gtk_tree_path_free (path);
818 /* TODO: This should be in _init(), with the parameters as properties. */
820 modest_msg_view_window_construct (ModestMsgViewWindow *self,
821 const gchar *modest_account_name,
822 const gchar *msg_uid)
825 ModestMsgViewWindowPrivate *priv = NULL;
826 ModestWindowPrivate *parent_priv = NULL;
827 ModestDimmingRulesGroup *menu_rules_group = NULL;
828 ModestDimmingRulesGroup *toolbar_rules_group = NULL;
829 ModestDimmingRulesGroup *clipboard_rules_group = NULL;
831 obj = G_OBJECT (self);
832 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(obj);
833 parent_priv = MODEST_WINDOW_GET_PRIVATE(obj);
835 priv->msg_uid = g_strdup (msg_uid);
838 parent_priv->menubar = modest_maemo_utils_get_manager_menubar_as_menu (parent_priv->ui_manager, "/MenuBar");
839 hildon_window_set_menu (HILDON_WINDOW(obj), GTK_MENU(parent_priv->menubar));
840 gtk_widget_show (parent_priv->menubar);
841 parent_priv->ui_dimming_manager = modest_ui_dimming_manager_new();
843 menu_rules_group = modest_dimming_rules_group_new (MODEST_DIMMING_RULES_MENU, FALSE);
844 toolbar_rules_group = modest_dimming_rules_group_new (MODEST_DIMMING_RULES_TOOLBAR, TRUE);
845 clipboard_rules_group = modest_dimming_rules_group_new (MODEST_DIMMING_RULES_CLIPBOARD, FALSE);
847 /* Add common dimming rules */
848 modest_dimming_rules_group_add_rules (menu_rules_group,
849 modest_msg_view_menu_dimming_entries,
850 G_N_ELEMENTS (modest_msg_view_menu_dimming_entries),
851 MODEST_WINDOW (self));
852 modest_dimming_rules_group_add_rules (toolbar_rules_group,
853 modest_msg_view_toolbar_dimming_entries,
854 G_N_ELEMENTS (modest_msg_view_toolbar_dimming_entries),
855 MODEST_WINDOW (self));
856 modest_dimming_rules_group_add_rules (clipboard_rules_group,
857 modest_msg_view_clipboard_dimming_entries,
858 G_N_ELEMENTS (modest_msg_view_clipboard_dimming_entries),
859 MODEST_WINDOW (self));
861 /* Insert dimming rules group for this window */
862 modest_ui_dimming_manager_insert_rules_group (parent_priv->ui_dimming_manager, menu_rules_group);
863 modest_ui_dimming_manager_insert_rules_group (parent_priv->ui_dimming_manager, toolbar_rules_group);
864 modest_ui_dimming_manager_insert_rules_group (parent_priv->ui_dimming_manager, clipboard_rules_group);
865 g_object_unref (menu_rules_group);
866 g_object_unref (toolbar_rules_group);
867 g_object_unref (clipboard_rules_group);
869 restore_settings (MODEST_MSG_VIEW_WINDOW(obj));
871 /* g_signal_connect (G_OBJECT(obj), "delete-event", G_CALLBACK(on_delete_event), obj); */
873 priv->clipboard_change_handler = g_signal_connect (G_OBJECT (gtk_clipboard_get (GDK_SELECTION_PRIMARY)), "owner-change", G_CALLBACK (modest_msg_view_window_clipboard_owner_change), obj);
874 g_signal_connect (G_OBJECT(priv->msg_view), "activate_link",
875 G_CALLBACK (modest_ui_actions_on_msg_link_clicked), obj);
876 g_signal_connect (G_OBJECT(priv->msg_view), "link_hover",
877 G_CALLBACK (modest_ui_actions_on_msg_link_hover), obj);
878 g_signal_connect (G_OBJECT(priv->msg_view), "attachment_clicked",
879 G_CALLBACK (modest_ui_actions_on_msg_attachment_clicked), obj);
880 g_signal_connect (G_OBJECT(priv->msg_view), "recpt_activated",
881 G_CALLBACK (modest_ui_actions_on_msg_recpt_activated), obj);
882 g_signal_connect (G_OBJECT(priv->msg_view), "link_contextual",
883 G_CALLBACK (modest_ui_actions_on_msg_link_contextual), obj);
885 g_signal_connect (G_OBJECT (obj), "key-release-event",
886 G_CALLBACK (modest_msg_view_window_key_event),
889 g_signal_connect (G_OBJECT (obj), "key-press-event",
890 G_CALLBACK (modest_msg_view_window_key_event),
893 g_signal_connect (G_OBJECT (obj), "window-state-event",
894 G_CALLBACK (modest_msg_view_window_window_state_event),
897 g_signal_connect (G_OBJECT (obj), "move-focus",
898 G_CALLBACK (on_move_focus), obj);
900 /* Mail Operation Queue */
901 priv->queue_change_handler = g_signal_connect (G_OBJECT (modest_runtime_get_mail_operation_queue ()),
903 G_CALLBACK (on_queue_changed),
906 /* Account manager */
907 priv->account_removed_handler = g_signal_connect (G_OBJECT (modest_runtime_get_account_store ()),
909 G_CALLBACK(on_account_removed),
912 modest_window_set_active_account (MODEST_WINDOW(obj), modest_account_name);
914 g_signal_connect (G_OBJECT (priv->find_toolbar), "close", G_CALLBACK (modest_msg_view_window_find_toolbar_close), obj);
915 g_signal_connect (G_OBJECT (priv->find_toolbar), "search", G_CALLBACK (modest_msg_view_window_find_toolbar_search), obj);
916 priv->last_search = NULL;
918 /* Init the clipboard actions dim status */
919 modest_msg_view_grab_focus(MODEST_MSG_VIEW (priv->msg_view));
921 update_window_title (MODEST_MSG_VIEW_WINDOW (obj));
926 /* FIXME: parameter checks */
928 modest_msg_view_window_new_with_header_model (TnyMsg *msg,
929 const gchar *modest_account_name,
930 const gchar *msg_uid,
932 GtkTreeRowReference *row_reference)
934 ModestMsgViewWindow *window = NULL;
935 ModestMsgViewWindowPrivate *priv = NULL;
936 TnyFolder *header_folder = NULL;
937 ModestHeaderView *header_view = NULL;
938 ModestWindow *main_window = NULL;
939 ModestWindowMgr *mgr = NULL;
942 modest_tny_mime_part_to_string (TNY_MIME_PART (msg), 0);
945 mgr = modest_runtime_get_window_mgr ();
946 window = MODEST_MSG_VIEW_WINDOW (modest_window_mgr_get_msg_view_window (mgr));
947 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), NULL);
949 modest_msg_view_window_construct (window, modest_account_name, msg_uid);
951 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
953 /* Remember the message list's TreeModel so we can detect changes
954 * and change the list selection when necessary: */
956 main_window = modest_window_mgr_get_main_window(mgr, FALSE); /* don't create */
958 header_view = MODEST_HEADER_VIEW(modest_main_window_get_child_widget(
959 MODEST_MAIN_WINDOW(main_window),
960 MODEST_MAIN_WINDOW_WIDGET_TYPE_HEADER_VIEW));
963 if (header_view != NULL){
964 header_folder = modest_header_view_get_folder(header_view);
965 /* This could happen if the header folder was
966 unseleted before opening this msg window (for
967 example if the user selects an account in the
968 folder view of the main window */
970 priv->is_outbox = (modest_tny_folder_guess_folder_type (header_folder) == TNY_FOLDER_TYPE_OUTBOX);
971 priv->header_folder_id = tny_folder_get_id(header_folder);
972 g_assert(priv->header_folder_id != NULL);
973 g_object_unref(header_folder);
977 /* Setup row references and connect signals */
978 priv->header_model = g_object_ref (model);
981 priv->row_reference = gtk_tree_row_reference_copy (row_reference);
982 priv->next_row_reference = gtk_tree_row_reference_copy (row_reference);
983 select_next_valid_row (model, &(priv->next_row_reference), TRUE, priv->is_outbox);
985 priv->row_reference = NULL;
986 priv->next_row_reference = NULL;
989 /* Connect signals */
990 priv->row_changed_handler =
991 g_signal_connect (GTK_TREE_MODEL(model), "row-changed",
992 G_CALLBACK(modest_msg_view_window_on_row_changed),
994 priv->row_deleted_handler =
995 g_signal_connect (GTK_TREE_MODEL(model), "row-deleted",
996 G_CALLBACK(modest_msg_view_window_on_row_deleted),
998 priv->row_inserted_handler =
999 g_signal_connect (GTK_TREE_MODEL(model), "row-inserted",
1000 G_CALLBACK(modest_msg_view_window_on_row_inserted),
1002 priv->rows_reordered_handler =
1003 g_signal_connect(GTK_TREE_MODEL(model), "rows-reordered",
1004 G_CALLBACK(modest_msg_view_window_on_row_reordered),
1007 if (header_view != NULL){
1008 modest_header_view_add_observer(header_view,
1009 MODEST_HEADER_VIEW_OBSERVER(window));
1012 tny_msg_view_set_msg (TNY_MSG_VIEW (priv->msg_view), msg);
1013 update_window_title (MODEST_MSG_VIEW_WINDOW (window));
1014 gtk_widget_show_all (GTK_WIDGET (window));
1015 modest_msg_view_window_update_priority (window);
1017 /* Check dimming rules */
1018 modest_ui_actions_check_toolbar_dimming_rules (MODEST_WINDOW (window));
1019 modest_ui_actions_check_menu_dimming_rules (MODEST_WINDOW (window));
1020 modest_window_check_dimming_rules_group (MODEST_WINDOW (window), MODEST_DIMMING_RULES_CLIPBOARD);
1022 return MODEST_WINDOW(window);
1026 modest_msg_view_window_new_for_search_result (TnyMsg *msg,
1027 const gchar *modest_account_name,
1028 const gchar *msg_uid)
1030 ModestMsgViewWindow *window = NULL;
1031 ModestMsgViewWindowPrivate *priv = NULL;
1032 ModestWindowMgr *mgr = NULL;
1034 mgr = modest_runtime_get_window_mgr ();
1035 window = MODEST_MSG_VIEW_WINDOW (modest_window_mgr_get_msg_view_window (mgr));
1036 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), NULL);
1037 modest_msg_view_window_construct (window, modest_account_name, msg_uid);
1039 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1041 /* Remember that this is a search result,
1042 * so we can disable some UI appropriately: */
1043 priv->is_search_result = TRUE;
1045 tny_msg_view_set_msg (TNY_MSG_VIEW (priv->msg_view), msg);
1047 update_window_title (window);
1048 gtk_widget_show_all (GTK_WIDGET (window));
1049 modest_msg_view_window_update_priority (window);
1051 /* Check dimming rules */
1052 modest_ui_actions_check_toolbar_dimming_rules (MODEST_WINDOW (window));
1053 modest_ui_actions_check_menu_dimming_rules (MODEST_WINDOW (window));
1054 modest_window_check_dimming_rules_group (MODEST_WINDOW (window), MODEST_DIMMING_RULES_CLIPBOARD);
1056 return MODEST_WINDOW(window);
1060 modest_msg_view_window_new_for_attachment (TnyMsg *msg,
1061 const gchar *modest_account_name,
1062 const gchar *msg_uid)
1064 GObject *obj = NULL;
1065 ModestMsgViewWindowPrivate *priv;
1066 ModestWindowMgr *mgr = NULL;
1068 g_return_val_if_fail (msg, NULL);
1069 mgr = modest_runtime_get_window_mgr ();
1070 obj = G_OBJECT (modest_window_mgr_get_msg_view_window (mgr));
1071 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (obj);
1072 modest_msg_view_window_construct (MODEST_MSG_VIEW_WINDOW (obj),
1073 modest_account_name, msg_uid);
1075 tny_msg_view_set_msg (TNY_MSG_VIEW (priv->msg_view), msg);
1076 update_window_title (MODEST_MSG_VIEW_WINDOW (obj));
1078 gtk_widget_show_all (GTK_WIDGET (obj));
1080 /* Check dimming rules */
1081 modest_ui_actions_check_toolbar_dimming_rules (MODEST_WINDOW (obj));
1082 modest_ui_actions_check_menu_dimming_rules (MODEST_WINDOW (obj));
1083 modest_window_check_dimming_rules_group (MODEST_WINDOW (obj), MODEST_DIMMING_RULES_CLIPBOARD);
1085 return MODEST_WINDOW(obj);
1089 modest_msg_view_window_on_row_changed (GtkTreeModel *header_model,
1092 ModestMsgViewWindow *window)
1094 check_dimming_rules_after_change (window);
1098 modest_msg_view_window_on_row_deleted(GtkTreeModel *header_model,
1100 ModestMsgViewWindow *window)
1102 check_dimming_rules_after_change (window);
1106 check_dimming_rules_after_change_in_idle (gpointer data)
1108 /* The window could have dissapeared */
1109 if (MODEST_IS_WINDOW (data)) {
1110 ModestWindow *win = MODEST_WINDOW (data);
1111 gdk_threads_enter ();
1112 modest_ui_actions_check_menu_dimming_rules (win);
1113 modest_ui_actions_check_toolbar_dimming_rules (win);
1114 gdk_threads_leave ();
1121 check_dimming_rules_after_change (ModestMsgViewWindow *window)
1123 static guint dimming_delayer = 0;
1125 if (dimming_delayer > 0)
1126 g_source_remove (dimming_delayer);
1128 /* We're expecting a lot of changes at the same time so don't
1129 need to check dimming rules for every change that
1131 dimming_delayer = g_timeout_add (100, check_dimming_rules_after_change_in_idle, window);
1135 /* On insertions we check if the folder still has the message we are
1136 * showing or do not. If do not, we do nothing. Which means we are still
1137 * not attached to any header folder and thus next/prev buttons are
1138 * still dimmed. Once the message that is shown by msg-view is found, the
1139 * new model of header-view will be attached and the references will be set.
1140 * On each further insertions dimming rules will be checked. However
1141 * this requires extra CPU time at least works.
1142 * (An message might be deleted from TnyFolder and thus will not be
1143 * inserted into the model again for example if it is removed by the
1144 * imap server and the header view is refreshed.)
1147 modest_msg_view_window_on_row_inserted (GtkTreeModel *model,
1148 GtkTreePath *tree_path,
1149 GtkTreeIter *tree_iter,
1150 ModestMsgViewWindow *window)
1152 ModestMsgViewWindowPrivate *priv = NULL;
1153 TnyHeader *header = NULL;
1155 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window));
1156 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1158 g_assert (model == priv->header_model);
1160 /* Check if the newly inserted message is the same we are actually
1161 * showing. IF not, we should remain detached from the header model
1162 * and thus prev and next toolbar buttons should remain dimmed. */
1163 gtk_tree_model_get (model, tree_iter,
1164 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
1167 if (TNY_IS_HEADER (header)) {
1170 uid = modest_tny_folder_get_header_unique_id (header);
1171 if (!g_str_equal(priv->msg_uid, uid)) {
1172 check_dimming_rules_after_change (window);
1174 g_object_unref (G_OBJECT(header));
1178 g_object_unref(G_OBJECT(header));
1181 if (priv->row_reference) {
1182 gtk_tree_row_reference_free (priv->row_reference);
1185 /* Setup row_reference for the actual msg. */
1186 priv->row_reference = gtk_tree_row_reference_new (priv->header_model, tree_path);
1187 if (priv->row_reference == NULL) {
1188 g_warning("No reference for msg header item.");
1192 /* Now set up next_row_reference. */
1193 if (priv->next_row_reference) {
1194 gtk_tree_row_reference_free (priv->next_row_reference);
1197 priv->next_row_reference = gtk_tree_row_reference_copy (priv->row_reference);
1198 select_next_valid_row (priv->header_model,
1199 &(priv->next_row_reference), FALSE, priv->is_outbox);
1201 /* Connect the remaining callbacks to become able to detect
1202 * changes in header-view. */
1203 priv->row_changed_handler =
1204 g_signal_connect (priv->header_model, "row-changed",
1205 G_CALLBACK (modest_msg_view_window_on_row_changed),
1207 priv->row_deleted_handler =
1208 g_signal_connect (priv->header_model, "row-deleted",
1209 G_CALLBACK (modest_msg_view_window_on_row_deleted),
1211 priv->rows_reordered_handler =
1212 g_signal_connect (priv->header_model, "rows-reordered",
1213 G_CALLBACK (modest_msg_view_window_on_row_reordered),
1216 check_dimming_rules_after_change (window);
1220 modest_msg_view_window_on_row_reordered (GtkTreeModel *header_model,
1224 ModestMsgViewWindow *window)
1226 ModestMsgViewWindowPrivate *priv = NULL;
1227 gboolean already_changed = FALSE;
1229 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(window);
1231 /* If the current row was reordered select the proper next
1232 valid row. The same if the next row reference changes */
1233 if (priv->row_reference &&
1234 gtk_tree_row_reference_valid (priv->row_reference)) {
1236 path = gtk_tree_row_reference_get_path (priv->row_reference);
1237 if (gtk_tree_path_compare (path, arg1) == 0) {
1238 if (priv->next_row_reference) {
1239 gtk_tree_row_reference_free (priv->next_row_reference);
1241 priv->next_row_reference = gtk_tree_row_reference_copy (priv->row_reference);
1242 select_next_valid_row (header_model, &(priv->next_row_reference), FALSE, priv->is_outbox);
1243 already_changed = TRUE;
1245 gtk_tree_path_free (path);
1247 if (!already_changed &&
1248 priv->next_row_reference &&
1249 gtk_tree_row_reference_valid (priv->next_row_reference)) {
1251 path = gtk_tree_row_reference_get_path (priv->next_row_reference);
1252 if (gtk_tree_path_compare (path, arg1) == 0) {
1253 if (priv->next_row_reference) {
1254 gtk_tree_row_reference_free (priv->next_row_reference);
1256 priv->next_row_reference = gtk_tree_row_reference_copy (priv->row_reference);
1257 select_next_valid_row (header_model, &(priv->next_row_reference), FALSE, priv->is_outbox);
1259 gtk_tree_path_free (path);
1261 check_dimming_rules_after_change (window);
1264 /* The modest_msg_view_window_update_model_replaced implements update
1265 * function for ModestHeaderViewObserver. Checks whether the TnyFolder
1266 * actually belongs to the header-view is the same as the TnyFolder of
1267 * the message of msg-view or not. If they are different, there is
1268 * nothing to do. If they are the same, then the model has replaced and
1269 * the reference in msg-view shall be replaced from the old model to
1270 * the new model. In this case the view will be detached from it's
1271 * header folder. From this point the next/prev buttons are dimmed.
1274 modest_msg_view_window_update_model_replaced (ModestHeaderViewObserver *observer,
1275 GtkTreeModel *model,
1276 const gchar *tny_folder_id)
1278 ModestMsgViewWindowPrivate *priv = NULL;
1279 ModestMsgViewWindow *window = NULL;
1281 g_assert(MODEST_IS_HEADER_VIEW_OBSERVER(observer));
1282 g_assert(MODEST_IS_MSG_VIEW_WINDOW(observer));
1284 window = MODEST_MSG_VIEW_WINDOW(observer);
1285 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(window);
1287 /* If there is an other folder in the header-view then we do
1288 * not care about it's model (msg list). Else if the
1289 * header-view shows the folder the msg shown by us is in, we
1290 * shall replace our model reference and make some check. */
1291 if(model == NULL || tny_folder_id == NULL ||
1292 (priv->header_folder_id && !g_str_equal(tny_folder_id, priv->header_folder_id)))
1295 /* Model is changed(replaced), so we should forget the old
1296 * one. Because there might be other references and there
1297 * might be some change on the model even if we unreferenced
1298 * it, we need to disconnect our signals here. */
1299 if (priv->header_model) {
1300 if (g_signal_handler_is_connected(G_OBJECT (priv->header_model),
1301 priv->row_changed_handler))
1302 g_signal_handler_disconnect(G_OBJECT (priv->header_model),
1303 priv->row_changed_handler);
1304 if (g_signal_handler_is_connected(G_OBJECT (priv->header_model),
1305 priv->row_deleted_handler))
1306 g_signal_handler_disconnect(G_OBJECT (priv->header_model),
1307 priv->row_deleted_handler);
1308 if (g_signal_handler_is_connected(G_OBJECT (priv->header_model),
1309 priv->row_inserted_handler))
1310 g_signal_handler_disconnect(G_OBJECT (priv->header_model),
1311 priv->row_inserted_handler);
1312 if (g_signal_handler_is_connected(G_OBJECT (priv->header_model),
1313 priv->rows_reordered_handler))
1314 g_signal_handler_disconnect(G_OBJECT (priv->header_model),
1315 priv->rows_reordered_handler);
1318 if (priv->row_reference)
1319 gtk_tree_row_reference_free (priv->row_reference);
1320 if (priv->next_row_reference)
1321 gtk_tree_row_reference_free (priv->next_row_reference);
1322 g_object_unref(priv->header_model);
1325 priv->row_changed_handler = 0;
1326 priv->row_deleted_handler = 0;
1327 priv->row_inserted_handler = 0;
1328 priv->rows_reordered_handler = 0;
1329 priv->next_row_reference = NULL;
1330 priv->row_reference = NULL;
1331 priv->header_model = NULL;
1334 priv->header_model = g_object_ref (model);
1336 /* Also we must connect to the new model for row insertions.
1337 * Only for insertions now. We will need other ones only after
1338 * the msg is show by msg-view is added to the new model. */
1339 priv->row_inserted_handler =
1340 g_signal_connect (priv->header_model, "row-inserted",
1341 G_CALLBACK(modest_msg_view_window_on_row_inserted),
1344 modest_ui_actions_check_menu_dimming_rules(MODEST_WINDOW(window));
1345 modest_ui_actions_check_toolbar_dimming_rules(MODEST_WINDOW(window));
1349 modest_msg_view_window_toolbar_on_transfer_mode (ModestMsgViewWindow *self)
1351 ModestMsgViewWindowPrivate *priv= NULL;
1353 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self), FALSE);
1354 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
1356 return priv->current_toolbar_mode == TOOLBAR_MODE_TRANSFER;
1360 modest_msg_view_window_get_header (ModestMsgViewWindow *self)
1362 ModestMsgViewWindowPrivate *priv= NULL;
1364 TnyHeader *header = NULL;
1365 GtkTreePath *path = NULL;
1368 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self), NULL);
1369 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
1371 /* If the message was not obtained from a treemodel,
1372 * for instance if it was opened directly by the search UI:
1374 if (priv->header_model == NULL ||
1375 priv->row_reference == NULL ||
1376 !gtk_tree_row_reference_valid (priv->row_reference)) {
1377 msg = modest_msg_view_window_get_message (self);
1379 header = tny_msg_get_header (msg);
1380 g_object_unref (msg);
1385 /* Get iter of the currently selected message in the header view: */
1386 path = gtk_tree_row_reference_get_path (priv->row_reference);
1387 g_return_val_if_fail (path != NULL, NULL);
1388 gtk_tree_model_get_iter (priv->header_model,
1392 /* Get current message header */
1393 gtk_tree_model_get (priv->header_model, &iter,
1394 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
1397 gtk_tree_path_free (path);
1402 modest_msg_view_window_get_message (ModestMsgViewWindow *self)
1404 ModestMsgViewWindowPrivate *priv;
1406 g_return_val_if_fail (self, NULL);
1408 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(self);
1410 return tny_msg_view_get_msg (TNY_MSG_VIEW (priv->msg_view));
1414 modest_msg_view_window_get_message_uid (ModestMsgViewWindow *self)
1416 ModestMsgViewWindowPrivate *priv;
1418 g_return_val_if_fail (self, NULL);
1420 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
1422 return (const gchar*) priv->msg_uid;
1426 modest_msg_view_window_toggle_find_toolbar (GtkToggleAction *toggle,
1429 ModestMsgViewWindow *window = MODEST_MSG_VIEW_WINDOW (data);
1430 ModestMsgViewWindowPrivate *priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1431 ModestWindowPrivate *parent_priv = MODEST_WINDOW_GET_PRIVATE (window);
1435 is_active = gtk_toggle_action_get_active (toggle);
1438 gtk_widget_show (priv->find_toolbar);
1439 hildon_find_toolbar_highlight_entry (HILDON_FIND_TOOLBAR (priv->find_toolbar), TRUE);
1441 gtk_widget_hide (priv->find_toolbar);
1444 /* update the toggle buttons status */
1445 action = gtk_ui_manager_get_action (parent_priv->ui_manager, "/ToolBar/FindInMessage");
1446 modest_utils_toggle_action_set_active_block_notify (GTK_TOGGLE_ACTION (action), is_active);
1447 action = gtk_ui_manager_get_action (parent_priv->ui_manager, "/MenuBar/ToolsMenu/ToolsFindInMessageMenu");
1448 modest_utils_toggle_action_set_active_block_notify (GTK_TOGGLE_ACTION (action), is_active);
1453 modest_msg_view_window_find_toolbar_close (GtkWidget *widget,
1454 ModestMsgViewWindow *obj)
1456 GtkToggleAction *toggle;
1457 ModestWindowPrivate *parent_priv;
1458 ModestMsgViewWindowPrivate *priv;
1460 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (obj);
1461 parent_priv = MODEST_WINDOW_GET_PRIVATE (obj);
1463 toggle = GTK_TOGGLE_ACTION (gtk_ui_manager_get_action (parent_priv->ui_manager, "/ToolBar/FindInMessage"));
1464 gtk_toggle_action_set_active (toggle, FALSE);
1465 modest_msg_view_grab_focus (MODEST_MSG_VIEW (priv->msg_view));
1469 modest_msg_view_window_find_toolbar_search (GtkWidget *widget,
1470 ModestMsgViewWindow *obj)
1472 gchar *current_search;
1473 ModestMsgViewWindowPrivate *priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (obj);
1475 if (modest_mime_part_view_is_empty (MODEST_MIME_PART_VIEW (priv->msg_view))) {
1476 hildon_banner_show_information (NULL, NULL, _("mail_ib_nothing_to_find"));
1480 g_object_get (G_OBJECT (widget), "prefix", ¤t_search, NULL);
1482 if ((current_search == NULL) || (strcmp (current_search, "") == 0)) {
1483 g_free (current_search);
1484 hildon_banner_show_information (NULL, NULL, dgettext("hildon-common-strings", "ecdg_ib_find_rep_enter_text"));
1488 if ((priv->last_search == NULL) || (strcmp (priv->last_search, current_search) != 0)) {
1490 g_free (priv->last_search);
1491 priv->last_search = g_strdup (current_search);
1492 result = modest_isearch_view_search (MODEST_ISEARCH_VIEW (priv->msg_view),
1495 hildon_banner_show_information (NULL, NULL, dgettext("hildon-libs", "ckct_ib_find_no_matches"));
1496 g_free (priv->last_search);
1497 priv->last_search = NULL;
1499 modest_msg_view_grab_focus (MODEST_MSG_VIEW (priv->msg_view));
1500 hildon_find_toolbar_highlight_entry (HILDON_FIND_TOOLBAR (priv->find_toolbar), TRUE);
1503 if (!modest_isearch_view_search_next (MODEST_ISEARCH_VIEW (priv->msg_view))) {
1504 hildon_banner_show_information (NULL, NULL, dgettext("hildon-libs", "ckct_ib_find_search_complete"));
1505 g_free (priv->last_search);
1506 priv->last_search = NULL;
1508 modest_msg_view_grab_focus (MODEST_MSG_VIEW (priv->msg_view));
1509 hildon_find_toolbar_highlight_entry (HILDON_FIND_TOOLBAR (priv->find_toolbar), TRUE);
1513 g_free (current_search);
1518 modest_msg_view_window_set_zoom (ModestWindow *window,
1521 ModestMsgViewWindowPrivate *priv;
1522 ModestWindowPrivate *parent_priv;
1523 GtkAction *action = NULL;
1524 gint int_zoom = (gint) rint (zoom*100.0+0.1);
1526 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window));
1528 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1529 parent_priv = MODEST_WINDOW_GET_PRIVATE (window);
1530 modest_zoomable_set_zoom (MODEST_ZOOMABLE (priv->msg_view), zoom);
1532 action = gtk_ui_manager_get_action (parent_priv->ui_manager,
1533 "/MenuBar/ViewMenu/ZoomMenu/Zoom50Menu");
1535 gtk_radio_action_set_current_value (GTK_RADIO_ACTION (action), int_zoom);
1539 modest_msg_view_window_get_zoom (ModestWindow *window)
1541 ModestMsgViewWindowPrivate *priv;
1543 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), 1.0);
1545 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1546 return modest_zoomable_get_zoom (MODEST_ZOOMABLE (priv->msg_view));
1550 modest_msg_view_window_zoom_plus (ModestWindow *window)
1552 ModestWindowPrivate *parent_priv;
1553 GtkRadioAction *zoom_radio_action;
1554 GSList *group, *node;
1556 parent_priv = MODEST_WINDOW_GET_PRIVATE (window);
1557 zoom_radio_action = GTK_RADIO_ACTION (gtk_ui_manager_get_action (parent_priv->ui_manager,
1558 "/MenuBar/ViewMenu/ZoomMenu/Zoom50Menu"));
1560 group = gtk_radio_action_get_group (zoom_radio_action);
1562 if (gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (group->data))) {
1563 hildon_banner_show_information (NULL, NULL, dgettext("hildon-common-strings", "ckct_ib_max_zoom_level_reached"));
1567 for (node = group; node != NULL; node = g_slist_next (node)) {
1568 if ((node->next != NULL) && gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (node->next->data))) {
1569 gtk_toggle_action_set_active (GTK_TOGGLE_ACTION (node->data), TRUE);
1577 modest_msg_view_window_zoom_minus (ModestWindow *window)
1579 ModestWindowPrivate *parent_priv;
1580 GtkRadioAction *zoom_radio_action;
1581 GSList *group, *node;
1583 parent_priv = MODEST_WINDOW_GET_PRIVATE (window);
1584 zoom_radio_action = GTK_RADIO_ACTION (gtk_ui_manager_get_action (parent_priv->ui_manager,
1585 "/MenuBar/ViewMenu/ZoomMenu/Zoom50Menu"));
1587 group = gtk_radio_action_get_group (zoom_radio_action);
1589 for (node = group; node != NULL; node = g_slist_next (node)) {
1590 if (gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (node->data))) {
1591 if (node->next != NULL) {
1592 gtk_toggle_action_set_active (GTK_TOGGLE_ACTION (node->next->data), TRUE);
1595 hildon_banner_show_information (NULL, NULL, dgettext("hildon-common-strings", "ckct_ib_min_zoom_level_reached"));
1605 modest_msg_view_window_key_event (GtkWidget *window,
1611 focus = gtk_window_get_focus (GTK_WINDOW (window));
1613 /* for the find toolbar case */
1614 if (focus && GTK_IS_ENTRY (focus)) {
1615 if (event->keyval == GDK_BackSpace) {
1617 copy = gdk_event_copy ((GdkEvent *) event);
1618 gtk_widget_event (focus, copy);
1619 gdk_event_free (copy);
1624 if (event->keyval == GDK_Up || event->keyval == GDK_KP_Up ||
1625 event->keyval == GDK_Down || event->keyval == GDK_KP_Down ||
1626 event->keyval == GDK_Page_Up || event->keyval == GDK_KP_Page_Up ||
1627 event->keyval == GDK_Page_Down || event->keyval == GDK_KP_Page_Down ||
1628 event->keyval == GDK_Home || event->keyval == GDK_KP_Home ||
1629 event->keyval == GDK_End || event->keyval == GDK_KP_End) {
1630 /* ModestMsgViewWindowPrivate *priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window); */
1631 /* gboolean return_value; */
1633 if (event->type == GDK_KEY_PRESS) {
1634 GtkScrollType scroll_type;
1636 switch (event->keyval) {
1639 scroll_type = GTK_SCROLL_STEP_UP; break;
1642 scroll_type = GTK_SCROLL_STEP_DOWN; break;
1644 case GDK_KP_Page_Up:
1645 scroll_type = GTK_SCROLL_PAGE_UP; break;
1647 case GDK_KP_Page_Down:
1648 scroll_type = GTK_SCROLL_PAGE_DOWN; break;
1651 scroll_type = GTK_SCROLL_START; break;
1654 scroll_type = GTK_SCROLL_END; break;
1655 default: scroll_type = GTK_SCROLL_NONE;
1658 /* g_signal_emit_by_name (G_OBJECT (priv->main_scroll), "scroll-child", */
1659 /* scroll_type, FALSE, &return_value); */
1670 modest_msg_view_window_last_message_selected (ModestMsgViewWindow *window)
1673 ModestMsgViewWindowPrivate *priv;
1674 GtkTreeIter tmp_iter;
1675 gboolean is_last_selected;
1677 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), TRUE);
1678 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1680 /*if no model (so no rows at all), then virtually we are the last*/
1681 if (!priv->header_model || !priv->row_reference)
1684 path = gtk_tree_row_reference_get_path (priv->row_reference);
1688 is_last_selected = TRUE;
1689 while (is_last_selected) {
1691 gtk_tree_path_next (path);
1692 if (!gtk_tree_model_get_iter (priv->header_model, &tmp_iter, path))
1694 gtk_tree_model_get (priv->header_model, &tmp_iter,
1695 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
1698 if (msg_is_visible (header, priv->is_outbox))
1699 is_last_selected = FALSE;
1700 g_object_unref(G_OBJECT(header));
1703 gtk_tree_path_free (path);
1704 return is_last_selected;
1708 modest_msg_view_window_has_headers_model (ModestMsgViewWindow *window)
1710 ModestMsgViewWindowPrivate *priv;
1712 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), TRUE);
1713 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1715 return priv->header_model != NULL;
1719 modest_msg_view_window_is_search_result (ModestMsgViewWindow *window)
1721 ModestMsgViewWindowPrivate *priv;
1723 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), TRUE);
1724 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1726 return priv->is_search_result;
1730 msg_is_visible (TnyHeader *header, gboolean check_outbox)
1732 return (!(tny_header_get_flags(header) & TNY_HEADER_FLAG_DELETED)) &&
1733 ( (!check_outbox) || (modest_tny_all_send_queues_get_msg_status (header) != MODEST_TNY_SEND_QUEUE_FAILED)) ;
1738 modest_msg_view_window_first_message_selected (ModestMsgViewWindow *window)
1741 ModestMsgViewWindowPrivate *priv;
1742 gboolean is_first_selected;
1743 GtkTreeIter tmp_iter;
1744 /* gchar * path_string;*/
1746 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), TRUE);
1747 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1749 /*if no model (so no rows at all), then virtually we are the first*/
1750 if (!priv->header_model || !priv->row_reference)
1753 path = gtk_tree_row_reference_get_path (priv->row_reference);
1757 /* path_string = gtk_tree_path_to_string (path);
1758 is_first_selected = strcmp (path_string, "0");
1760 g_free (path_string);
1761 gtk_tree_path_free (path);
1763 return is_first_selected;*/
1765 is_first_selected = TRUE;
1766 while (is_first_selected) {
1768 if(!gtk_tree_path_prev (path))
1770 /* Here the 'if' is needless for logic, but let make sure
1771 * iter is valid for gtk_tree_model_get. */
1772 if (!gtk_tree_model_get_iter (priv->header_model, &tmp_iter, path))
1774 gtk_tree_model_get (priv->header_model, &tmp_iter,
1775 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
1778 if (msg_is_visible (header, priv->is_outbox))
1779 is_first_selected = FALSE;
1780 g_object_unref(G_OBJECT(header));
1783 gtk_tree_path_free (path);
1784 return is_first_selected;
1789 GtkTreeRowReference *row_reference;
1793 message_reader_performer (gboolean canceled,
1795 GtkWindow *parent_window,
1796 TnyAccount *account,
1799 ModestMailOperation *mail_op = NULL;
1800 MsgReaderInfo *info;
1802 info = (MsgReaderInfo *) user_data;
1803 if (canceled || err) {
1807 /* Register the header - it'll be unregistered in the callback */
1808 modest_window_mgr_register_header (modest_runtime_get_window_mgr (), info->header, NULL);
1810 /* New mail operation */
1811 mail_op = modest_mail_operation_new_with_error_handling (G_OBJECT(parent_window),
1812 modest_ui_actions_disk_operations_error_handler,
1815 modest_mail_operation_queue_add (modest_runtime_get_mail_operation_queue (), mail_op);
1816 modest_mail_operation_get_msg (mail_op, info->header, TRUE, view_msg_cb, info->row_reference);
1817 g_object_unref (mail_op);
1819 /* Update dimming rules */
1820 modest_ui_actions_check_toolbar_dimming_rules (MODEST_WINDOW (parent_window));
1821 modest_ui_actions_check_menu_dimming_rules (MODEST_WINDOW (parent_window));
1824 /* Frees. The row_reference will be freed by the view_msg_cb callback */
1825 g_object_unref (info->header);
1826 g_slice_free (MsgReaderInfo, info);
1831 * Reads the message whose summary item is @header. It takes care of
1832 * several things, among others:
1834 * If the message was not previously downloaded then ask the user
1835 * before downloading. If there is no connection launch the connection
1836 * dialog. Update toolbar dimming rules.
1838 * Returns: TRUE if the mail operation was started, otherwise if the
1839 * user do not want to download the message, or if the user do not
1840 * want to connect, then the operation is not issued
1843 message_reader (ModestMsgViewWindow *window,
1844 ModestMsgViewWindowPrivate *priv,
1846 GtkTreeRowReference *row_reference)
1848 gboolean already_showing = FALSE;
1849 ModestWindow *msg_window = NULL;
1850 ModestWindowMgr *mgr;
1851 TnyAccount *account;
1853 MsgReaderInfo *info;
1855 g_return_val_if_fail (row_reference != NULL, FALSE);
1857 mgr = modest_runtime_get_window_mgr ();
1858 already_showing = modest_window_mgr_find_registered_header (mgr, header, &msg_window);
1859 if (already_showing && (msg_window != MODEST_WINDOW (window))) {
1862 gtk_window_present (GTK_WINDOW (msg_window));
1863 g_signal_emit_by_name (G_OBJECT (window), "delete-event", NULL, &retval);
1867 /* Msg download completed */
1868 if (!(tny_header_get_flags (header) & TNY_HEADER_FLAG_CACHED)) {
1869 /* Ask the user if he wants to download the message if
1871 if (!tny_device_is_online (modest_runtime_get_device())) {
1872 GtkResponseType response;
1874 response = modest_platform_run_confirmation_dialog (GTK_WINDOW (window),
1875 _("mcen_nc_get_msg"));
1876 if (response == GTK_RESPONSE_CANCEL)
1879 folder = tny_header_get_folder (header);
1880 info = g_slice_new (MsgReaderInfo);
1881 info->header = g_object_ref (header);
1882 info->row_reference = gtk_tree_row_reference_copy (row_reference);
1884 /* Offer the connection dialog if necessary */
1885 modest_platform_connect_if_remote_and_perform ((GtkWindow *) window,
1887 TNY_FOLDER_STORE (folder),
1888 message_reader_performer,
1890 g_object_unref (folder);
1895 folder = tny_header_get_folder (header);
1896 account = tny_folder_get_account (folder);
1897 info = g_slice_new (MsgReaderInfo);
1898 info->header = g_object_ref (header);
1899 info->row_reference = gtk_tree_row_reference_copy (row_reference);
1901 message_reader_performer (FALSE, NULL, (GtkWindow *) window, account, info);
1902 g_object_unref (account);
1903 g_object_unref (folder);
1909 modest_msg_view_window_select_next_message (ModestMsgViewWindow *window)
1911 ModestMsgViewWindowPrivate *priv;
1912 GtkTreePath *path= NULL;
1913 GtkTreeIter tmp_iter;
1915 gboolean retval = TRUE;
1916 GtkTreeRowReference *row_reference = NULL;
1918 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), FALSE);
1919 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1921 if (!priv->row_reference)
1924 /* Update the next row reference if it's not valid. This could
1925 happen if for example the header which it was pointing to,
1926 was deleted. The best place to do it is in the row-deleted
1927 handler but the tinymail model do not work like the glib
1928 tree models and reports the deletion when the row is still
1930 if (!gtk_tree_row_reference_valid (priv->next_row_reference)) {
1931 if (gtk_tree_row_reference_valid (priv->row_reference)) {
1932 priv->next_row_reference = gtk_tree_row_reference_copy (priv->row_reference);
1933 select_next_valid_row (priv->header_model, &(priv->next_row_reference), FALSE, priv->is_outbox);
1936 if (priv->next_row_reference)
1937 path = gtk_tree_row_reference_get_path (priv->next_row_reference);
1941 row_reference = gtk_tree_row_reference_copy (priv->next_row_reference);
1943 gtk_tree_model_get_iter (priv->header_model,
1946 gtk_tree_path_free (path);
1948 gtk_tree_model_get (priv->header_model, &tmp_iter,
1949 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
1952 /* Read the message & show it */
1953 if (!message_reader (window, priv, header, row_reference)) {
1956 gtk_tree_row_reference_free (row_reference);
1959 g_object_unref (header);
1965 modest_msg_view_window_select_previous_message (ModestMsgViewWindow *window)
1967 ModestMsgViewWindowPrivate *priv = NULL;
1969 gboolean finished = FALSE;
1970 gboolean retval = FALSE;
1972 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), FALSE);
1973 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1975 /* Return inmediatly if there is no header model */
1976 if (!priv->header_model || !priv->row_reference)
1979 path = gtk_tree_row_reference_get_path (priv->row_reference);
1980 while (!finished && gtk_tree_path_prev (path)) {
1984 gtk_tree_model_get_iter (priv->header_model, &iter, path);
1985 gtk_tree_model_get (priv->header_model, &iter,
1986 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
1990 if (msg_is_visible (header, priv->is_outbox)) {
1991 GtkTreeRowReference *row_reference;
1992 row_reference = gtk_tree_row_reference_new (priv->header_model, path);
1993 /* Read the message & show it */
1994 retval = message_reader (window, priv, header, row_reference);
1995 gtk_tree_row_reference_free (row_reference);
1999 g_object_unref (header);
2003 gtk_tree_path_free (path);
2008 view_msg_cb (ModestMailOperation *mail_op,
2015 ModestMsgViewWindow *self = NULL;
2016 ModestMsgViewWindowPrivate *priv = NULL;
2017 GtkTreeRowReference *row_reference = NULL;
2019 /* Unregister the header (it was registered before creating the mail operation) */
2020 modest_window_mgr_unregister_header (modest_runtime_get_window_mgr (), header);
2022 row_reference = (GtkTreeRowReference *) user_data;
2024 gtk_tree_row_reference_free (row_reference);
2028 /* If there was any error */
2029 if (!modest_ui_actions_msg_retrieval_check (mail_op, header, msg)) {
2030 gtk_tree_row_reference_free (row_reference);
2034 /* Get the window */
2035 self = (ModestMsgViewWindow *) modest_mail_operation_get_source (mail_op);
2036 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self));
2037 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
2039 /* Update the row reference */
2040 if (priv->row_reference != NULL) {
2041 gtk_tree_row_reference_free (priv->row_reference);
2042 priv->row_reference = gtk_tree_row_reference_copy (row_reference);
2043 if (priv->next_row_reference != NULL) {
2044 gtk_tree_row_reference_free (priv->next_row_reference);
2046 priv->next_row_reference = gtk_tree_row_reference_copy (priv->row_reference);
2047 select_next_valid_row (priv->header_model, &(priv->next_row_reference), TRUE, priv->is_outbox);
2050 /* Mark header as read */
2051 if (!(tny_header_get_flags (header) & TNY_HEADER_FLAG_SEEN))
2052 tny_header_set_flag (header, TNY_HEADER_FLAG_SEEN);
2054 /* Set new message */
2055 if (priv->msg_view != NULL && TNY_IS_MSG_VIEW (priv->msg_view)) {
2056 tny_msg_view_set_msg (TNY_MSG_VIEW (priv->msg_view), msg);
2057 modest_msg_view_window_update_priority (self);
2058 update_window_title (MODEST_MSG_VIEW_WINDOW (self));
2059 modest_msg_view_grab_focus (MODEST_MSG_VIEW (priv->msg_view));
2062 /* Set the new message uid of the window */
2063 if (priv->msg_uid) {
2064 g_free (priv->msg_uid);
2065 priv->msg_uid = modest_tny_folder_get_header_unique_id (header);
2068 /* Notify the observers */
2069 g_signal_emit (G_OBJECT (self), signals[MSG_CHANGED_SIGNAL],
2070 0, priv->header_model, priv->row_reference);
2073 g_object_unref (self);
2074 gtk_tree_row_reference_free (row_reference);
2078 modest_msg_view_window_get_folder_type (ModestMsgViewWindow *window)
2080 ModestMsgViewWindowPrivate *priv;
2082 TnyFolderType folder_type;
2084 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
2086 folder_type = TNY_FOLDER_TYPE_UNKNOWN;
2088 msg = tny_msg_view_get_msg (TNY_MSG_VIEW (priv->msg_view));
2092 folder = tny_msg_get_folder (msg);
2094 folder_type = modest_tny_folder_guess_folder_type (folder);
2095 g_object_unref (folder);
2097 g_object_unref (msg);
2105 modest_msg_view_window_update_priority (ModestMsgViewWindow *window)
2107 ModestMsgViewWindowPrivate *priv;
2108 TnyHeader *header = NULL;
2109 TnyHeaderFlags flags = 0;
2111 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
2113 if (priv->header_model && priv->row_reference) {
2115 GtkTreePath *path = NULL;
2117 path = gtk_tree_row_reference_get_path (priv->row_reference);
2118 g_return_if_fail (path != NULL);
2119 gtk_tree_model_get_iter (priv->header_model,
2121 gtk_tree_row_reference_get_path (priv->row_reference));
2123 gtk_tree_model_get (priv->header_model, &iter, TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
2125 gtk_tree_path_free (path);
2128 msg = tny_msg_view_get_msg (TNY_MSG_VIEW (priv->msg_view));
2130 header = tny_msg_get_header (msg);
2131 g_object_unref (msg);
2136 flags = tny_header_get_flags (header);
2137 g_object_unref(G_OBJECT(header));
2140 modest_msg_view_set_priority (MODEST_MSG_VIEW(priv->msg_view), flags);
2145 toolbar_resize (ModestMsgViewWindow *self)
2147 ModestMsgViewWindowPrivate *priv = NULL;
2148 ModestWindowPrivate *parent_priv = NULL;
2150 gint static_button_size;
2151 ModestWindowMgr *mgr;
2153 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self));
2154 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
2155 parent_priv = MODEST_WINDOW_GET_PRIVATE(self);
2157 mgr = modest_runtime_get_window_mgr ();
2158 static_button_size = modest_window_mgr_get_fullscreen_mode (mgr)?118:108;
2160 if (parent_priv->toolbar) {
2161 /* left size buttons */
2162 widget = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/ToolbarMessageReply");
2163 gtk_tool_item_set_expand (GTK_TOOL_ITEM (widget), FALSE);
2164 gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (widget), FALSE);
2165 gtk_widget_set_size_request (GTK_WIDGET (widget), static_button_size, -1);
2166 widget = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/ToolbarMessageMoveTo");
2167 gtk_tool_item_set_expand (GTK_TOOL_ITEM (widget), FALSE);
2168 gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (widget), FALSE);
2169 gtk_widget_set_size_request (GTK_WIDGET (widget), static_button_size, -1);
2170 widget = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/ToolbarDeleteMessage");
2171 gtk_tool_item_set_expand (GTK_TOOL_ITEM (widget), FALSE);
2172 gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (widget), FALSE);
2173 gtk_widget_set_size_request (GTK_WIDGET (widget), static_button_size, -1);
2174 widget = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/FindInMessage");
2175 gtk_tool_item_set_expand (GTK_TOOL_ITEM (widget), FALSE);
2176 gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (widget), FALSE);
2177 gtk_widget_set_size_request (GTK_WIDGET (widget), static_button_size, -1);
2179 gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (priv->progress_toolitem), FALSE);
2180 gtk_tool_item_set_expand (GTK_TOOL_ITEM (priv->progress_toolitem), TRUE);
2181 gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (priv->cancel_toolitem), FALSE);
2182 gtk_tool_item_set_expand (GTK_TOOL_ITEM (priv->cancel_toolitem), FALSE);
2183 gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (priv->next_toolitem), TRUE);
2184 gtk_tool_item_set_expand (GTK_TOOL_ITEM (priv->next_toolitem), TRUE);
2185 gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (priv->prev_toolitem), TRUE);
2186 gtk_tool_item_set_expand (GTK_TOOL_ITEM (priv->prev_toolitem), TRUE);
2192 modest_msg_view_window_window_state_event (GtkWidget *widget, GdkEventWindowState *event, gpointer userdata)
2194 if (event->changed_mask & GDK_WINDOW_STATE_FULLSCREEN) {
2195 ModestWindowPrivate *parent_priv;
2196 ModestWindowMgr *mgr;
2197 gboolean is_fullscreen;
2198 GtkAction *fs_toggle_action;
2201 mgr = modest_runtime_get_window_mgr ();
2202 is_fullscreen = (modest_window_mgr_get_fullscreen_mode (mgr))?1:0;
2204 parent_priv = MODEST_WINDOW_GET_PRIVATE (widget);
2206 fs_toggle_action = gtk_ui_manager_get_action (parent_priv->ui_manager, "/MenuBar/ViewMenu/ViewToggleFullscreenMenu");
2207 active = (gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (fs_toggle_action)))?1:0;
2208 if (is_fullscreen != active) {
2209 gtk_toggle_action_set_active (GTK_TOGGLE_ACTION (fs_toggle_action), is_fullscreen);
2211 toolbar_resize (MODEST_MSG_VIEW_WINDOW (widget));
2219 modest_msg_view_window_show_toolbar (ModestWindow *self,
2220 gboolean show_toolbar)
2222 ModestMsgViewWindowPrivate *priv = NULL;
2223 ModestWindowPrivate *parent_priv;
2224 GtkWidget *reply_button = NULL, *menu = NULL;
2225 GtkWidget *placeholder = NULL;
2227 const gchar *action_name;
2230 parent_priv = MODEST_WINDOW_GET_PRIVATE(self);
2231 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(self);
2233 /* Set optimized view status */
2234 priv->optimized_view = !show_toolbar;
2236 if (!parent_priv->toolbar) {
2237 parent_priv->toolbar = gtk_ui_manager_get_widget (parent_priv->ui_manager,
2239 gtk_widget_set_no_show_all (parent_priv->toolbar, TRUE);
2241 priv->progress_toolitem = GTK_WIDGET (gtk_tool_item_new ());
2242 priv->cancel_toolitem = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/ToolbarCancel");
2243 priv->next_toolitem = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/ToolbarMessageNext");
2244 priv->prev_toolitem = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/ToolbarMessageBack");
2245 toolbar_resize (MODEST_MSG_VIEW_WINDOW (self));
2247 /* Add ProgressBar (Transfer toolbar) */
2248 priv->progress_bar = modest_progress_bar_new ();
2249 gtk_widget_set_no_show_all (priv->progress_bar, TRUE);
2250 placeholder = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/ProgressbarView");
2251 insert_index = gtk_toolbar_get_item_index(GTK_TOOLBAR (parent_priv->toolbar), GTK_TOOL_ITEM(placeholder));
2252 gtk_container_add (GTK_CONTAINER (priv->progress_toolitem), priv->progress_bar);
2253 gtk_toolbar_insert(GTK_TOOLBAR(parent_priv->toolbar), GTK_TOOL_ITEM (priv->progress_toolitem), insert_index);
2255 /* Connect cancel 'clicked' signal to abort progress mode */
2256 g_signal_connect(priv->cancel_toolitem, "clicked",
2257 G_CALLBACK(cancel_progressbar),
2260 /* Add it to the observers list */
2261 priv->progress_widgets = g_slist_prepend(priv->progress_widgets, priv->progress_bar);
2264 hildon_window_add_toolbar (HILDON_WINDOW (self),
2265 GTK_TOOLBAR (parent_priv->toolbar));
2267 /* Set reply button tap and hold menu */
2268 reply_button = gtk_ui_manager_get_widget (parent_priv->ui_manager,
2269 "/ToolBar/ToolbarMessageReply");
2270 menu = gtk_ui_manager_get_widget (parent_priv->ui_manager,
2271 "/ToolbarReplyCSM");
2272 gtk_widget_tap_and_hold_setup (GTK_WIDGET (reply_button), menu, NULL, 0);
2276 /* Quick hack: this prevents toolbar icons "dance" when progress bar show status is changed */
2277 /* TODO: resize mode migth be GTK_RESIZE_QUEUE, in order to avoid unneccesary shows */
2278 gtk_container_set_resize_mode (GTK_CONTAINER(parent_priv->toolbar), GTK_RESIZE_IMMEDIATE);
2280 gtk_widget_show (GTK_WIDGET (parent_priv->toolbar));
2281 if (modest_msg_view_window_transfer_mode_enabled (MODEST_MSG_VIEW_WINDOW (self)))
2282 set_toolbar_mode (MODEST_MSG_VIEW_WINDOW (self), TOOLBAR_MODE_TRANSFER);
2284 set_toolbar_mode (MODEST_MSG_VIEW_WINDOW (self), TOOLBAR_MODE_NORMAL);
2287 gtk_widget_set_no_show_all (parent_priv->toolbar, TRUE);
2288 gtk_widget_hide (GTK_WIDGET (parent_priv->toolbar));
2291 /* Update also the actions (to update the toggles in the
2292 menus), we have to do it manually because some other window
2293 of the same time could have changed it (remember that the
2294 toolbar fullscreen mode is shared by all the windows of the
2296 if (modest_window_mgr_get_fullscreen_mode (modest_runtime_get_window_mgr ()))
2297 action_name = "/MenuBar/ViewMenu/ViewShowToolbarMenu/ViewShowToolbarFullScreenMenu";
2299 action_name = "/MenuBar/ViewMenu/ViewShowToolbarMenu/ViewShowToolbarNormalScreenMenu";
2301 action = gtk_ui_manager_get_action (parent_priv->ui_manager, action_name);
2302 modest_utils_toggle_action_set_active_block_notify (GTK_TOGGLE_ACTION (action),
2307 modest_msg_view_window_clipboard_owner_change (GtkClipboard *clipboard,
2309 ModestMsgViewWindow *window)
2311 if (!GTK_WIDGET_VISIBLE (window))
2314 modest_window_check_dimming_rules_group (MODEST_WINDOW (window), MODEST_DIMMING_RULES_CLIPBOARD);
2318 modest_msg_view_window_transfer_mode_enabled (ModestMsgViewWindow *self)
2320 ModestMsgViewWindowPrivate *priv;
2322 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self), FALSE);
2323 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(self);
2325 return priv->current_toolbar_mode == TOOLBAR_MODE_TRANSFER;
2329 cancel_progressbar (GtkToolButton *toolbutton,
2330 ModestMsgViewWindow *self)
2333 ModestMsgViewWindowPrivate *priv;
2335 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(self);
2337 /* Get operation observers and cancel its current operation */
2338 tmp = priv->progress_widgets;
2340 modest_progress_object_cancel_current_operation (MODEST_PROGRESS_OBJECT(tmp->data));
2341 tmp=g_slist_next(tmp);
2345 observers_empty (ModestMsgViewWindow *self)
2348 ModestMsgViewWindowPrivate *priv;
2349 gboolean is_empty = TRUE;
2350 guint pending_ops = 0;
2352 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(self);
2353 tmp = priv->progress_widgets;
2355 /* Check all observers */
2356 while (tmp && is_empty) {
2357 pending_ops = modest_progress_object_num_pending_operations (MODEST_PROGRESS_OBJECT(tmp->data));
2358 is_empty = pending_ops == 0;
2360 tmp = g_slist_next(tmp);
2367 on_account_removed (TnyAccountStore *account_store,
2368 TnyAccount *account,
2371 /* Do nothing if it's a transport account, because we only
2372 show the messages of a store account */
2373 if (tny_account_get_account_type(account) == TNY_ACCOUNT_TYPE_STORE) {
2374 const gchar *parent_acc = NULL;
2375 const gchar *our_acc = NULL;
2377 our_acc = modest_window_get_active_account (MODEST_WINDOW (user_data));
2378 parent_acc = modest_tny_account_get_parent_modest_account_name_for_server_account (account);
2380 /* Close this window if I'm showing a message of the removed account */
2381 if (strcmp (parent_acc, our_acc) == 0)
2382 modest_ui_actions_on_close_window (NULL, MODEST_WINDOW (user_data));
2387 on_mail_operation_started (ModestMailOperation *mail_op,
2390 ModestMsgViewWindow *self;
2391 ModestMailOperationTypeOperation op_type;
2393 ModestMsgViewWindowPrivate *priv;
2394 GObject *source = NULL;
2396 self = MODEST_MSG_VIEW_WINDOW (user_data);
2397 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
2398 op_type = modest_mail_operation_get_type_operation (mail_op);
2399 tmp = priv->progress_widgets;
2400 source = modest_mail_operation_get_source(mail_op);
2401 if (G_OBJECT (self) == source) {
2402 if (op_type == MODEST_MAIL_OPERATION_TYPE_RECEIVE || op_type == MODEST_MAIL_OPERATION_TYPE_OPEN ) {
2403 set_toolbar_transfer_mode(self);
2405 modest_progress_object_add_operation (
2406 MODEST_PROGRESS_OBJECT (tmp->data),
2408 tmp = g_slist_next (tmp);
2412 g_object_unref (source);
2416 on_mail_operation_finished (ModestMailOperation *mail_op,
2419 ModestMsgViewWindow *self;
2420 ModestMailOperationTypeOperation op_type;
2422 ModestMsgViewWindowPrivate *priv;
2424 self = MODEST_MSG_VIEW_WINDOW (user_data);
2425 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
2426 op_type = modest_mail_operation_get_type_operation (mail_op);
2427 tmp = priv->progress_widgets;
2429 if (op_type == MODEST_MAIL_OPERATION_TYPE_RECEIVE || op_type == MODEST_MAIL_OPERATION_TYPE_OPEN ) {
2431 modest_progress_object_remove_operation (MODEST_PROGRESS_OBJECT (tmp->data),
2433 tmp = g_slist_next (tmp);
2436 /* If no more operations are being observed, NORMAL mode is enabled again */
2437 if (observers_empty (self)) {
2438 set_toolbar_mode (self, TOOLBAR_MODE_NORMAL);
2441 /* Update dimming rules. We have to do this right here
2442 and not in view_msg_cb because at that point the
2443 transfer mode is still enabled so the dimming rule
2444 won't let the user delete the message that has been
2445 readed for example */
2446 modest_ui_actions_check_toolbar_dimming_rules (MODEST_WINDOW (self));
2447 modest_ui_actions_check_menu_dimming_rules (MODEST_WINDOW (self));
2452 on_queue_changed (ModestMailOperationQueue *queue,
2453 ModestMailOperation *mail_op,
2454 ModestMailOperationQueueNotification type,
2455 ModestMsgViewWindow *self)
2457 ModestMsgViewWindowPrivate *priv;
2459 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
2461 /* If this operations was created by another window, do nothing */
2462 if (!modest_mail_operation_is_mine (mail_op, G_OBJECT(self)))
2465 if (type == MODEST_MAIL_OPERATION_QUEUE_OPERATION_ADDED) {
2466 priv->sighandlers = modest_signal_mgr_connect (priv->sighandlers,
2468 "operation-started",
2469 G_CALLBACK (on_mail_operation_started),
2471 priv->sighandlers = modest_signal_mgr_connect (priv->sighandlers,
2473 "operation-finished",
2474 G_CALLBACK (on_mail_operation_finished),
2476 } else if (type == MODEST_MAIL_OPERATION_QUEUE_OPERATION_REMOVED) {
2477 priv->sighandlers = modest_signal_mgr_disconnect (priv->sighandlers,
2479 "operation-started");
2480 priv->sighandlers = modest_signal_mgr_disconnect (priv->sighandlers,
2482 "operation-finished");
2487 modest_msg_view_window_get_attachments (ModestMsgViewWindow *win)
2489 ModestMsgViewWindowPrivate *priv;
2490 TnyList *selected_attachments = NULL;
2492 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (win), NULL);
2493 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (win);
2495 selected_attachments = modest_msg_view_get_selected_attachments (MODEST_MSG_VIEW (priv->msg_view));
2497 return selected_attachments;
2503 guint banner_idle_id;
2504 } DecodeAsyncHelper;
2507 decode_async_banner_idle (gpointer user_data)
2509 DecodeAsyncHelper *helper = (DecodeAsyncHelper *) user_data;
2511 helper->banner_idle_id = 0;
2512 helper->banner = hildon_banner_show_animation (NULL, NULL, _("mail_me_opening"));
2513 g_object_ref (helper->banner);
2519 on_decode_to_stream_async_handler (TnyMimePart *mime_part,
2525 DecodeAsyncHelper *helper = (DecodeAsyncHelper *) user_data;
2527 if (helper->banner_idle_id > 0) {
2528 g_source_remove (helper->banner_idle_id);
2529 helper->banner_idle_id = 0;
2531 if (helper->banner) {
2532 gtk_widget_destroy (helper->banner);
2534 if (cancelled || err) {
2535 modest_platform_information_banner (NULL, NULL,
2536 _("mail_ib_file_operation_failed"));
2540 /* make the file read-only */
2541 g_chmod(helper->filepath, 0444);
2543 /* Activate the file */
2544 modest_platform_activate_file (helper->filepath, tny_mime_part_get_content_type (mime_part));
2548 g_free (helper->filepath);
2549 g_object_unref (helper->banner);
2550 g_slice_free (DecodeAsyncHelper, helper);
2554 modest_msg_view_window_view_attachment (ModestMsgViewWindow *window, TnyMimePart *mime_part)
2556 ModestMsgViewWindowPrivate *priv;
2557 const gchar *msg_uid;
2558 gchar *attachment_uid = NULL;
2559 gint attachment_index = 0;
2560 TnyList *attachments;
2562 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window));
2563 g_return_if_fail (TNY_IS_MIME_PART (mime_part) || (mime_part == NULL));
2564 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
2566 msg_uid = modest_msg_view_window_get_message_uid (MODEST_MSG_VIEW_WINDOW (window));
2567 attachments = modest_msg_view_get_attachments (MODEST_MSG_VIEW (priv->msg_view));
2568 attachment_index = modest_list_index (attachments, (GObject *) mime_part);
2569 g_object_unref (attachments);
2571 if (msg_uid && attachment_index >= 0) {
2572 attachment_uid = g_strdup_printf ("%s/%d", msg_uid, attachment_index);
2575 if (mime_part == NULL) {
2576 gboolean error = FALSE;
2577 TnyList *selected_attachments = modest_msg_view_get_selected_attachments (MODEST_MSG_VIEW (priv->msg_view));
2578 if (selected_attachments == NULL || tny_list_get_length (selected_attachments) == 0) {
2580 } else if (tny_list_get_length (selected_attachments) > 1) {
2581 hildon_banner_show_information (NULL, NULL, _("mcen_ib_unable_to_display_more"));
2585 iter = tny_list_create_iterator (selected_attachments);
2586 mime_part = (TnyMimePart *) tny_iterator_get_current (iter);
2587 g_object_unref (iter);
2589 g_object_unref (selected_attachments);
2594 g_object_ref (mime_part);
2597 if (tny_mime_part_is_purged (mime_part)) {
2598 g_object_unref (mime_part);
2602 if (!modest_tny_mime_part_is_msg (mime_part)) {
2603 gchar *filepath = NULL;
2604 const gchar *att_filename = tny_mime_part_get_filename (mime_part);
2605 gboolean show_error_banner = FALSE;
2606 TnyFsStream *temp_stream = NULL;
2607 temp_stream = modest_utils_create_temp_stream (att_filename, attachment_uid,
2610 if (temp_stream != NULL) {
2611 DecodeAsyncHelper *helper = g_slice_new (DecodeAsyncHelper);
2612 helper->filepath = g_strdup (filepath);
2613 helper->banner = NULL;
2614 helper->banner_idle_id = g_timeout_add (1000, decode_async_banner_idle, helper);
2615 tny_mime_part_decode_to_stream_async (mime_part, TNY_STREAM (temp_stream),
2616 on_decode_to_stream_async_handler,
2619 g_object_unref (temp_stream);
2620 /* NOTE: files in the temporary area will be automatically
2621 * cleaned after some time if they are no longer in use */
2623 if (filepath != NULL) {
2624 const gchar *content_type;
2625 /* the file may already exist but it isn't writable,
2626 * let's try to open it anyway */
2627 content_type = tny_mime_part_get_content_type (mime_part);
2628 modest_platform_activate_file (filepath, content_type);
2631 g_warning ("%s: modest_utils_create_temp_stream failed", __FUNCTION__);
2632 show_error_banner = TRUE;
2635 if (show_error_banner)
2636 modest_platform_information_banner (NULL, NULL, _("mail_ib_file_operation_failed"));
2638 /* message attachment */
2639 TnyHeader *header = NULL;
2640 ModestWindowMgr *mgr;
2641 ModestWindow *msg_win = NULL;
2644 header = tny_msg_get_header (TNY_MSG (mime_part));
2645 mgr = modest_runtime_get_window_mgr ();
2646 found = modest_window_mgr_find_registered_header (mgr, header, &msg_win);
2649 if (msg_win) /* there is already a window for this uid; top it */
2650 gtk_window_present (GTK_WINDOW(msg_win));
2652 /* if it's found, but there is no msg_win, it's probably in the process of being created;
2653 * thus, we don't do anything */
2654 g_warning ("window for is already being created");
2656 /* it's not found, so create a new window for it */
2657 modest_window_mgr_register_header (mgr, header, attachment_uid); /* register the uid before building the window */
2658 gchar *account = g_strdup (modest_window_get_active_account (MODEST_WINDOW (window)));
2660 account = modest_account_mgr_get_default_account (modest_runtime_get_account_mgr ());
2661 msg_win = modest_msg_view_window_new_for_attachment (TNY_MSG (mime_part), account, attachment_uid);
2662 modest_window_set_zoom (MODEST_WINDOW (msg_win),
2663 modest_window_get_zoom (MODEST_WINDOW (window)));
2664 modest_window_mgr_register_window (mgr, msg_win);
2665 gtk_widget_show_all (GTK_WIDGET (msg_win));
2668 g_object_unref (mime_part);
2681 GnomeVFSResult result;
2684 static void save_mime_part_info_free (SaveMimePartInfo *info, gboolean with_struct);
2685 static gboolean idle_save_mime_part_show_result (SaveMimePartInfo *info);
2686 static gpointer save_mime_part_to_file (SaveMimePartInfo *info);
2687 static void save_mime_parts_to_file_with_checks (SaveMimePartInfo *info);
2690 save_mime_part_info_free (SaveMimePartInfo *info, gboolean with_struct)
2694 for (node = info->pairs; node != NULL; node = g_list_next (node)) {
2695 SaveMimePartPair *pair = (SaveMimePartPair *) node->data;
2696 g_free (pair->filename);
2697 g_object_unref (pair->part);
2698 g_slice_free (SaveMimePartPair, pair);
2700 g_list_free (info->pairs);
2703 gtk_widget_destroy (info->banner);
2704 g_slice_free (SaveMimePartInfo, info);
2709 idle_save_mime_part_show_result (SaveMimePartInfo *info)
2711 if (info->pairs != NULL) {
2712 save_mime_part_to_file (info);
2714 /* This is a GDK lock because we are an idle callback and
2715 * hildon_banner_show_information is or does Gtk+ code */
2717 gdk_threads_enter (); /* CHECKED */
2718 save_mime_part_info_free (info, TRUE);
2719 if (info->result == GNOME_VFS_OK) {
2720 hildon_banner_show_information (NULL, NULL, _CS("sfil_ib_saved"));
2721 } else if (info->result == GNOME_VFS_ERROR_NO_SPACE) {
2722 hildon_banner_show_information (NULL, NULL, dgettext("ke-recv",
2723 "cerm_device_memory_full"));
2725 hildon_banner_show_information (NULL, NULL, _("mail_ib_file_operation_failed"));
2727 gdk_threads_leave (); /* CHECKED */
2734 save_mime_part_to_file (SaveMimePartInfo *info)
2736 GnomeVFSHandle *handle;
2738 SaveMimePartPair *pair = (SaveMimePartPair *) info->pairs->data;
2740 info->result = gnome_vfs_create (&handle, pair->filename, GNOME_VFS_OPEN_WRITE, FALSE, 0644);
2741 if (info->result == GNOME_VFS_OK) {
2742 GError *error = NULL;
2743 stream = tny_vfs_stream_new (handle);
2744 if (tny_mime_part_decode_to_stream (pair->part, stream, &error) < 0) {
2745 g_warning ("modest: could not save attachment %s: %d (%s)\n", pair->filename, error?error->code:-1, error?error->message:"Unknown error");
2747 info->result = GNOME_VFS_ERROR_IO;
2749 g_object_unref (G_OBJECT (stream));
2750 g_object_unref (pair->part);
2751 g_slice_free (SaveMimePartPair, pair);
2752 info->pairs = g_list_delete_link (info->pairs, info->pairs);
2754 g_warning ("modest: could not create save attachment %s: %s\n", pair->filename, gnome_vfs_result_to_string (info->result));
2755 save_mime_part_info_free (info, FALSE);
2758 g_idle_add ((GSourceFunc) idle_save_mime_part_show_result, info);
2763 save_mime_parts_to_file_with_checks (SaveMimePartInfo *info)
2765 gboolean is_ok = TRUE;
2766 gint replaced_files = 0;
2767 const GList *files = info->pairs;
2770 for (iter = files; (iter != NULL) && (replaced_files < 2); iter = g_list_next(iter)) {
2771 SaveMimePartPair *pair = iter->data;
2772 if (modest_utils_file_exists (pair->filename)) {
2776 if (replaced_files) {
2777 GtkWidget *confirm_overwrite_dialog;
2778 const gchar *message = (replaced_files == 1) ?
2779 _FM("docm_nc_replace_file") : _FM("docm_nc_replace_multiple");
2780 confirm_overwrite_dialog = hildon_note_new_confirmation (NULL, message);
2781 if (gtk_dialog_run (GTK_DIALOG (confirm_overwrite_dialog)) != GTK_RESPONSE_OK) {
2784 gtk_widget_destroy (confirm_overwrite_dialog);
2788 save_mime_part_info_free (info, TRUE);
2790 GtkWidget *banner = hildon_banner_show_animation (NULL, NULL,
2791 _CS("sfil_ib_saving"));
2792 info->banner = banner;
2793 g_thread_create ((GThreadFunc)save_mime_part_to_file, info, FALSE, NULL);
2799 modest_msg_view_window_save_attachments (ModestMsgViewWindow *window, TnyList *mime_parts)
2801 ModestMsgViewWindowPrivate *priv;
2802 GList *files_to_save = NULL;
2803 GtkWidget *save_dialog = NULL;
2804 gchar *folder = NULL;
2805 const gchar *filename = NULL;
2806 gchar *save_multiple_str = NULL;
2808 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window));
2809 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
2811 if (mime_parts == NULL) {
2812 mime_parts = modest_msg_view_get_selected_attachments (MODEST_MSG_VIEW (priv->msg_view));
2813 if (mime_parts == NULL || tny_list_get_length (mime_parts) == 0)
2816 g_object_ref (mime_parts);
2819 /* prepare dialog */
2820 if (tny_list_get_length (mime_parts) == 1) {
2822 /* only one attachment selected */
2823 iter = tny_list_create_iterator (mime_parts);
2824 TnyMimePart *mime_part = (TnyMimePart *) tny_iterator_get_current (iter);
2825 g_object_unref (iter);
2826 if (!modest_tny_mime_part_is_msg (mime_part) &&
2827 modest_tny_mime_part_is_attachment_for_modest (mime_part) &&
2828 !tny_mime_part_is_purged (mime_part)) {
2829 filename = tny_mime_part_get_filename (mime_part);
2831 /* TODO: show any error? */
2832 g_warning ("Tried to save a non-file attachment");
2833 g_object_unref (mime_parts);
2836 g_object_unref (mime_part);
2838 save_multiple_str = g_strdup_printf (_FM("sfil_va_number_of_objects_attachments"),
2839 tny_list_get_length (mime_parts));
2842 save_dialog = hildon_file_chooser_dialog_new (GTK_WINDOW (window),
2843 GTK_FILE_CHOOSER_ACTION_SAVE);
2846 folder = g_build_filename (g_get_home_dir (), DEFAULT_FOLDER, NULL);
2847 gtk_file_chooser_set_current_folder (GTK_FILE_CHOOSER (save_dialog), folder);
2851 if (filename != NULL)
2852 gtk_file_chooser_set_current_name (GTK_FILE_CHOOSER (save_dialog),
2855 /* if multiple, set multiple string */
2856 if (save_multiple_str) {
2857 g_object_set (G_OBJECT (save_dialog), "save-multiple", save_multiple_str, NULL);
2858 gtk_window_set_title (GTK_WINDOW (save_dialog), _FM("sfil_ti_save_objects_files"));
2862 if (gtk_dialog_run (GTK_DIALOG (save_dialog)) == GTK_RESPONSE_OK) {
2863 gchar *chooser_uri = gtk_file_chooser_get_uri (GTK_FILE_CHOOSER (save_dialog));
2865 if (!modest_utils_folder_writable (chooser_uri)) {
2866 hildon_banner_show_information
2867 (NULL, NULL, dgettext("hildon-fm", "sfil_ib_readonly_location"));
2871 iter = tny_list_create_iterator (mime_parts);
2872 while (!tny_iterator_is_done (iter)) {
2873 TnyMimePart *mime_part = (TnyMimePart *) tny_iterator_get_current (iter);
2875 if ((modest_tny_mime_part_is_attachment_for_modest (mime_part)) &&
2876 !tny_mime_part_is_purged (mime_part) &&
2877 (tny_mime_part_get_filename (mime_part) != NULL)) {
2878 SaveMimePartPair *pair;
2880 pair = g_slice_new0 (SaveMimePartPair);
2881 if (save_multiple_str) {
2882 gchar *escaped = gnome_vfs_escape_slashes (
2883 tny_mime_part_get_filename (mime_part));
2884 pair->filename = g_build_filename (chooser_uri, escaped, NULL);
2887 pair->filename = g_strdup (chooser_uri);
2889 pair->part = mime_part;
2890 files_to_save = g_list_prepend (files_to_save, pair);
2892 tny_iterator_next (iter);
2894 g_object_unref (iter);
2896 g_free (chooser_uri);
2899 gtk_widget_destroy (save_dialog);
2901 g_object_unref (mime_parts);
2903 if (files_to_save != NULL) {
2904 SaveMimePartInfo *info = g_slice_new0 (SaveMimePartInfo);
2905 info->pairs = files_to_save;
2906 info->result = TRUE;
2907 save_mime_parts_to_file_with_checks (info);
2912 show_remove_attachment_information (gpointer userdata)
2914 ModestMsgViewWindow *window = (ModestMsgViewWindow *) userdata;
2915 ModestMsgViewWindowPrivate *priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
2917 /* We're outside the main lock */
2918 gdk_threads_enter ();
2920 if (priv->remove_attachment_banner != NULL) {
2921 gtk_widget_destroy (priv->remove_attachment_banner);
2922 g_object_unref (priv->remove_attachment_banner);
2925 priv->remove_attachment_banner = g_object_ref (
2926 hildon_banner_show_animation (NULL, NULL, _("mcen_ib_removing_attachment")));
2928 gdk_threads_leave ();
2934 modest_msg_view_window_remove_attachments (ModestMsgViewWindow *window, gboolean get_all)
2936 ModestMsgViewWindowPrivate *priv;
2937 TnyList *mime_parts = NULL;
2938 gchar *confirmation_message;
2944 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window));
2945 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
2948 mime_parts = modest_msg_view_get_attachments (MODEST_MSG_VIEW (priv->msg_view));
2950 mime_parts = modest_msg_view_get_selected_attachments (MODEST_MSG_VIEW (priv->msg_view));
2952 /* Remove already purged messages from mime parts list */
2953 iter = tny_list_create_iterator (mime_parts);
2954 while (!tny_iterator_is_done (iter)) {
2955 TnyMimePart *part = TNY_MIME_PART (tny_iterator_get_current (iter));
2956 tny_iterator_next (iter);
2957 if (tny_mime_part_is_purged (part)) {
2958 tny_list_remove (mime_parts, (GObject *) part);
2960 g_object_unref (part);
2962 g_object_unref (iter);
2964 if (tny_list_get_length (mime_parts) == 0) {
2965 g_object_unref (mime_parts);
2969 n_attachments = tny_list_get_length (mime_parts);
2970 if (n_attachments == 1) {
2974 iter = tny_list_create_iterator (mime_parts);
2975 part = (TnyMimePart *) tny_iterator_get_current (iter);
2976 g_object_unref (iter);
2977 if (modest_tny_mime_part_is_msg (part)) {
2979 header = tny_msg_get_header (TNY_MSG (part));
2980 filename = tny_header_dup_subject (header);
2981 g_object_unref (header);
2982 if (filename == NULL)
2983 filename = g_strdup (_("mail_va_no_subject"));
2985 filename = g_strdup (tny_mime_part_get_filename (TNY_MIME_PART (part)));
2987 confirmation_message = g_strdup_printf (_("mcen_nc_purge_file_text"), filename);
2989 g_object_unref (part);
2991 confirmation_message = g_strdup_printf (ngettext("mcen_nc_purge_file_text",
2992 "mcen_nc_purge_files_text",
2993 n_attachments), n_attachments);
2995 response = modest_platform_run_confirmation_dialog (GTK_WINDOW (window),
2996 confirmation_message);
2997 g_free (confirmation_message);
2999 if (response != GTK_RESPONSE_OK) {
3000 g_object_unref (mime_parts);
3004 priv->purge_timeout = g_timeout_add (2000, show_remove_attachment_information, window);
3006 iter = tny_list_create_iterator (mime_parts);
3007 while (!tny_iterator_is_done (iter)) {
3010 part = (TnyMimePart *) tny_iterator_get_current (iter);
3011 tny_mime_part_set_purged (TNY_MIME_PART (part));
3012 g_object_unref (part);
3013 tny_iterator_next (iter);
3015 g_object_unref (iter);
3017 msg = tny_msg_view_get_msg (TNY_MSG_VIEW (priv->msg_view));
3018 tny_msg_view_clear (TNY_MSG_VIEW (priv->msg_view));
3019 tny_msg_rewrite_cache (msg);
3020 tny_msg_view_set_msg (TNY_MSG_VIEW (priv->msg_view), msg);
3021 g_object_unref (msg);
3023 g_object_unref (mime_parts);
3025 if (priv->purge_timeout > 0) {
3026 g_source_remove (priv->purge_timeout);
3027 priv->purge_timeout = 0;
3030 if (priv->remove_attachment_banner) {
3031 gtk_widget_destroy (priv->remove_attachment_banner);
3032 g_object_unref (priv->remove_attachment_banner);
3033 priv->remove_attachment_banner = NULL;
3041 update_window_title (ModestMsgViewWindow *window)
3043 ModestMsgViewWindowPrivate *priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
3045 TnyHeader *header = NULL;
3046 gchar *subject = NULL;
3048 msg = tny_msg_view_get_msg (TNY_MSG_VIEW (priv->msg_view));
3051 header = tny_msg_get_header (msg);
3052 subject = tny_header_dup_subject (header);
3053 g_object_unref (header);
3054 g_object_unref (msg);
3057 if ((subject == NULL)||(subject[0] == '\0')) {
3059 subject = g_strdup (_("mail_va_no_subject"));
3062 gtk_window_set_title (GTK_WINDOW (window), subject);
3066 static void on_move_focus (GtkWidget *widget,
3067 GtkDirectionType direction,
3070 g_signal_stop_emission_by_name (G_OBJECT (widget), "move-focus");