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 on_fetch_image (ModestMsgView *msgview,
161 ModestMsgViewWindow *window);
163 /* list my signals */
169 static const GtkToggleActionEntry msg_view_toggle_action_entries [] = {
170 { "FindInMessage", MODEST_TOOLBAR_ICON_FIND, N_("qgn_toolb_gene_find"), NULL, NULL, G_CALLBACK (modest_msg_view_window_toggle_find_toolbar), FALSE },
171 { "ToolsFindInMessage", NULL, N_("mcen_me_viewer_find"), "<CTRL>F", NULL, G_CALLBACK (modest_msg_view_window_toggle_find_toolbar), FALSE },
174 static const GtkRadioActionEntry msg_view_zoom_action_entries [] = {
175 { "Zoom50", NULL, N_("mcen_me_viewer_50"), NULL, NULL, 50 },
176 { "Zoom80", NULL, N_("mcen_me_viewer_80"), NULL, NULL, 80 },
177 { "Zoom100", NULL, N_("mcen_me_viewer_100"), NULL, NULL, 100 },
178 { "Zoom120", NULL, N_("mcen_me_viewer_120"), NULL, NULL, 120 },
179 { "Zoom150", NULL, N_("mcen_me_viewer_150"), NULL, NULL, 150 },
180 { "Zoom200", NULL, N_("mcen_me_viewer_200"), NULL, NULL, 200 }
183 typedef struct _ModestMsgViewWindowPrivate ModestMsgViewWindowPrivate;
184 struct _ModestMsgViewWindowPrivate {
187 GtkWidget *main_scroll;
188 GtkWidget *find_toolbar;
191 /* Progress observers */
192 GtkWidget *progress_bar;
193 GSList *progress_widgets;
196 GtkWidget *progress_toolitem;
197 GtkWidget *cancel_toolitem;
198 GtkWidget *prev_toolitem;
199 GtkWidget *next_toolitem;
200 ModestToolBarModes current_toolbar_mode;
202 /* Optimized view enabled */
203 gboolean optimized_view;
205 /* Whether this was created via the *_new_for_search_result() function. */
206 gboolean is_search_result;
208 /* Whether the message is in outbox */
211 /* A reference to the @model of the header view
212 * to allow selecting previous/next messages,
213 * if the message is currently selected in the header view.
215 const gchar *header_folder_id;
216 GtkTreeModel *header_model;
217 GtkTreeRowReference *row_reference;
218 GtkTreeRowReference *next_row_reference;
220 gulong clipboard_change_handler;
221 gulong queue_change_handler;
222 gulong account_removed_handler;
223 gulong row_changed_handler;
224 gulong row_deleted_handler;
225 gulong row_inserted_handler;
226 gulong rows_reordered_handler;
229 GtkWidget *remove_attachment_banner;
231 guint progress_bar_timeout;
238 #define MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE((o), \
239 MODEST_TYPE_MSG_VIEW_WINDOW, \
240 ModestMsgViewWindowPrivate))
242 static GtkWindowClass *parent_class = NULL;
244 /* uncomment the following if you have defined any signals */
245 static guint signals[LAST_SIGNAL] = {0};
248 modest_msg_view_window_get_type (void)
250 static GType my_type = 0;
252 static const GTypeInfo my_info = {
253 sizeof(ModestMsgViewWindowClass),
254 NULL, /* base init */
255 NULL, /* base finalize */
256 (GClassInitFunc) modest_msg_view_window_class_init,
257 NULL, /* class finalize */
258 NULL, /* class data */
259 sizeof(ModestMsgViewWindow),
261 (GInstanceInitFunc) modest_msg_view_window_init,
264 my_type = g_type_register_static (MODEST_TYPE_WINDOW,
265 "ModestMsgViewWindow",
268 static const GInterfaceInfo modest_header_view_observer_info =
270 (GInterfaceInitFunc) modest_header_view_observer_init,
271 NULL, /* interface_finalize */
272 NULL /* interface_data */
275 g_type_add_interface_static (my_type,
276 MODEST_TYPE_HEADER_VIEW_OBSERVER,
277 &modest_header_view_observer_info);
283 save_state (ModestWindow *self)
285 modest_widget_memory_save (modest_runtime_get_conf (),
287 MODEST_CONF_MSG_VIEW_WINDOW_KEY);
292 restore_settings (ModestMsgViewWindow *self)
295 ModestWindowPrivate *parent_priv = MODEST_WINDOW_GET_PRIVATE (self);
298 conf = modest_runtime_get_conf ();
299 action = gtk_ui_manager_get_action (parent_priv->ui_manager,
300 "/MenuBar/ViewMenu/ViewShowToolbarMenu/ViewShowToolbarNormalScreenMenu");
301 gtk_toggle_action_set_active (GTK_TOGGLE_ACTION (action),
302 modest_conf_get_bool (conf, MODEST_CONF_MSG_VIEW_WINDOW_SHOW_TOOLBAR, NULL));
303 action = gtk_ui_manager_get_action (parent_priv->ui_manager,
304 "/MenuBar/ViewMenu/ViewShowToolbarMenu/ViewShowToolbarFullScreenMenu");
305 gtk_toggle_action_set_active (GTK_TOGGLE_ACTION (action),
306 modest_conf_get_bool (conf, MODEST_CONF_MSG_VIEW_WINDOW_SHOW_TOOLBAR_FULLSCREEN, NULL));
307 modest_widget_memory_restore (conf,
309 MODEST_CONF_MSG_VIEW_WINDOW_KEY);
313 modest_msg_view_window_class_init (ModestMsgViewWindowClass *klass)
315 GObjectClass *gobject_class;
316 ModestWindowClass *modest_window_class;
317 gobject_class = (GObjectClass*) klass;
318 modest_window_class = (ModestWindowClass *) klass;
320 parent_class = g_type_class_peek_parent (klass);
321 gobject_class->finalize = modest_msg_view_window_finalize;
323 modest_window_class->set_zoom_func = modest_msg_view_window_set_zoom;
324 modest_window_class->get_zoom_func = modest_msg_view_window_get_zoom;
325 modest_window_class->zoom_minus_func = modest_msg_view_window_zoom_minus;
326 modest_window_class->zoom_plus_func = modest_msg_view_window_zoom_plus;
327 modest_window_class->show_toolbar_func = modest_msg_view_window_show_toolbar;
328 modest_window_class->disconnect_signals_func = modest_msg_view_window_disconnect_signals;
330 g_type_class_add_private (gobject_class, sizeof(ModestMsgViewWindowPrivate));
332 modest_window_class->save_state_func = save_state;
334 signals[MSG_CHANGED_SIGNAL] =
335 g_signal_new ("msg-changed",
336 G_TYPE_FROM_CLASS (gobject_class),
338 G_STRUCT_OFFSET (ModestMsgViewWindowClass, msg_changed),
340 modest_marshal_VOID__POINTER_POINTER,
341 G_TYPE_NONE, 2, G_TYPE_POINTER, G_TYPE_POINTER);
344 static void modest_header_view_observer_init(
345 ModestHeaderViewObserverIface *iface_class)
347 iface_class->update_func = modest_msg_view_window_update_model_replaced;
351 modest_msg_view_window_init (ModestMsgViewWindow *obj)
353 ModestMsgViewWindowPrivate *priv;
354 ModestWindowPrivate *parent_priv = NULL;
355 GtkActionGroup *action_group = NULL;
356 GError *error = NULL;
357 GdkPixbuf *window_icon;
359 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(obj);
360 parent_priv = MODEST_WINDOW_GET_PRIVATE(obj);
361 parent_priv->ui_manager = gtk_ui_manager_new();
363 action_group = gtk_action_group_new ("ModestMsgViewWindowActions");
364 gtk_action_group_set_translation_domain (action_group, GETTEXT_PACKAGE);
366 /* Add common actions */
367 gtk_action_group_add_actions (action_group,
368 modest_action_entries,
369 G_N_ELEMENTS (modest_action_entries),
371 gtk_action_group_add_toggle_actions (action_group,
372 modest_toggle_action_entries,
373 G_N_ELEMENTS (modest_toggle_action_entries),
375 gtk_action_group_add_toggle_actions (action_group,
376 msg_view_toggle_action_entries,
377 G_N_ELEMENTS (msg_view_toggle_action_entries),
379 gtk_action_group_add_radio_actions (action_group,
380 msg_view_zoom_action_entries,
381 G_N_ELEMENTS (msg_view_zoom_action_entries),
383 G_CALLBACK (modest_ui_actions_on_change_zoom),
386 gtk_ui_manager_insert_action_group (parent_priv->ui_manager, action_group, 0);
387 g_object_unref (action_group);
389 /* Load the UI definition */
390 gtk_ui_manager_add_ui_from_file (parent_priv->ui_manager, MODEST_UIDIR "modest-msg-view-window-ui.xml",
393 g_printerr ("modest: could not merge modest-msg-view-window-ui.xml: %s\n", error->message);
394 g_error_free (error);
399 /* Add accelerators */
400 gtk_window_add_accel_group (GTK_WINDOW (obj),
401 gtk_ui_manager_get_accel_group (parent_priv->ui_manager));
403 priv->is_search_result = FALSE;
404 priv->is_outbox = FALSE;
406 priv->msg_view = NULL;
407 priv->header_model = NULL;
408 priv->header_folder_id = NULL;
409 priv->clipboard_change_handler = 0;
410 priv->queue_change_handler = 0;
411 priv->account_removed_handler = 0;
412 priv->row_changed_handler = 0;
413 priv->row_deleted_handler = 0;
414 priv->row_inserted_handler = 0;
415 priv->rows_reordered_handler = 0;
416 priv->current_toolbar_mode = TOOLBAR_MODE_NORMAL;
418 priv->optimized_view = FALSE;
419 priv->progress_bar_timeout = 0;
420 priv->purge_timeout = 0;
421 priv->remove_attachment_banner = NULL;
422 priv->msg_uid = NULL;
424 priv->sighandlers = NULL;
427 init_window (MODEST_MSG_VIEW_WINDOW(obj));
429 /* Set window icon */
430 window_icon = modest_platform_get_icon (MODEST_APP_MSG_VIEW_ICON, MODEST_ICON_SIZE_BIG);
432 gtk_window_set_icon (GTK_WINDOW (obj), window_icon);
433 g_object_unref (window_icon);
436 hildon_program_add_window (hildon_program_get_instance(),
439 modest_window_mgr_register_help_id (modest_runtime_get_window_mgr(),
440 GTK_WINDOW(obj),"applications_email_viewer");
445 set_toolbar_transfer_mode (ModestMsgViewWindow *self)
447 ModestMsgViewWindowPrivate *priv = NULL;
449 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self), FALSE);
451 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(self);
453 set_toolbar_mode (self, TOOLBAR_MODE_TRANSFER);
455 if (priv->progress_bar_timeout > 0) {
456 g_source_remove (priv->progress_bar_timeout);
457 priv->progress_bar_timeout = 0;
464 set_toolbar_mode (ModestMsgViewWindow *self,
465 ModestToolBarModes mode)
467 ModestWindowPrivate *parent_priv;
468 ModestMsgViewWindowPrivate *priv;
469 /* GtkWidget *widget = NULL; */
471 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self));
473 parent_priv = MODEST_WINDOW_GET_PRIVATE(self);
474 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(self);
476 /* Sets current toolbar mode */
477 priv->current_toolbar_mode = mode;
479 /* Update toolbar dimming state */
480 modest_ui_actions_check_toolbar_dimming_rules (MODEST_WINDOW (self));
483 case TOOLBAR_MODE_NORMAL:
484 if (priv->progress_toolitem) {
485 gtk_tool_item_set_expand (GTK_TOOL_ITEM (priv->progress_toolitem), FALSE);
486 gtk_widget_hide (priv->progress_toolitem);
489 if (priv->progress_bar)
490 gtk_widget_hide (priv->progress_bar);
492 if (priv->cancel_toolitem)
493 gtk_widget_hide (priv->cancel_toolitem);
495 if (priv->prev_toolitem)
496 gtk_widget_show (priv->prev_toolitem);
498 if (priv->next_toolitem)
499 gtk_widget_show (priv->next_toolitem);
501 /* Hide toolbar if optimized view is enabled */
502 if (priv->optimized_view) {
503 gtk_widget_set_no_show_all (parent_priv->toolbar, TRUE);
504 gtk_widget_hide (GTK_WIDGET(parent_priv->toolbar));
508 case TOOLBAR_MODE_TRANSFER:
509 if (priv->prev_toolitem)
510 gtk_widget_hide (priv->prev_toolitem);
512 if (priv->next_toolitem)
513 gtk_widget_hide (priv->next_toolitem);
515 if (priv->progress_bar)
516 gtk_widget_show (priv->progress_bar);
518 if (priv->progress_toolitem) {
519 gtk_tool_item_set_expand (GTK_TOOL_ITEM (priv->progress_toolitem), TRUE);
520 gtk_widget_show (priv->progress_toolitem);
523 if (priv->cancel_toolitem)
524 gtk_widget_show (priv->cancel_toolitem);
526 /* Show toolbar if it's hiden (optimized view ) */
527 if (priv->optimized_view) {
528 gtk_widget_set_no_show_all (parent_priv->toolbar, FALSE);
529 gtk_widget_show (GTK_WIDGET (parent_priv->toolbar));
534 g_return_if_reached ();
541 init_window (ModestMsgViewWindow *obj)
543 GtkWidget *main_vbox;
544 ModestMsgViewWindowPrivate *priv;
546 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(obj);
548 priv->msg_view = GTK_WIDGET (tny_platform_factory_new_msg_view (modest_tny_platform_factory_get_instance ()));
549 modest_msg_view_set_shadow_type (MODEST_MSG_VIEW (priv->msg_view), GTK_SHADOW_NONE);
550 main_vbox = gtk_vbox_new (FALSE, 6);
552 #ifdef MODEST_USE_MOZEMBED
553 priv->main_scroll = priv->msg_view;
554 gtk_widget_set_size_request (priv->msg_view, -1, 1600);
556 priv->main_scroll = gtk_scrolled_window_new (NULL, NULL);
557 gtk_container_add (GTK_CONTAINER (priv->main_scroll), priv->msg_view);
559 gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (priv->main_scroll), GTK_POLICY_NEVER, GTK_POLICY_AUTOMATIC);
560 gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW (priv->main_scroll), GTK_SHADOW_NONE);
561 modest_maemo_set_thumbable_scrollbar (GTK_SCROLLED_WINDOW(priv->main_scroll), TRUE);
563 gtk_box_pack_start (GTK_BOX(main_vbox), priv->main_scroll, TRUE, TRUE, 0);
564 gtk_container_add (GTK_CONTAINER(obj), main_vbox);
566 priv->find_toolbar = hildon_find_toolbar_new (NULL);
567 hildon_window_add_toolbar (HILDON_WINDOW (obj), GTK_TOOLBAR (priv->find_toolbar));
568 gtk_widget_set_no_show_all (priv->find_toolbar, TRUE);
570 gtk_widget_show_all (GTK_WIDGET(main_vbox));
574 modest_msg_view_window_disconnect_signals (ModestWindow *self)
576 ModestMsgViewWindowPrivate *priv;
577 ModestHeaderView *header_view = NULL;
578 ModestWindow *main_window = NULL;
580 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
582 if (gtk_clipboard_get (GDK_SELECTION_PRIMARY) &&
583 g_signal_handler_is_connected (gtk_clipboard_get (GDK_SELECTION_PRIMARY),
584 priv->clipboard_change_handler))
585 g_signal_handler_disconnect (gtk_clipboard_get (GDK_SELECTION_PRIMARY),
586 priv->clipboard_change_handler);
588 if (g_signal_handler_is_connected (G_OBJECT (modest_runtime_get_mail_operation_queue ()),
589 priv->queue_change_handler))
590 g_signal_handler_disconnect (G_OBJECT (modest_runtime_get_mail_operation_queue ()),
591 priv->queue_change_handler);
593 if (g_signal_handler_is_connected (G_OBJECT (modest_runtime_get_account_store ()),
594 priv->account_removed_handler))
595 g_signal_handler_disconnect (G_OBJECT (modest_runtime_get_account_store ()),
596 priv->account_removed_handler);
598 if (priv->header_model) {
599 if (g_signal_handler_is_connected(G_OBJECT (priv->header_model),
600 priv->row_changed_handler))
601 g_signal_handler_disconnect(G_OBJECT (priv->header_model),
602 priv->row_changed_handler);
604 if (g_signal_handler_is_connected(G_OBJECT (priv->header_model),
605 priv->row_deleted_handler))
606 g_signal_handler_disconnect(G_OBJECT (priv->header_model),
607 priv->row_deleted_handler);
609 if (g_signal_handler_is_connected(G_OBJECT (priv->header_model),
610 priv->row_inserted_handler))
611 g_signal_handler_disconnect(G_OBJECT (priv->header_model),
612 priv->row_inserted_handler);
614 if (g_signal_handler_is_connected(G_OBJECT (priv->header_model),
615 priv->rows_reordered_handler))
616 g_signal_handler_disconnect(G_OBJECT (priv->header_model),
617 priv->rows_reordered_handler);
620 modest_signal_mgr_disconnect_all_and_destroy (priv->sighandlers);
621 priv->sighandlers = NULL;
623 main_window = modest_window_mgr_get_main_window (modest_runtime_get_window_mgr(),
624 FALSE); /* don't create */
628 header_view = MODEST_HEADER_VIEW(
629 modest_main_window_get_child_widget(
630 MODEST_MAIN_WINDOW(main_window),
631 MODEST_MAIN_WINDOW_WIDGET_TYPE_HEADER_VIEW));
632 if (header_view == NULL)
635 modest_header_view_remove_observer(header_view,
636 MODEST_HEADER_VIEW_OBSERVER(self));
640 modest_msg_view_window_finalize (GObject *obj)
642 ModestMsgViewWindowPrivate *priv;
644 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (obj);
646 /* Sanity check: shouldn't be needed, the window mgr should
647 call this function before */
648 modest_msg_view_window_disconnect_signals (MODEST_WINDOW (obj));
650 if (priv->header_model != NULL) {
651 g_object_unref (priv->header_model);
652 priv->header_model = NULL;
655 if (priv->progress_bar_timeout > 0) {
656 g_source_remove (priv->progress_bar_timeout);
657 priv->progress_bar_timeout = 0;
660 if (priv->remove_attachment_banner) {
661 gtk_widget_destroy (priv->remove_attachment_banner);
662 g_object_unref (priv->remove_attachment_banner);
663 priv->remove_attachment_banner = NULL;
666 if (priv->purge_timeout > 0) {
667 g_source_remove (priv->purge_timeout);
668 priv->purge_timeout = 0;
671 if (priv->row_reference) {
672 gtk_tree_row_reference_free (priv->row_reference);
673 priv->row_reference = NULL;
676 if (priv->next_row_reference) {
677 gtk_tree_row_reference_free (priv->next_row_reference);
678 priv->next_row_reference = NULL;
682 g_free (priv->msg_uid);
683 priv->msg_uid = NULL;
686 G_OBJECT_CLASS(parent_class)->finalize (obj);
690 select_next_valid_row (GtkTreeModel *model,
691 GtkTreeRowReference **row_reference,
695 GtkTreeIter tmp_iter;
697 GtkTreePath *next = NULL;
698 gboolean retval = FALSE, finished;
700 g_return_val_if_fail (gtk_tree_row_reference_valid (*row_reference), FALSE);
702 path = gtk_tree_row_reference_get_path (*row_reference);
703 gtk_tree_model_get_iter (model, &tmp_iter, path);
704 gtk_tree_row_reference_free (*row_reference);
705 *row_reference = NULL;
709 TnyHeader *header = NULL;
711 if (gtk_tree_model_iter_next (model, &tmp_iter)) {
712 gtk_tree_model_get (model, &tmp_iter,
713 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
717 if (msg_is_visible (header, is_outbox)) {
718 next = gtk_tree_model_get_path (model, &tmp_iter);
719 *row_reference = gtk_tree_row_reference_new (model, next);
723 g_object_unref (header);
726 } else if (cycle && gtk_tree_model_get_iter_first (model, &tmp_iter)) {
727 next = gtk_tree_model_get_path (model, &tmp_iter);
729 /* Ensure that we are not selecting the same */
730 if (gtk_tree_path_compare (path, next) != 0) {
731 gtk_tree_model_get (model, &tmp_iter,
732 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
735 if (msg_is_visible (header, is_outbox)) {
736 *row_reference = gtk_tree_row_reference_new (model, next);
740 g_object_unref (header);
744 /* If we ended up in the same message
745 then there is no valid next
750 /* If there are no more messages and we don't
751 want to start again in the first one then
752 there is no valid next message */
758 gtk_tree_path_free (path);
760 gtk_tree_path_free (next);
765 /* TODO: This should be in _init(), with the parameters as properties. */
767 modest_msg_view_window_construct (ModestMsgViewWindow *self,
768 const gchar *modest_account_name,
769 const gchar *msg_uid)
772 ModestMsgViewWindowPrivate *priv = NULL;
773 ModestWindowPrivate *parent_priv = NULL;
774 ModestDimmingRulesGroup *menu_rules_group = NULL;
775 ModestDimmingRulesGroup *toolbar_rules_group = NULL;
776 ModestDimmingRulesGroup *clipboard_rules_group = NULL;
778 obj = G_OBJECT (self);
779 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(obj);
780 parent_priv = MODEST_WINDOW_GET_PRIVATE(obj);
782 priv->msg_uid = g_strdup (msg_uid);
785 parent_priv->menubar = modest_maemo_utils_get_manager_menubar_as_menu (parent_priv->ui_manager, "/MenuBar");
786 hildon_window_set_menu (HILDON_WINDOW(obj), GTK_MENU(parent_priv->menubar));
787 gtk_widget_show (parent_priv->menubar);
788 parent_priv->ui_dimming_manager = modest_ui_dimming_manager_new();
790 menu_rules_group = modest_dimming_rules_group_new (MODEST_DIMMING_RULES_MENU, FALSE);
791 toolbar_rules_group = modest_dimming_rules_group_new (MODEST_DIMMING_RULES_TOOLBAR, TRUE);
792 clipboard_rules_group = modest_dimming_rules_group_new (MODEST_DIMMING_RULES_CLIPBOARD, FALSE);
794 /* Add common dimming rules */
795 modest_dimming_rules_group_add_rules (menu_rules_group,
796 modest_msg_view_menu_dimming_entries,
797 G_N_ELEMENTS (modest_msg_view_menu_dimming_entries),
798 MODEST_WINDOW (self));
799 modest_dimming_rules_group_add_rules (toolbar_rules_group,
800 modest_msg_view_toolbar_dimming_entries,
801 G_N_ELEMENTS (modest_msg_view_toolbar_dimming_entries),
802 MODEST_WINDOW (self));
803 modest_dimming_rules_group_add_rules (clipboard_rules_group,
804 modest_msg_view_clipboard_dimming_entries,
805 G_N_ELEMENTS (modest_msg_view_clipboard_dimming_entries),
806 MODEST_WINDOW (self));
808 /* Insert dimming rules group for this window */
809 modest_ui_dimming_manager_insert_rules_group (parent_priv->ui_dimming_manager, menu_rules_group);
810 modest_ui_dimming_manager_insert_rules_group (parent_priv->ui_dimming_manager, toolbar_rules_group);
811 modest_ui_dimming_manager_insert_rules_group (parent_priv->ui_dimming_manager, clipboard_rules_group);
812 g_object_unref (menu_rules_group);
813 g_object_unref (toolbar_rules_group);
814 g_object_unref (clipboard_rules_group);
816 restore_settings (MODEST_MSG_VIEW_WINDOW(obj));
818 /* g_signal_connect (G_OBJECT(obj), "delete-event", G_CALLBACK(on_delete_event), obj); */
820 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);
821 g_signal_connect (G_OBJECT(priv->msg_view), "activate_link",
822 G_CALLBACK (modest_ui_actions_on_msg_link_clicked), obj);
823 g_signal_connect (G_OBJECT(priv->msg_view), "link_hover",
824 G_CALLBACK (modest_ui_actions_on_msg_link_hover), obj);
825 g_signal_connect (G_OBJECT(priv->msg_view), "attachment_clicked",
826 G_CALLBACK (modest_ui_actions_on_msg_attachment_clicked), obj);
827 g_signal_connect (G_OBJECT(priv->msg_view), "recpt_activated",
828 G_CALLBACK (modest_ui_actions_on_msg_recpt_activated), obj);
829 g_signal_connect (G_OBJECT(priv->msg_view), "link_contextual",
830 G_CALLBACK (modest_ui_actions_on_msg_link_contextual), obj);
831 g_signal_connect (G_OBJECT (priv->msg_view), "fetch_image",
832 G_CALLBACK (on_fetch_image), obj);
834 g_signal_connect (G_OBJECT (obj), "key-release-event",
835 G_CALLBACK (modest_msg_view_window_key_event),
838 g_signal_connect (G_OBJECT (obj), "key-press-event",
839 G_CALLBACK (modest_msg_view_window_key_event),
842 g_signal_connect (G_OBJECT (obj), "window-state-event",
843 G_CALLBACK (modest_msg_view_window_window_state_event),
846 g_signal_connect (G_OBJECT (obj), "move-focus",
847 G_CALLBACK (on_move_focus), obj);
849 /* Mail Operation Queue */
850 priv->queue_change_handler = g_signal_connect (G_OBJECT (modest_runtime_get_mail_operation_queue ()),
852 G_CALLBACK (on_queue_changed),
855 /* Account manager */
856 priv->account_removed_handler = g_signal_connect (G_OBJECT (modest_runtime_get_account_store ()),
858 G_CALLBACK(on_account_removed),
861 modest_window_set_active_account (MODEST_WINDOW(obj), modest_account_name);
863 g_signal_connect (G_OBJECT (priv->find_toolbar), "close", G_CALLBACK (modest_msg_view_window_find_toolbar_close), obj);
864 g_signal_connect (G_OBJECT (priv->find_toolbar), "search", G_CALLBACK (modest_msg_view_window_find_toolbar_search), obj);
865 priv->last_search = NULL;
867 /* Init the clipboard actions dim status */
868 modest_msg_view_grab_focus(MODEST_MSG_VIEW (priv->msg_view));
870 update_window_title (MODEST_MSG_VIEW_WINDOW (obj));
875 /* FIXME: parameter checks */
877 modest_msg_view_window_new_with_header_model (TnyMsg *msg,
878 const gchar *modest_account_name,
879 const gchar *msg_uid,
881 GtkTreeRowReference *row_reference)
883 ModestMsgViewWindow *window = NULL;
884 ModestMsgViewWindowPrivate *priv = NULL;
885 TnyFolder *header_folder = NULL;
886 ModestHeaderView *header_view = NULL;
887 ModestWindow *main_window = NULL;
888 ModestWindowMgr *mgr = NULL;
891 modest_tny_mime_part_to_string (TNY_MIME_PART (msg), 0);
894 mgr = modest_runtime_get_window_mgr ();
895 window = MODEST_MSG_VIEW_WINDOW (modest_window_mgr_get_msg_view_window (mgr));
896 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), NULL);
898 modest_msg_view_window_construct (window, modest_account_name, msg_uid);
900 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
902 /* Remember the message list's TreeModel so we can detect changes
903 * and change the list selection when necessary: */
905 main_window = modest_window_mgr_get_main_window(mgr, FALSE); /* don't create */
907 header_view = MODEST_HEADER_VIEW(modest_main_window_get_child_widget(
908 MODEST_MAIN_WINDOW(main_window),
909 MODEST_MAIN_WINDOW_WIDGET_TYPE_HEADER_VIEW));
912 if (header_view != NULL){
913 header_folder = modest_header_view_get_folder(header_view);
914 /* This could happen if the header folder was
915 unseleted before opening this msg window (for
916 example if the user selects an account in the
917 folder view of the main window */
919 priv->is_outbox = (modest_tny_folder_guess_folder_type (header_folder) == TNY_FOLDER_TYPE_OUTBOX);
920 priv->header_folder_id = tny_folder_get_id(header_folder);
921 g_assert(priv->header_folder_id != NULL);
922 g_object_unref(header_folder);
926 /* Setup row references and connect signals */
927 priv->header_model = g_object_ref (model);
930 priv->row_reference = gtk_tree_row_reference_copy (row_reference);
931 priv->next_row_reference = gtk_tree_row_reference_copy (row_reference);
932 select_next_valid_row (model, &(priv->next_row_reference), TRUE, priv->is_outbox);
934 priv->row_reference = NULL;
935 priv->next_row_reference = NULL;
938 /* Connect signals */
939 priv->row_changed_handler =
940 g_signal_connect (GTK_TREE_MODEL(model), "row-changed",
941 G_CALLBACK(modest_msg_view_window_on_row_changed),
943 priv->row_deleted_handler =
944 g_signal_connect (GTK_TREE_MODEL(model), "row-deleted",
945 G_CALLBACK(modest_msg_view_window_on_row_deleted),
947 priv->row_inserted_handler =
948 g_signal_connect (GTK_TREE_MODEL(model), "row-inserted",
949 G_CALLBACK(modest_msg_view_window_on_row_inserted),
951 priv->rows_reordered_handler =
952 g_signal_connect(GTK_TREE_MODEL(model), "rows-reordered",
953 G_CALLBACK(modest_msg_view_window_on_row_reordered),
956 if (header_view != NULL){
957 modest_header_view_add_observer(header_view,
958 MODEST_HEADER_VIEW_OBSERVER(window));
961 tny_msg_view_set_msg (TNY_MSG_VIEW (priv->msg_view), msg);
962 update_window_title (MODEST_MSG_VIEW_WINDOW (window));
963 gtk_widget_show_all (GTK_WIDGET (window));
964 modest_msg_view_window_update_priority (window);
966 /* Check dimming rules */
967 modest_ui_actions_check_toolbar_dimming_rules (MODEST_WINDOW (window));
968 modest_ui_actions_check_menu_dimming_rules (MODEST_WINDOW (window));
969 modest_window_check_dimming_rules_group (MODEST_WINDOW (window), MODEST_DIMMING_RULES_CLIPBOARD);
971 return MODEST_WINDOW(window);
975 modest_msg_view_window_new_for_search_result (TnyMsg *msg,
976 const gchar *modest_account_name,
977 const gchar *msg_uid)
979 ModestMsgViewWindow *window = NULL;
980 ModestMsgViewWindowPrivate *priv = NULL;
981 ModestWindowMgr *mgr = NULL;
983 mgr = modest_runtime_get_window_mgr ();
984 window = MODEST_MSG_VIEW_WINDOW (modest_window_mgr_get_msg_view_window (mgr));
985 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), NULL);
986 modest_msg_view_window_construct (window, modest_account_name, msg_uid);
988 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
990 /* Remember that this is a search result,
991 * so we can disable some UI appropriately: */
992 priv->is_search_result = TRUE;
994 tny_msg_view_set_msg (TNY_MSG_VIEW (priv->msg_view), msg);
996 update_window_title (window);
997 gtk_widget_show_all (GTK_WIDGET (window));
998 modest_msg_view_window_update_priority (window);
1000 /* Check dimming rules */
1001 modest_ui_actions_check_toolbar_dimming_rules (MODEST_WINDOW (window));
1002 modest_ui_actions_check_menu_dimming_rules (MODEST_WINDOW (window));
1003 modest_window_check_dimming_rules_group (MODEST_WINDOW (window), MODEST_DIMMING_RULES_CLIPBOARD);
1005 return MODEST_WINDOW(window);
1009 modest_msg_view_window_new_for_attachment (TnyMsg *msg,
1010 const gchar *modest_account_name,
1011 const gchar *msg_uid)
1013 GObject *obj = NULL;
1014 ModestMsgViewWindowPrivate *priv;
1015 ModestWindowMgr *mgr = NULL;
1017 g_return_val_if_fail (msg, NULL);
1018 mgr = modest_runtime_get_window_mgr ();
1019 obj = G_OBJECT (modest_window_mgr_get_msg_view_window (mgr));
1020 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (obj);
1021 modest_msg_view_window_construct (MODEST_MSG_VIEW_WINDOW (obj),
1022 modest_account_name, msg_uid);
1024 tny_msg_view_set_msg (TNY_MSG_VIEW (priv->msg_view), msg);
1025 update_window_title (MODEST_MSG_VIEW_WINDOW (obj));
1027 gtk_widget_show_all (GTK_WIDGET (obj));
1029 /* Check dimming rules */
1030 modest_ui_actions_check_toolbar_dimming_rules (MODEST_WINDOW (obj));
1031 modest_ui_actions_check_menu_dimming_rules (MODEST_WINDOW (obj));
1032 modest_window_check_dimming_rules_group (MODEST_WINDOW (obj), MODEST_DIMMING_RULES_CLIPBOARD);
1034 return MODEST_WINDOW(obj);
1038 modest_msg_view_window_on_row_changed (GtkTreeModel *header_model,
1041 ModestMsgViewWindow *window)
1043 check_dimming_rules_after_change (window);
1047 modest_msg_view_window_on_row_deleted(GtkTreeModel *header_model,
1049 ModestMsgViewWindow *window)
1051 check_dimming_rules_after_change (window);
1055 check_dimming_rules_after_change_in_idle (gpointer data)
1057 /* The window could have dissapeared */
1058 if (MODEST_IS_WINDOW (data)) {
1059 ModestWindow *win = MODEST_WINDOW (data);
1060 gdk_threads_enter ();
1061 modest_ui_actions_check_menu_dimming_rules (win);
1062 modest_ui_actions_check_toolbar_dimming_rules (win);
1063 gdk_threads_leave ();
1070 check_dimming_rules_after_change (ModestMsgViewWindow *window)
1072 static guint dimming_delayer = 0;
1074 if (dimming_delayer > 0)
1075 g_source_remove (dimming_delayer);
1077 /* We're expecting a lot of changes at the same time so don't
1078 need to check dimming rules for every change that
1080 dimming_delayer = g_timeout_add (100, check_dimming_rules_after_change_in_idle, window);
1084 /* On insertions we check if the folder still has the message we are
1085 * showing or do not. If do not, we do nothing. Which means we are still
1086 * not attached to any header folder and thus next/prev buttons are
1087 * still dimmed. Once the message that is shown by msg-view is found, the
1088 * new model of header-view will be attached and the references will be set.
1089 * On each further insertions dimming rules will be checked. However
1090 * this requires extra CPU time at least works.
1091 * (An message might be deleted from TnyFolder and thus will not be
1092 * inserted into the model again for example if it is removed by the
1093 * imap server and the header view is refreshed.)
1096 modest_msg_view_window_on_row_inserted (GtkTreeModel *model,
1097 GtkTreePath *tree_path,
1098 GtkTreeIter *tree_iter,
1099 ModestMsgViewWindow *window)
1101 ModestMsgViewWindowPrivate *priv = NULL;
1102 TnyHeader *header = NULL;
1104 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window));
1105 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1107 g_assert (model == priv->header_model);
1109 /* Check if the newly inserted message is the same we are actually
1110 * showing. IF not, we should remain detached from the header model
1111 * and thus prev and next toolbar buttons should remain dimmed. */
1112 gtk_tree_model_get (model, tree_iter,
1113 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
1116 if (TNY_IS_HEADER (header)) {
1119 uid = modest_tny_folder_get_header_unique_id (header);
1120 if (!g_str_equal(priv->msg_uid, uid)) {
1121 check_dimming_rules_after_change (window);
1123 g_object_unref (G_OBJECT(header));
1127 g_object_unref(G_OBJECT(header));
1130 if (priv->row_reference) {
1131 gtk_tree_row_reference_free (priv->row_reference);
1134 /* Setup row_reference for the actual msg. */
1135 priv->row_reference = gtk_tree_row_reference_new (priv->header_model, tree_path);
1136 if (priv->row_reference == NULL) {
1137 g_warning("No reference for msg header item.");
1141 /* Now set up next_row_reference. */
1142 if (priv->next_row_reference) {
1143 gtk_tree_row_reference_free (priv->next_row_reference);
1146 priv->next_row_reference = gtk_tree_row_reference_copy (priv->row_reference);
1147 select_next_valid_row (priv->header_model,
1148 &(priv->next_row_reference), FALSE, priv->is_outbox);
1150 /* Connect the remaining callbacks to become able to detect
1151 * changes in header-view. */
1152 priv->row_changed_handler =
1153 g_signal_connect (priv->header_model, "row-changed",
1154 G_CALLBACK (modest_msg_view_window_on_row_changed),
1156 priv->row_deleted_handler =
1157 g_signal_connect (priv->header_model, "row-deleted",
1158 G_CALLBACK (modest_msg_view_window_on_row_deleted),
1160 priv->rows_reordered_handler =
1161 g_signal_connect (priv->header_model, "rows-reordered",
1162 G_CALLBACK (modest_msg_view_window_on_row_reordered),
1165 check_dimming_rules_after_change (window);
1169 modest_msg_view_window_on_row_reordered (GtkTreeModel *header_model,
1173 ModestMsgViewWindow *window)
1175 ModestMsgViewWindowPrivate *priv = NULL;
1176 gboolean already_changed = FALSE;
1178 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(window);
1180 /* If the current row was reordered select the proper next
1181 valid row. The same if the next row reference changes */
1182 if (priv->row_reference &&
1183 gtk_tree_row_reference_valid (priv->row_reference)) {
1185 path = gtk_tree_row_reference_get_path (priv->row_reference);
1186 if (gtk_tree_path_compare (path, arg1) == 0) {
1187 if (priv->next_row_reference) {
1188 gtk_tree_row_reference_free (priv->next_row_reference);
1190 priv->next_row_reference = gtk_tree_row_reference_copy (priv->row_reference);
1191 select_next_valid_row (header_model, &(priv->next_row_reference), FALSE, priv->is_outbox);
1192 already_changed = TRUE;
1194 gtk_tree_path_free (path);
1196 if (!already_changed &&
1197 priv->next_row_reference &&
1198 gtk_tree_row_reference_valid (priv->next_row_reference)) {
1200 path = gtk_tree_row_reference_get_path (priv->next_row_reference);
1201 if (gtk_tree_path_compare (path, arg1) == 0) {
1202 if (priv->next_row_reference) {
1203 gtk_tree_row_reference_free (priv->next_row_reference);
1205 priv->next_row_reference = gtk_tree_row_reference_copy (priv->row_reference);
1206 select_next_valid_row (header_model, &(priv->next_row_reference), FALSE, priv->is_outbox);
1208 gtk_tree_path_free (path);
1210 check_dimming_rules_after_change (window);
1213 /* The modest_msg_view_window_update_model_replaced implements update
1214 * function for ModestHeaderViewObserver. Checks whether the TnyFolder
1215 * actually belongs to the header-view is the same as the TnyFolder of
1216 * the message of msg-view or not. If they are different, there is
1217 * nothing to do. If they are the same, then the model has replaced and
1218 * the reference in msg-view shall be replaced from the old model to
1219 * the new model. In this case the view will be detached from it's
1220 * header folder. From this point the next/prev buttons are dimmed.
1223 modest_msg_view_window_update_model_replaced (ModestHeaderViewObserver *observer,
1224 GtkTreeModel *model,
1225 const gchar *tny_folder_id)
1227 ModestMsgViewWindowPrivate *priv = NULL;
1228 ModestMsgViewWindow *window = NULL;
1230 g_assert(MODEST_IS_HEADER_VIEW_OBSERVER(observer));
1231 g_assert(MODEST_IS_MSG_VIEW_WINDOW(observer));
1233 window = MODEST_MSG_VIEW_WINDOW(observer);
1234 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(window);
1236 /* If there is an other folder in the header-view then we do
1237 * not care about it's model (msg list). Else if the
1238 * header-view shows the folder the msg shown by us is in, we
1239 * shall replace our model reference and make some check. */
1240 if(model == NULL || tny_folder_id == NULL ||
1241 (priv->header_folder_id && !g_str_equal(tny_folder_id, priv->header_folder_id)))
1244 /* Model is changed(replaced), so we should forget the old
1245 * one. Because there might be other references and there
1246 * might be some change on the model even if we unreferenced
1247 * it, we need to disconnect our signals here. */
1248 if (priv->header_model) {
1249 if (g_signal_handler_is_connected(G_OBJECT (priv->header_model),
1250 priv->row_changed_handler))
1251 g_signal_handler_disconnect(G_OBJECT (priv->header_model),
1252 priv->row_changed_handler);
1253 if (g_signal_handler_is_connected(G_OBJECT (priv->header_model),
1254 priv->row_deleted_handler))
1255 g_signal_handler_disconnect(G_OBJECT (priv->header_model),
1256 priv->row_deleted_handler);
1257 if (g_signal_handler_is_connected(G_OBJECT (priv->header_model),
1258 priv->row_inserted_handler))
1259 g_signal_handler_disconnect(G_OBJECT (priv->header_model),
1260 priv->row_inserted_handler);
1261 if (g_signal_handler_is_connected(G_OBJECT (priv->header_model),
1262 priv->rows_reordered_handler))
1263 g_signal_handler_disconnect(G_OBJECT (priv->header_model),
1264 priv->rows_reordered_handler);
1267 if (priv->row_reference)
1268 gtk_tree_row_reference_free (priv->row_reference);
1269 if (priv->next_row_reference)
1270 gtk_tree_row_reference_free (priv->next_row_reference);
1271 g_object_unref(priv->header_model);
1274 priv->row_changed_handler = 0;
1275 priv->row_deleted_handler = 0;
1276 priv->row_inserted_handler = 0;
1277 priv->rows_reordered_handler = 0;
1278 priv->next_row_reference = NULL;
1279 priv->row_reference = NULL;
1280 priv->header_model = NULL;
1283 priv->header_model = g_object_ref (model);
1285 /* Also we must connect to the new model for row insertions.
1286 * Only for insertions now. We will need other ones only after
1287 * the msg is show by msg-view is added to the new model. */
1288 priv->row_inserted_handler =
1289 g_signal_connect (priv->header_model, "row-inserted",
1290 G_CALLBACK(modest_msg_view_window_on_row_inserted),
1293 modest_ui_actions_check_menu_dimming_rules(MODEST_WINDOW(window));
1294 modest_ui_actions_check_toolbar_dimming_rules(MODEST_WINDOW(window));
1298 modest_msg_view_window_toolbar_on_transfer_mode (ModestMsgViewWindow *self)
1300 ModestMsgViewWindowPrivate *priv= NULL;
1302 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self), FALSE);
1303 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
1305 return priv->current_toolbar_mode == TOOLBAR_MODE_TRANSFER;
1309 modest_msg_view_window_get_header (ModestMsgViewWindow *self)
1311 ModestMsgViewWindowPrivate *priv= NULL;
1313 TnyHeader *header = NULL;
1314 GtkTreePath *path = NULL;
1317 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self), NULL);
1318 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
1320 /* If the message was not obtained from a treemodel,
1321 * for instance if it was opened directly by the search UI:
1323 if (priv->header_model == NULL ||
1324 priv->row_reference == NULL ||
1325 !gtk_tree_row_reference_valid (priv->row_reference)) {
1326 msg = modest_msg_view_window_get_message (self);
1328 header = tny_msg_get_header (msg);
1329 g_object_unref (msg);
1334 /* Get iter of the currently selected message in the header view: */
1335 path = gtk_tree_row_reference_get_path (priv->row_reference);
1336 g_return_val_if_fail (path != NULL, NULL);
1337 gtk_tree_model_get_iter (priv->header_model,
1341 /* Get current message header */
1342 gtk_tree_model_get (priv->header_model, &iter,
1343 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
1346 gtk_tree_path_free (path);
1351 modest_msg_view_window_get_message (ModestMsgViewWindow *self)
1353 ModestMsgViewWindowPrivate *priv;
1355 g_return_val_if_fail (self, NULL);
1357 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(self);
1359 return tny_msg_view_get_msg (TNY_MSG_VIEW (priv->msg_view));
1363 modest_msg_view_window_get_message_uid (ModestMsgViewWindow *self)
1365 ModestMsgViewWindowPrivate *priv;
1367 g_return_val_if_fail (self, NULL);
1369 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
1371 return (const gchar*) priv->msg_uid;
1375 modest_msg_view_window_toggle_find_toolbar (GtkToggleAction *toggle,
1378 ModestMsgViewWindow *window = MODEST_MSG_VIEW_WINDOW (data);
1379 ModestMsgViewWindowPrivate *priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1380 ModestWindowPrivate *parent_priv = MODEST_WINDOW_GET_PRIVATE (window);
1384 is_active = gtk_toggle_action_get_active (toggle);
1387 gtk_widget_show (priv->find_toolbar);
1388 hildon_find_toolbar_highlight_entry (HILDON_FIND_TOOLBAR (priv->find_toolbar), TRUE);
1390 gtk_widget_hide (priv->find_toolbar);
1393 /* update the toggle buttons status */
1394 action = gtk_ui_manager_get_action (parent_priv->ui_manager, "/ToolBar/FindInMessage");
1395 modest_utils_toggle_action_set_active_block_notify (GTK_TOGGLE_ACTION (action), is_active);
1396 action = gtk_ui_manager_get_action (parent_priv->ui_manager, "/MenuBar/ToolsMenu/ToolsFindInMessageMenu");
1397 modest_utils_toggle_action_set_active_block_notify (GTK_TOGGLE_ACTION (action), is_active);
1402 modest_msg_view_window_find_toolbar_close (GtkWidget *widget,
1403 ModestMsgViewWindow *obj)
1405 GtkToggleAction *toggle;
1406 ModestWindowPrivate *parent_priv;
1407 ModestMsgViewWindowPrivate *priv;
1409 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (obj);
1410 parent_priv = MODEST_WINDOW_GET_PRIVATE (obj);
1412 toggle = GTK_TOGGLE_ACTION (gtk_ui_manager_get_action (parent_priv->ui_manager, "/ToolBar/FindInMessage"));
1413 gtk_toggle_action_set_active (toggle, FALSE);
1414 modest_msg_view_grab_focus (MODEST_MSG_VIEW (priv->msg_view));
1418 modest_msg_view_window_find_toolbar_search (GtkWidget *widget,
1419 ModestMsgViewWindow *obj)
1421 gchar *current_search;
1422 ModestMsgViewWindowPrivate *priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (obj);
1424 if (modest_mime_part_view_is_empty (MODEST_MIME_PART_VIEW (priv->msg_view))) {
1425 hildon_banner_show_information (NULL, NULL, _("mail_ib_nothing_to_find"));
1429 g_object_get (G_OBJECT (widget), "prefix", ¤t_search, NULL);
1431 if ((current_search == NULL) || (strcmp (current_search, "") == 0)) {
1432 g_free (current_search);
1433 hildon_banner_show_information (NULL, NULL, dgettext("hildon-common-strings", "ecdg_ib_find_rep_enter_text"));
1437 if ((priv->last_search == NULL) || (strcmp (priv->last_search, current_search) != 0)) {
1439 g_free (priv->last_search);
1440 priv->last_search = g_strdup (current_search);
1441 result = modest_isearch_view_search (MODEST_ISEARCH_VIEW (priv->msg_view),
1444 hildon_banner_show_information (NULL, NULL, dgettext("hildon-libs", "ckct_ib_find_no_matches"));
1445 g_free (priv->last_search);
1446 priv->last_search = NULL;
1448 modest_msg_view_grab_focus (MODEST_MSG_VIEW (priv->msg_view));
1449 hildon_find_toolbar_highlight_entry (HILDON_FIND_TOOLBAR (priv->find_toolbar), TRUE);
1452 if (!modest_isearch_view_search_next (MODEST_ISEARCH_VIEW (priv->msg_view))) {
1453 hildon_banner_show_information (NULL, NULL, dgettext("hildon-libs", "ckct_ib_find_search_complete"));
1454 g_free (priv->last_search);
1455 priv->last_search = NULL;
1457 modest_msg_view_grab_focus (MODEST_MSG_VIEW (priv->msg_view));
1458 hildon_find_toolbar_highlight_entry (HILDON_FIND_TOOLBAR (priv->find_toolbar), TRUE);
1462 g_free (current_search);
1467 modest_msg_view_window_set_zoom (ModestWindow *window,
1470 ModestMsgViewWindowPrivate *priv;
1471 ModestWindowPrivate *parent_priv;
1472 GtkAction *action = NULL;
1473 gint int_zoom = (gint) rint (zoom*100.0+0.1);
1475 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window));
1477 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1478 parent_priv = MODEST_WINDOW_GET_PRIVATE (window);
1479 modest_zoomable_set_zoom (MODEST_ZOOMABLE (priv->msg_view), zoom);
1481 action = gtk_ui_manager_get_action (parent_priv->ui_manager,
1482 "/MenuBar/ViewMenu/ZoomMenu/Zoom50Menu");
1484 gtk_radio_action_set_current_value (GTK_RADIO_ACTION (action), int_zoom);
1488 modest_msg_view_window_get_zoom (ModestWindow *window)
1490 ModestMsgViewWindowPrivate *priv;
1492 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), 1.0);
1494 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1495 return modest_zoomable_get_zoom (MODEST_ZOOMABLE (priv->msg_view));
1499 modest_msg_view_window_zoom_plus (ModestWindow *window)
1501 ModestWindowPrivate *parent_priv;
1502 GtkRadioAction *zoom_radio_action;
1503 GSList *group, *node;
1505 parent_priv = MODEST_WINDOW_GET_PRIVATE (window);
1506 zoom_radio_action = GTK_RADIO_ACTION (gtk_ui_manager_get_action (parent_priv->ui_manager,
1507 "/MenuBar/ViewMenu/ZoomMenu/Zoom50Menu"));
1509 group = gtk_radio_action_get_group (zoom_radio_action);
1511 if (gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (group->data))) {
1512 hildon_banner_show_information (NULL, NULL, dgettext("hildon-common-strings", "ckct_ib_max_zoom_level_reached"));
1516 for (node = group; node != NULL; node = g_slist_next (node)) {
1517 if ((node->next != NULL) && gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (node->next->data))) {
1518 gtk_toggle_action_set_active (GTK_TOGGLE_ACTION (node->data), TRUE);
1526 modest_msg_view_window_zoom_minus (ModestWindow *window)
1528 ModestWindowPrivate *parent_priv;
1529 GtkRadioAction *zoom_radio_action;
1530 GSList *group, *node;
1532 parent_priv = MODEST_WINDOW_GET_PRIVATE (window);
1533 zoom_radio_action = GTK_RADIO_ACTION (gtk_ui_manager_get_action (parent_priv->ui_manager,
1534 "/MenuBar/ViewMenu/ZoomMenu/Zoom50Menu"));
1536 group = gtk_radio_action_get_group (zoom_radio_action);
1538 for (node = group; node != NULL; node = g_slist_next (node)) {
1539 if (gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (node->data))) {
1540 if (node->next != NULL) {
1541 gtk_toggle_action_set_active (GTK_TOGGLE_ACTION (node->next->data), TRUE);
1544 hildon_banner_show_information (NULL, NULL, dgettext("hildon-common-strings", "ckct_ib_min_zoom_level_reached"));
1554 modest_msg_view_window_key_event (GtkWidget *window,
1560 focus = gtk_window_get_focus (GTK_WINDOW (window));
1562 /* for the find toolbar case */
1563 if (focus && GTK_IS_ENTRY (focus)) {
1564 if (event->keyval == GDK_BackSpace) {
1566 copy = gdk_event_copy ((GdkEvent *) event);
1567 gtk_widget_event (focus, copy);
1568 gdk_event_free (copy);
1573 if (event->keyval == GDK_Up || event->keyval == GDK_KP_Up ||
1574 event->keyval == GDK_Down || event->keyval == GDK_KP_Down ||
1575 event->keyval == GDK_Page_Up || event->keyval == GDK_KP_Page_Up ||
1576 event->keyval == GDK_Page_Down || event->keyval == GDK_KP_Page_Down ||
1577 event->keyval == GDK_Home || event->keyval == GDK_KP_Home ||
1578 event->keyval == GDK_End || event->keyval == GDK_KP_End) {
1579 ModestMsgViewWindowPrivate *priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1580 gboolean return_value;
1582 if (event->type == GDK_KEY_RELEASE) {
1583 GtkScrollType scroll_type;
1585 switch (event->keyval) {
1588 scroll_type = GTK_SCROLL_STEP_UP; break;
1591 scroll_type = GTK_SCROLL_STEP_DOWN; break;
1593 case GDK_KP_Page_Up:
1594 scroll_type = GTK_SCROLL_PAGE_UP; break;
1596 case GDK_KP_Page_Down:
1597 scroll_type = GTK_SCROLL_PAGE_DOWN; break;
1600 scroll_type = GTK_SCROLL_START; break;
1603 scroll_type = GTK_SCROLL_END; break;
1604 default: scroll_type = GTK_SCROLL_NONE;
1607 g_signal_emit_by_name (G_OBJECT (priv->main_scroll), "scroll-child",
1608 scroll_type, FALSE, &return_value);
1619 modest_msg_view_window_last_message_selected (ModestMsgViewWindow *window)
1622 ModestMsgViewWindowPrivate *priv;
1623 GtkTreeIter tmp_iter;
1624 gboolean is_last_selected;
1626 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), TRUE);
1627 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1629 /*if no model (so no rows at all), then virtually we are the last*/
1630 if (!priv->header_model || !priv->row_reference)
1633 path = gtk_tree_row_reference_get_path (priv->row_reference);
1637 is_last_selected = TRUE;
1638 while (is_last_selected) {
1640 gtk_tree_path_next (path);
1641 if (!gtk_tree_model_get_iter (priv->header_model, &tmp_iter, path))
1643 gtk_tree_model_get (priv->header_model, &tmp_iter,
1644 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
1647 if (msg_is_visible (header, priv->is_outbox))
1648 is_last_selected = FALSE;
1649 g_object_unref(G_OBJECT(header));
1652 gtk_tree_path_free (path);
1653 return is_last_selected;
1657 modest_msg_view_window_has_headers_model (ModestMsgViewWindow *window)
1659 ModestMsgViewWindowPrivate *priv;
1661 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), TRUE);
1662 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1664 return priv->header_model != NULL;
1668 modest_msg_view_window_is_search_result (ModestMsgViewWindow *window)
1670 ModestMsgViewWindowPrivate *priv;
1672 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), TRUE);
1673 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1675 return priv->is_search_result;
1679 msg_is_visible (TnyHeader *header, gboolean check_outbox)
1681 if ((tny_header_get_flags(header) & TNY_HEADER_FLAG_DELETED))
1683 if (!check_outbox) {
1686 ModestTnySendQueueStatus status;
1687 status = modest_tny_all_send_queues_get_msg_status (header);
1688 return ((status != MODEST_TNY_SEND_QUEUE_FAILED) &&
1689 (status != MODEST_TNY_SEND_QUEUE_SENDING));
1694 modest_msg_view_window_first_message_selected (ModestMsgViewWindow *window)
1697 ModestMsgViewWindowPrivate *priv;
1698 gboolean is_first_selected;
1699 GtkTreeIter tmp_iter;
1700 /* gchar * path_string;*/
1702 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), TRUE);
1703 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1705 /*if no model (so no rows at all), then virtually we are the first*/
1706 if (!priv->header_model || !priv->row_reference)
1709 path = gtk_tree_row_reference_get_path (priv->row_reference);
1713 /* path_string = gtk_tree_path_to_string (path);
1714 is_first_selected = strcmp (path_string, "0");
1716 g_free (path_string);
1717 gtk_tree_path_free (path);
1719 return is_first_selected;*/
1721 is_first_selected = TRUE;
1722 while (is_first_selected) {
1724 if(!gtk_tree_path_prev (path))
1726 /* Here the 'if' is needless for logic, but let make sure
1727 * iter is valid for gtk_tree_model_get. */
1728 if (!gtk_tree_model_get_iter (priv->header_model, &tmp_iter, path))
1730 gtk_tree_model_get (priv->header_model, &tmp_iter,
1731 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
1734 if (msg_is_visible (header, priv->is_outbox))
1735 is_first_selected = FALSE;
1736 g_object_unref(G_OBJECT(header));
1739 gtk_tree_path_free (path);
1740 return is_first_selected;
1745 GtkTreeRowReference *row_reference;
1749 message_reader_performer (gboolean canceled,
1751 GtkWindow *parent_window,
1752 TnyAccount *account,
1755 ModestMailOperation *mail_op = NULL;
1756 MsgReaderInfo *info;
1758 info = (MsgReaderInfo *) user_data;
1759 if (canceled || err) {
1763 /* Register the header - it'll be unregistered in the callback */
1764 modest_window_mgr_register_header (modest_runtime_get_window_mgr (), info->header, NULL);
1766 /* New mail operation */
1767 mail_op = modest_mail_operation_new_with_error_handling (G_OBJECT(parent_window),
1768 modest_ui_actions_disk_operations_error_handler,
1771 modest_mail_operation_queue_add (modest_runtime_get_mail_operation_queue (), mail_op);
1772 modest_mail_operation_get_msg (mail_op, info->header, TRUE, view_msg_cb, info->row_reference);
1773 g_object_unref (mail_op);
1775 /* Update dimming rules */
1776 modest_ui_actions_check_toolbar_dimming_rules (MODEST_WINDOW (parent_window));
1777 modest_ui_actions_check_menu_dimming_rules (MODEST_WINDOW (parent_window));
1780 /* Frees. The row_reference will be freed by the view_msg_cb callback */
1781 g_object_unref (info->header);
1782 g_slice_free (MsgReaderInfo, info);
1787 * Reads the message whose summary item is @header. It takes care of
1788 * several things, among others:
1790 * If the message was not previously downloaded then ask the user
1791 * before downloading. If there is no connection launch the connection
1792 * dialog. Update toolbar dimming rules.
1794 * Returns: TRUE if the mail operation was started, otherwise if the
1795 * user do not want to download the message, or if the user do not
1796 * want to connect, then the operation is not issued
1799 message_reader (ModestMsgViewWindow *window,
1800 ModestMsgViewWindowPrivate *priv,
1802 GtkTreeRowReference *row_reference)
1804 gboolean already_showing = FALSE;
1805 ModestWindow *msg_window = NULL;
1806 ModestWindowMgr *mgr;
1807 TnyAccount *account;
1809 MsgReaderInfo *info;
1811 g_return_val_if_fail (row_reference != NULL, FALSE);
1813 mgr = modest_runtime_get_window_mgr ();
1814 already_showing = modest_window_mgr_find_registered_header (mgr, header, &msg_window);
1815 if (already_showing && (msg_window != MODEST_WINDOW (window))) {
1818 gtk_window_present (GTK_WINDOW (msg_window));
1819 g_signal_emit_by_name (G_OBJECT (window), "delete-event", NULL, &retval);
1823 /* Msg download completed */
1824 if (!(tny_header_get_flags (header) & TNY_HEADER_FLAG_CACHED)) {
1825 /* Ask the user if he wants to download the message if
1827 if (!tny_device_is_online (modest_runtime_get_device())) {
1828 GtkResponseType response;
1830 response = modest_platform_run_confirmation_dialog (GTK_WINDOW (window),
1831 _("mcen_nc_get_msg"));
1832 if (response == GTK_RESPONSE_CANCEL)
1835 folder = tny_header_get_folder (header);
1836 info = g_slice_new (MsgReaderInfo);
1837 info->header = g_object_ref (header);
1838 info->row_reference = gtk_tree_row_reference_copy (row_reference);
1840 /* Offer the connection dialog if necessary */
1841 modest_platform_connect_if_remote_and_perform ((GtkWindow *) window,
1843 TNY_FOLDER_STORE (folder),
1844 message_reader_performer,
1846 g_object_unref (folder);
1851 folder = tny_header_get_folder (header);
1852 account = tny_folder_get_account (folder);
1853 info = g_slice_new (MsgReaderInfo);
1854 info->header = g_object_ref (header);
1855 info->row_reference = gtk_tree_row_reference_copy (row_reference);
1857 message_reader_performer (FALSE, NULL, (GtkWindow *) window, account, info);
1858 g_object_unref (account);
1859 g_object_unref (folder);
1865 modest_msg_view_window_select_next_message (ModestMsgViewWindow *window)
1867 ModestMsgViewWindowPrivate *priv;
1868 GtkTreePath *path= NULL;
1869 GtkTreeIter tmp_iter;
1871 gboolean retval = TRUE;
1872 GtkTreeRowReference *row_reference = NULL;
1874 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), FALSE);
1875 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1877 if (!priv->row_reference)
1880 /* Update the next row reference if it's not valid. This could
1881 happen if for example the header which it was pointing to,
1882 was deleted. The best place to do it is in the row-deleted
1883 handler but the tinymail model do not work like the glib
1884 tree models and reports the deletion when the row is still
1886 if (!gtk_tree_row_reference_valid (priv->next_row_reference)) {
1887 if (gtk_tree_row_reference_valid (priv->row_reference)) {
1888 priv->next_row_reference = gtk_tree_row_reference_copy (priv->row_reference);
1889 select_next_valid_row (priv->header_model, &(priv->next_row_reference), FALSE, priv->is_outbox);
1892 if (priv->next_row_reference)
1893 path = gtk_tree_row_reference_get_path (priv->next_row_reference);
1897 row_reference = gtk_tree_row_reference_copy (priv->next_row_reference);
1899 gtk_tree_model_get_iter (priv->header_model,
1902 gtk_tree_path_free (path);
1904 gtk_tree_model_get (priv->header_model, &tmp_iter,
1905 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
1908 /* Read the message & show it */
1909 if (!message_reader (window, priv, header, row_reference)) {
1912 gtk_tree_row_reference_free (row_reference);
1915 g_object_unref (header);
1921 modest_msg_view_window_select_previous_message (ModestMsgViewWindow *window)
1923 ModestMsgViewWindowPrivate *priv = NULL;
1925 gboolean finished = FALSE;
1926 gboolean retval = FALSE;
1928 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), FALSE);
1929 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1931 /* Return inmediatly if there is no header model */
1932 if (!priv->header_model || !priv->row_reference)
1935 path = gtk_tree_row_reference_get_path (priv->row_reference);
1936 while (!finished && gtk_tree_path_prev (path)) {
1940 gtk_tree_model_get_iter (priv->header_model, &iter, path);
1941 gtk_tree_model_get (priv->header_model, &iter,
1942 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
1946 if (msg_is_visible (header, priv->is_outbox)) {
1947 GtkTreeRowReference *row_reference;
1948 row_reference = gtk_tree_row_reference_new (priv->header_model, path);
1949 /* Read the message & show it */
1950 retval = message_reader (window, priv, header, row_reference);
1951 gtk_tree_row_reference_free (row_reference);
1955 g_object_unref (header);
1959 gtk_tree_path_free (path);
1964 view_msg_cb (ModestMailOperation *mail_op,
1971 ModestMsgViewWindow *self = NULL;
1972 ModestMsgViewWindowPrivate *priv = NULL;
1973 GtkTreeRowReference *row_reference = NULL;
1975 /* Unregister the header (it was registered before creating the mail operation) */
1976 modest_window_mgr_unregister_header (modest_runtime_get_window_mgr (), header);
1978 row_reference = (GtkTreeRowReference *) user_data;
1980 gtk_tree_row_reference_free (row_reference);
1984 /* If there was any error */
1985 if (!modest_ui_actions_msg_retrieval_check (mail_op, header, msg)) {
1986 gtk_tree_row_reference_free (row_reference);
1990 /* Get the window */
1991 self = (ModestMsgViewWindow *) modest_mail_operation_get_source (mail_op);
1992 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self));
1993 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
1995 /* Update the row reference */
1996 if (priv->row_reference != NULL) {
1997 gtk_tree_row_reference_free (priv->row_reference);
1998 priv->row_reference = gtk_tree_row_reference_copy (row_reference);
1999 if (priv->next_row_reference != NULL) {
2000 gtk_tree_row_reference_free (priv->next_row_reference);
2002 priv->next_row_reference = gtk_tree_row_reference_copy (priv->row_reference);
2003 select_next_valid_row (priv->header_model, &(priv->next_row_reference), TRUE, priv->is_outbox);
2006 /* Mark header as read */
2007 if (!(tny_header_get_flags (header) & TNY_HEADER_FLAG_SEEN))
2008 tny_header_set_flag (header, TNY_HEADER_FLAG_SEEN);
2010 /* Set new message */
2011 if (priv->msg_view != NULL && TNY_IS_MSG_VIEW (priv->msg_view)) {
2012 tny_msg_view_set_msg (TNY_MSG_VIEW (priv->msg_view), msg);
2013 modest_msg_view_window_update_priority (self);
2014 update_window_title (MODEST_MSG_VIEW_WINDOW (self));
2015 modest_msg_view_grab_focus (MODEST_MSG_VIEW (priv->msg_view));
2018 /* Set the new message uid of the window */
2019 if (priv->msg_uid) {
2020 g_free (priv->msg_uid);
2021 priv->msg_uid = modest_tny_folder_get_header_unique_id (header);
2024 /* Notify the observers */
2025 g_signal_emit (G_OBJECT (self), signals[MSG_CHANGED_SIGNAL],
2026 0, priv->header_model, priv->row_reference);
2029 g_object_unref (self);
2030 gtk_tree_row_reference_free (row_reference);
2034 modest_msg_view_window_get_folder_type (ModestMsgViewWindow *window)
2036 ModestMsgViewWindowPrivate *priv;
2038 TnyFolderType folder_type;
2040 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
2042 folder_type = TNY_FOLDER_TYPE_UNKNOWN;
2044 msg = tny_msg_view_get_msg (TNY_MSG_VIEW (priv->msg_view));
2048 folder = tny_msg_get_folder (msg);
2050 folder_type = modest_tny_folder_guess_folder_type (folder);
2051 g_object_unref (folder);
2053 g_object_unref (msg);
2061 modest_msg_view_window_update_priority (ModestMsgViewWindow *window)
2063 ModestMsgViewWindowPrivate *priv;
2064 TnyHeader *header = NULL;
2065 TnyHeaderFlags flags = 0;
2067 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
2069 if (priv->header_model && priv->row_reference) {
2071 GtkTreePath *path = NULL;
2073 path = gtk_tree_row_reference_get_path (priv->row_reference);
2074 g_return_if_fail (path != NULL);
2075 gtk_tree_model_get_iter (priv->header_model,
2077 gtk_tree_row_reference_get_path (priv->row_reference));
2079 gtk_tree_model_get (priv->header_model, &iter, TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
2081 gtk_tree_path_free (path);
2084 msg = tny_msg_view_get_msg (TNY_MSG_VIEW (priv->msg_view));
2086 header = tny_msg_get_header (msg);
2087 g_object_unref (msg);
2092 flags = tny_header_get_flags (header);
2093 g_object_unref(G_OBJECT(header));
2096 modest_msg_view_set_priority (MODEST_MSG_VIEW(priv->msg_view), flags);
2101 toolbar_resize (ModestMsgViewWindow *self)
2103 ModestMsgViewWindowPrivate *priv = NULL;
2104 ModestWindowPrivate *parent_priv = NULL;
2106 gint static_button_size;
2107 ModestWindowMgr *mgr;
2109 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self));
2110 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
2111 parent_priv = MODEST_WINDOW_GET_PRIVATE(self);
2113 mgr = modest_runtime_get_window_mgr ();
2114 static_button_size = modest_window_mgr_get_fullscreen_mode (mgr)?118:108;
2116 if (parent_priv->toolbar) {
2117 /* left size buttons */
2118 widget = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/ToolbarMessageReply");
2119 gtk_tool_item_set_expand (GTK_TOOL_ITEM (widget), FALSE);
2120 gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (widget), FALSE);
2121 gtk_widget_set_size_request (GTK_WIDGET (widget), static_button_size, -1);
2122 widget = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/ToolbarMessageMoveTo");
2123 gtk_tool_item_set_expand (GTK_TOOL_ITEM (widget), FALSE);
2124 gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (widget), FALSE);
2125 gtk_widget_set_size_request (GTK_WIDGET (widget), static_button_size, -1);
2126 widget = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/ToolbarDeleteMessage");
2127 gtk_tool_item_set_expand (GTK_TOOL_ITEM (widget), FALSE);
2128 gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (widget), FALSE);
2129 gtk_widget_set_size_request (GTK_WIDGET (widget), static_button_size, -1);
2130 widget = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/FindInMessage");
2131 gtk_tool_item_set_expand (GTK_TOOL_ITEM (widget), FALSE);
2132 gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (widget), FALSE);
2133 gtk_widget_set_size_request (GTK_WIDGET (widget), static_button_size, -1);
2135 gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (priv->progress_toolitem), FALSE);
2136 gtk_tool_item_set_expand (GTK_TOOL_ITEM (priv->progress_toolitem), TRUE);
2137 gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (priv->cancel_toolitem), FALSE);
2138 gtk_tool_item_set_expand (GTK_TOOL_ITEM (priv->cancel_toolitem), FALSE);
2139 gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (priv->next_toolitem), TRUE);
2140 gtk_tool_item_set_expand (GTK_TOOL_ITEM (priv->next_toolitem), TRUE);
2141 gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (priv->prev_toolitem), TRUE);
2142 gtk_tool_item_set_expand (GTK_TOOL_ITEM (priv->prev_toolitem), TRUE);
2148 modest_msg_view_window_window_state_event (GtkWidget *widget, GdkEventWindowState *event, gpointer userdata)
2150 if (event->changed_mask & GDK_WINDOW_STATE_FULLSCREEN) {
2151 ModestWindowPrivate *parent_priv;
2152 ModestWindowMgr *mgr;
2153 gboolean is_fullscreen;
2154 GtkAction *fs_toggle_action;
2157 mgr = modest_runtime_get_window_mgr ();
2158 is_fullscreen = (modest_window_mgr_get_fullscreen_mode (mgr))?1:0;
2160 parent_priv = MODEST_WINDOW_GET_PRIVATE (widget);
2162 fs_toggle_action = gtk_ui_manager_get_action (parent_priv->ui_manager, "/MenuBar/ViewMenu/ViewToggleFullscreenMenu");
2163 active = (gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (fs_toggle_action)))?1:0;
2164 if (is_fullscreen != active) {
2165 gtk_toggle_action_set_active (GTK_TOGGLE_ACTION (fs_toggle_action), is_fullscreen);
2167 toolbar_resize (MODEST_MSG_VIEW_WINDOW (widget));
2175 modest_msg_view_window_show_toolbar (ModestWindow *self,
2176 gboolean show_toolbar)
2178 ModestMsgViewWindowPrivate *priv = NULL;
2179 ModestWindowPrivate *parent_priv;
2180 GtkWidget *reply_button = NULL, *menu = NULL;
2181 GtkWidget *placeholder = NULL;
2183 const gchar *action_name;
2186 parent_priv = MODEST_WINDOW_GET_PRIVATE(self);
2187 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(self);
2189 /* Set optimized view status */
2190 priv->optimized_view = !show_toolbar;
2192 if (!parent_priv->toolbar) {
2193 parent_priv->toolbar = gtk_ui_manager_get_widget (parent_priv->ui_manager,
2195 gtk_widget_set_no_show_all (parent_priv->toolbar, TRUE);
2197 priv->progress_toolitem = GTK_WIDGET (gtk_tool_item_new ());
2198 priv->cancel_toolitem = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/ToolbarCancel");
2199 priv->next_toolitem = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/ToolbarMessageNext");
2200 priv->prev_toolitem = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/ToolbarMessageBack");
2201 toolbar_resize (MODEST_MSG_VIEW_WINDOW (self));
2203 /* Add ProgressBar (Transfer toolbar) */
2204 priv->progress_bar = modest_progress_bar_new ();
2205 gtk_widget_set_no_show_all (priv->progress_bar, TRUE);
2206 placeholder = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/ProgressbarView");
2207 insert_index = gtk_toolbar_get_item_index(GTK_TOOLBAR (parent_priv->toolbar), GTK_TOOL_ITEM(placeholder));
2208 gtk_container_add (GTK_CONTAINER (priv->progress_toolitem), priv->progress_bar);
2209 gtk_toolbar_insert(GTK_TOOLBAR(parent_priv->toolbar), GTK_TOOL_ITEM (priv->progress_toolitem), insert_index);
2211 /* Connect cancel 'clicked' signal to abort progress mode */
2212 g_signal_connect(priv->cancel_toolitem, "clicked",
2213 G_CALLBACK(cancel_progressbar),
2216 /* Add it to the observers list */
2217 priv->progress_widgets = g_slist_prepend(priv->progress_widgets, priv->progress_bar);
2220 hildon_window_add_toolbar (HILDON_WINDOW (self),
2221 GTK_TOOLBAR (parent_priv->toolbar));
2223 /* Set reply button tap and hold menu */
2224 reply_button = gtk_ui_manager_get_widget (parent_priv->ui_manager,
2225 "/ToolBar/ToolbarMessageReply");
2226 menu = gtk_ui_manager_get_widget (parent_priv->ui_manager,
2227 "/ToolbarReplyCSM");
2228 gtk_widget_tap_and_hold_setup (GTK_WIDGET (reply_button), menu, NULL, 0);
2232 /* Quick hack: this prevents toolbar icons "dance" when progress bar show status is changed */
2233 /* TODO: resize mode migth be GTK_RESIZE_QUEUE, in order to avoid unneccesary shows */
2234 gtk_container_set_resize_mode (GTK_CONTAINER(parent_priv->toolbar), GTK_RESIZE_IMMEDIATE);
2236 gtk_widget_show (GTK_WIDGET (parent_priv->toolbar));
2237 if (modest_msg_view_window_transfer_mode_enabled (MODEST_MSG_VIEW_WINDOW (self)))
2238 set_toolbar_mode (MODEST_MSG_VIEW_WINDOW (self), TOOLBAR_MODE_TRANSFER);
2240 set_toolbar_mode (MODEST_MSG_VIEW_WINDOW (self), TOOLBAR_MODE_NORMAL);
2243 gtk_widget_set_no_show_all (parent_priv->toolbar, TRUE);
2244 gtk_widget_hide (GTK_WIDGET (parent_priv->toolbar));
2247 /* Update also the actions (to update the toggles in the
2248 menus), we have to do it manually because some other window
2249 of the same time could have changed it (remember that the
2250 toolbar fullscreen mode is shared by all the windows of the
2252 if (modest_window_mgr_get_fullscreen_mode (modest_runtime_get_window_mgr ()))
2253 action_name = "/MenuBar/ViewMenu/ViewShowToolbarMenu/ViewShowToolbarFullScreenMenu";
2255 action_name = "/MenuBar/ViewMenu/ViewShowToolbarMenu/ViewShowToolbarNormalScreenMenu";
2257 action = gtk_ui_manager_get_action (parent_priv->ui_manager, action_name);
2258 modest_utils_toggle_action_set_active_block_notify (GTK_TOGGLE_ACTION (action),
2263 modest_msg_view_window_clipboard_owner_change (GtkClipboard *clipboard,
2265 ModestMsgViewWindow *window)
2267 if (!GTK_WIDGET_VISIBLE (window))
2270 modest_window_check_dimming_rules_group (MODEST_WINDOW (window), MODEST_DIMMING_RULES_CLIPBOARD);
2274 modest_msg_view_window_transfer_mode_enabled (ModestMsgViewWindow *self)
2276 ModestMsgViewWindowPrivate *priv;
2278 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self), FALSE);
2279 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(self);
2281 return priv->current_toolbar_mode == TOOLBAR_MODE_TRANSFER;
2285 cancel_progressbar (GtkToolButton *toolbutton,
2286 ModestMsgViewWindow *self)
2289 ModestMsgViewWindowPrivate *priv;
2291 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(self);
2293 /* Get operation observers and cancel its current operation */
2294 tmp = priv->progress_widgets;
2296 modest_progress_object_cancel_current_operation (MODEST_PROGRESS_OBJECT(tmp->data));
2297 tmp=g_slist_next(tmp);
2301 observers_empty (ModestMsgViewWindow *self)
2304 ModestMsgViewWindowPrivate *priv;
2305 gboolean is_empty = TRUE;
2306 guint pending_ops = 0;
2308 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(self);
2309 tmp = priv->progress_widgets;
2311 /* Check all observers */
2312 while (tmp && is_empty) {
2313 pending_ops = modest_progress_object_num_pending_operations (MODEST_PROGRESS_OBJECT(tmp->data));
2314 is_empty = pending_ops == 0;
2316 tmp = g_slist_next(tmp);
2323 on_account_removed (TnyAccountStore *account_store,
2324 TnyAccount *account,
2327 /* Do nothing if it's a transport account, because we only
2328 show the messages of a store account */
2329 if (tny_account_get_account_type(account) == TNY_ACCOUNT_TYPE_STORE) {
2330 const gchar *parent_acc = NULL;
2331 const gchar *our_acc = NULL;
2333 our_acc = modest_window_get_active_account (MODEST_WINDOW (user_data));
2334 parent_acc = modest_tny_account_get_parent_modest_account_name_for_server_account (account);
2336 /* Close this window if I'm showing a message of the removed account */
2337 if (strcmp (parent_acc, our_acc) == 0)
2338 modest_ui_actions_on_close_window (NULL, MODEST_WINDOW (user_data));
2343 on_mail_operation_started (ModestMailOperation *mail_op,
2346 ModestMsgViewWindow *self;
2347 ModestMailOperationTypeOperation op_type;
2349 ModestMsgViewWindowPrivate *priv;
2350 GObject *source = NULL;
2352 self = MODEST_MSG_VIEW_WINDOW (user_data);
2353 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
2354 op_type = modest_mail_operation_get_type_operation (mail_op);
2355 tmp = priv->progress_widgets;
2356 source = modest_mail_operation_get_source(mail_op);
2357 if (G_OBJECT (self) == source) {
2358 if (op_type == MODEST_MAIL_OPERATION_TYPE_RECEIVE || op_type == MODEST_MAIL_OPERATION_TYPE_OPEN ) {
2359 set_toolbar_transfer_mode(self);
2361 modest_progress_object_add_operation (
2362 MODEST_PROGRESS_OBJECT (tmp->data),
2364 tmp = g_slist_next (tmp);
2368 g_object_unref (source);
2372 on_mail_operation_finished (ModestMailOperation *mail_op,
2375 ModestMsgViewWindow *self;
2376 ModestMailOperationTypeOperation op_type;
2378 ModestMsgViewWindowPrivate *priv;
2380 self = MODEST_MSG_VIEW_WINDOW (user_data);
2381 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
2382 op_type = modest_mail_operation_get_type_operation (mail_op);
2383 tmp = priv->progress_widgets;
2385 if (op_type == MODEST_MAIL_OPERATION_TYPE_RECEIVE || op_type == MODEST_MAIL_OPERATION_TYPE_OPEN ) {
2387 modest_progress_object_remove_operation (MODEST_PROGRESS_OBJECT (tmp->data),
2389 tmp = g_slist_next (tmp);
2392 /* If no more operations are being observed, NORMAL mode is enabled again */
2393 if (observers_empty (self)) {
2394 set_toolbar_mode (self, TOOLBAR_MODE_NORMAL);
2397 /* Update dimming rules. We have to do this right here
2398 and not in view_msg_cb because at that point the
2399 transfer mode is still enabled so the dimming rule
2400 won't let the user delete the message that has been
2401 readed for example */
2402 modest_ui_actions_check_toolbar_dimming_rules (MODEST_WINDOW (self));
2403 modest_ui_actions_check_menu_dimming_rules (MODEST_WINDOW (self));
2408 on_queue_changed (ModestMailOperationQueue *queue,
2409 ModestMailOperation *mail_op,
2410 ModestMailOperationQueueNotification type,
2411 ModestMsgViewWindow *self)
2413 ModestMsgViewWindowPrivate *priv;
2415 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
2417 /* If this operations was created by another window, do nothing */
2418 if (!modest_mail_operation_is_mine (mail_op, G_OBJECT(self)))
2421 if (type == MODEST_MAIL_OPERATION_QUEUE_OPERATION_ADDED) {
2422 priv->sighandlers = modest_signal_mgr_connect (priv->sighandlers,
2424 "operation-started",
2425 G_CALLBACK (on_mail_operation_started),
2427 priv->sighandlers = modest_signal_mgr_connect (priv->sighandlers,
2429 "operation-finished",
2430 G_CALLBACK (on_mail_operation_finished),
2432 } else if (type == MODEST_MAIL_OPERATION_QUEUE_OPERATION_REMOVED) {
2433 priv->sighandlers = modest_signal_mgr_disconnect (priv->sighandlers,
2435 "operation-started");
2436 priv->sighandlers = modest_signal_mgr_disconnect (priv->sighandlers,
2438 "operation-finished");
2443 modest_msg_view_window_get_attachments (ModestMsgViewWindow *win)
2445 ModestMsgViewWindowPrivate *priv;
2446 TnyList *selected_attachments = NULL;
2448 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (win), NULL);
2449 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (win);
2451 selected_attachments = modest_msg_view_get_selected_attachments (MODEST_MSG_VIEW (priv->msg_view));
2453 return selected_attachments;
2457 modest_msg_view_window_view_attachment (ModestMsgViewWindow *window, TnyMimePart *mime_part)
2459 ModestMsgViewWindowPrivate *priv;
2460 const gchar *msg_uid;
2461 gchar *attachment_uid = NULL;
2462 gint attachment_index = 0;
2463 TnyList *attachments;
2465 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window));
2466 g_return_if_fail (TNY_IS_MIME_PART (mime_part) || (mime_part == NULL));
2467 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
2469 msg_uid = modest_msg_view_window_get_message_uid (MODEST_MSG_VIEW_WINDOW (window));
2470 attachments = modest_msg_view_get_attachments (MODEST_MSG_VIEW (priv->msg_view));
2471 attachment_index = modest_list_index (attachments, (GObject *) mime_part);
2472 g_object_unref (attachments);
2474 if (msg_uid && attachment_index >= 0) {
2475 attachment_uid = g_strdup_printf ("%s/%d", msg_uid, attachment_index);
2478 if (mime_part == NULL) {
2479 gboolean error = FALSE;
2480 TnyList *selected_attachments = modest_msg_view_get_selected_attachments (MODEST_MSG_VIEW (priv->msg_view));
2481 if (selected_attachments == NULL || tny_list_get_length (selected_attachments) == 0) {
2483 } else if (tny_list_get_length (selected_attachments) > 1) {
2484 hildon_banner_show_information (NULL, NULL, _("mcen_ib_unable_to_display_more"));
2488 iter = tny_list_create_iterator (selected_attachments);
2489 mime_part = (TnyMimePart *) tny_iterator_get_current (iter);
2490 g_object_unref (iter);
2492 g_object_unref (selected_attachments);
2497 g_object_ref (mime_part);
2500 if (tny_mime_part_is_purged (mime_part)) {
2501 g_object_unref (mime_part);
2505 if (!modest_tny_mime_part_is_msg (mime_part)) {
2506 gchar *filepath = NULL;
2507 const gchar *att_filename = tny_mime_part_get_filename (mime_part);
2508 const gchar *content_type;
2509 gboolean show_error_banner = FALSE;
2511 TnyFsStream *temp_stream = NULL;
2512 temp_stream = modest_utils_create_temp_stream (att_filename, attachment_uid,
2515 if (temp_stream != NULL) {
2516 content_type = tny_mime_part_get_content_type (mime_part);
2517 if (tny_mime_part_decode_to_stream (mime_part, TNY_STREAM (temp_stream), &err) >= 0) {
2518 /* make the file read-only */
2519 if (g_chmod(filepath, 0444) != 0)
2520 g_warning ("%s: failed to set file '%s' to read-only: %s",
2521 __FUNCTION__, filepath, strerror(errno));
2523 modest_platform_activate_file (filepath, content_type);
2525 /* error while saving attachment, maybe cerm_device_memory_full */
2526 show_error_banner = TRUE;
2528 g_warning ("%s: tny_mime_part_decode_to_stream failed (%s)", __FUNCTION__, err->message);
2532 g_object_unref (temp_stream);
2534 /* NOTE: files in the temporary area will be automatically
2535 * cleaned after some time if they are no longer in use */
2537 if (filepath != NULL) {
2538 /* the file may already exist but it isn't writable,
2539 * let's try to open it anyway */
2540 content_type = tny_mime_part_get_content_type (mime_part);
2541 modest_platform_activate_file (filepath, content_type);
2544 g_warning ("%s: modest_utils_create_temp_stream failed", __FUNCTION__);
2545 show_error_banner = TRUE;
2548 if (show_error_banner)
2549 modest_platform_information_banner (NULL, NULL, _("mail_ib_file_operation_failed"));
2551 /* message attachment */
2552 TnyHeader *header = NULL;
2553 ModestWindowMgr *mgr;
2554 ModestWindow *msg_win = NULL;
2557 header = tny_msg_get_header (TNY_MSG (mime_part));
2558 mgr = modest_runtime_get_window_mgr ();
2559 found = modest_window_mgr_find_registered_header (mgr, header, &msg_win);
2562 if (msg_win) /* there is already a window for this uid; top it */
2563 gtk_window_present (GTK_WINDOW(msg_win));
2565 /* if it's found, but there is no msg_win, it's probably in the process of being created;
2566 * thus, we don't do anything */
2567 g_warning ("window for is already being created");
2569 /* it's not found, so create a new window for it */
2570 modest_window_mgr_register_header (mgr, header, attachment_uid); /* register the uid before building the window */
2571 gchar *account = g_strdup (modest_window_get_active_account (MODEST_WINDOW (window)));
2573 account = modest_account_mgr_get_default_account (modest_runtime_get_account_mgr ());
2574 msg_win = modest_msg_view_window_new_for_attachment (TNY_MSG (mime_part), account, attachment_uid);
2575 modest_window_set_zoom (MODEST_WINDOW (msg_win),
2576 modest_window_get_zoom (MODEST_WINDOW (window)));
2577 modest_window_mgr_register_window (mgr, msg_win);
2578 gtk_widget_show_all (GTK_WIDGET (msg_win));
2581 g_object_unref (mime_part);
2594 GnomeVFSResult result;
2597 static void save_mime_part_info_free (SaveMimePartInfo *info, gboolean with_struct);
2598 static gboolean idle_save_mime_part_show_result (SaveMimePartInfo *info);
2599 static gpointer save_mime_part_to_file (SaveMimePartInfo *info);
2600 static void save_mime_parts_to_file_with_checks (SaveMimePartInfo *info);
2603 save_mime_part_info_free (SaveMimePartInfo *info, gboolean with_struct)
2607 for (node = info->pairs; node != NULL; node = g_list_next (node)) {
2608 SaveMimePartPair *pair = (SaveMimePartPair *) node->data;
2609 g_free (pair->filename);
2610 g_object_unref (pair->part);
2611 g_slice_free (SaveMimePartPair, pair);
2613 g_list_free (info->pairs);
2616 gtk_widget_destroy (info->banner);
2617 g_slice_free (SaveMimePartInfo, info);
2622 idle_save_mime_part_show_result (SaveMimePartInfo *info)
2624 if (info->pairs != NULL) {
2625 save_mime_part_to_file (info);
2627 /* This is a GDK lock because we are an idle callback and
2628 * hildon_banner_show_information is or does Gtk+ code */
2630 gdk_threads_enter (); /* CHECKED */
2631 save_mime_part_info_free (info, TRUE);
2632 if (info->result == GNOME_VFS_OK) {
2633 hildon_banner_show_information (NULL, NULL, _CS("sfil_ib_saved"));
2634 } else if (info->result == GNOME_VFS_ERROR_NO_SPACE) {
2635 hildon_banner_show_information (NULL, NULL, dgettext("ke-recv",
2636 "cerm_device_memory_full"));
2638 hildon_banner_show_information (NULL, NULL, _("mail_ib_file_operation_failed"));
2640 gdk_threads_leave (); /* CHECKED */
2647 save_mime_part_to_file (SaveMimePartInfo *info)
2649 GnomeVFSHandle *handle;
2651 SaveMimePartPair *pair = (SaveMimePartPair *) info->pairs->data;
2653 info->result = gnome_vfs_create (&handle, pair->filename, GNOME_VFS_OPEN_WRITE, FALSE, 0644);
2654 if (info->result == GNOME_VFS_OK) {
2655 stream = tny_vfs_stream_new (handle);
2656 if (tny_mime_part_decode_to_stream (pair->part, stream, NULL) < 0) {
2657 info->result = GNOME_VFS_ERROR_IO;
2659 g_object_unref (G_OBJECT (stream));
2660 g_object_unref (pair->part);
2661 g_slice_free (SaveMimePartPair, pair);
2662 info->pairs = g_list_delete_link (info->pairs, info->pairs);
2664 save_mime_part_info_free (info, FALSE);
2667 g_idle_add ((GSourceFunc) idle_save_mime_part_show_result, info);
2672 save_mime_parts_to_file_with_checks (SaveMimePartInfo *info)
2674 gboolean is_ok = TRUE;
2675 gint replaced_files = 0;
2676 const GList *files = info->pairs;
2679 for (iter = files; (iter != NULL) && (replaced_files < 2); iter = g_list_next(iter)) {
2680 SaveMimePartPair *pair = iter->data;
2681 if (modest_utils_file_exists (pair->filename)) {
2685 if (replaced_files) {
2686 GtkWidget *confirm_overwrite_dialog;
2687 const gchar *message = (replaced_files == 1) ?
2688 _FM("docm_nc_replace_file") : _FM("docm_nc_replace_multiple");
2689 confirm_overwrite_dialog = hildon_note_new_confirmation (NULL, message);
2690 if (gtk_dialog_run (GTK_DIALOG (confirm_overwrite_dialog)) != GTK_RESPONSE_OK) {
2693 gtk_widget_destroy (confirm_overwrite_dialog);
2697 save_mime_part_info_free (info, TRUE);
2699 GtkWidget *banner = hildon_banner_show_animation (NULL, NULL,
2700 _CS("sfil_ib_saving"));
2701 info->banner = banner;
2702 g_thread_create ((GThreadFunc)save_mime_part_to_file, info, FALSE, NULL);
2709 modest_msg_view_window_save_attachments (ModestMsgViewWindow *window, TnyList *mime_parts)
2711 ModestMsgViewWindowPrivate *priv;
2712 GList *files_to_save = NULL;
2713 GtkWidget *save_dialog = NULL;
2714 gchar *folder = NULL;
2715 gchar *filename = NULL;
2716 gchar *save_multiple_str = NULL;
2718 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window));
2719 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
2721 if (mime_parts == NULL) {
2722 mime_parts = modest_msg_view_get_selected_attachments (MODEST_MSG_VIEW (priv->msg_view));
2723 if (mime_parts == NULL || tny_list_get_length (mime_parts) == 0)
2726 g_object_ref (mime_parts);
2729 /* prepare dialog */
2730 if (tny_list_get_length (mime_parts) == 1) {
2732 /* only one attachment selected */
2733 iter = tny_list_create_iterator (mime_parts);
2734 TnyMimePart *mime_part = (TnyMimePart *) tny_iterator_get_current (iter);
2735 g_object_unref (iter);
2736 if (!modest_tny_mime_part_is_msg (mime_part) &&
2737 modest_tny_mime_part_is_attachment_for_modest (mime_part) &&
2738 !tny_mime_part_is_purged (mime_part)) {
2739 filename = g_strdup (tny_mime_part_get_filename (mime_part));
2741 /* TODO: show any error? */
2742 g_warning ("Tried to save a non-file attachment");
2743 g_object_unref (mime_parts);
2746 g_object_unref (mime_part);
2748 save_multiple_str = g_strdup_printf (_FM("sfil_va_number_of_objects_attachments"),
2749 tny_list_get_length (mime_parts));
2752 save_dialog = hildon_file_chooser_dialog_new (GTK_WINDOW (window),
2753 GTK_FILE_CHOOSER_ACTION_SAVE);
2756 folder = g_build_filename (g_get_home_dir (), DEFAULT_FOLDER, NULL);
2757 gtk_file_chooser_set_current_folder (GTK_FILE_CHOOSER (save_dialog), folder);
2762 gtk_file_chooser_set_current_name (GTK_FILE_CHOOSER (save_dialog),
2767 /* if multiple, set multiple string */
2768 if (save_multiple_str) {
2769 g_object_set (G_OBJECT (save_dialog), "save-multiple", save_multiple_str, NULL);
2770 gtk_window_set_title (GTK_WINDOW (save_dialog), _FM("sfil_ti_save_objects_files"));
2774 if (gtk_dialog_run (GTK_DIALOG (save_dialog)) == GTK_RESPONSE_OK) {
2775 gchar *chooser_uri = gtk_file_chooser_get_uri (GTK_FILE_CHOOSER (save_dialog));
2777 if (!modest_utils_folder_writable (chooser_uri)) {
2778 hildon_banner_show_information
2779 (NULL, NULL, dgettext("hildon-fm", "sfil_ib_readonly_location"));
2783 iter = tny_list_create_iterator (mime_parts);
2784 while (!tny_iterator_is_done (iter)) {
2785 TnyMimePart *mime_part = (TnyMimePart *) tny_iterator_get_current (iter);
2787 if ((modest_tny_mime_part_is_attachment_for_modest (mime_part)) &&
2788 !tny_mime_part_is_purged (mime_part) &&
2789 (tny_mime_part_get_filename (mime_part) != NULL)) {
2790 SaveMimePartPair *pair;
2792 pair = g_slice_new0 (SaveMimePartPair);
2793 if (save_multiple_str) {
2794 gchar *escaped = gnome_vfs_escape_slashes (
2795 tny_mime_part_get_filename (mime_part));
2796 pair->filename = g_build_filename (chooser_uri, escaped, NULL);
2799 pair->filename = g_strdup (chooser_uri);
2801 pair->part = mime_part;
2802 files_to_save = g_list_prepend (files_to_save, pair);
2804 tny_iterator_next (iter);
2806 g_object_unref (iter);
2808 g_free (chooser_uri);
2811 gtk_widget_destroy (save_dialog);
2813 g_object_unref (mime_parts);
2815 if (files_to_save != NULL) {
2816 SaveMimePartInfo *info = g_slice_new0 (SaveMimePartInfo);
2817 info->pairs = files_to_save;
2818 info->result = TRUE;
2819 save_mime_parts_to_file_with_checks (info);
2824 show_remove_attachment_information (gpointer userdata)
2826 ModestMsgViewWindow *window = (ModestMsgViewWindow *) userdata;
2827 ModestMsgViewWindowPrivate *priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
2829 /* We're outside the main lock */
2830 gdk_threads_enter ();
2832 if (priv->remove_attachment_banner != NULL) {
2833 gtk_widget_destroy (priv->remove_attachment_banner);
2834 g_object_unref (priv->remove_attachment_banner);
2837 priv->remove_attachment_banner = g_object_ref (
2838 hildon_banner_show_animation (NULL, NULL, _("mcen_ib_removing_attachment")));
2840 gdk_threads_leave ();
2846 modest_msg_view_window_remove_attachments (ModestMsgViewWindow *window, gboolean get_all)
2848 ModestMsgViewWindowPrivate *priv;
2849 TnyList *mime_parts = NULL;
2850 gchar *confirmation_message;
2855 /* TnyFolder *folder; */
2857 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window));
2858 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
2861 mime_parts = modest_msg_view_get_attachments (MODEST_MSG_VIEW (priv->msg_view));
2863 mime_parts = modest_msg_view_get_selected_attachments (MODEST_MSG_VIEW (priv->msg_view));
2865 /* Remove already purged messages from mime parts list */
2866 iter = tny_list_create_iterator (mime_parts);
2867 while (!tny_iterator_is_done (iter)) {
2868 TnyMimePart *part = TNY_MIME_PART (tny_iterator_get_current (iter));
2869 tny_iterator_next (iter);
2870 if (tny_mime_part_is_purged (part)) {
2871 tny_list_remove (mime_parts, (GObject *) part);
2873 g_object_unref (part);
2875 g_object_unref (iter);
2877 if (tny_list_get_length (mime_parts) == 0) {
2878 g_object_unref (mime_parts);
2882 n_attachments = tny_list_get_length (mime_parts);
2883 if (n_attachments == 1) {
2887 iter = tny_list_create_iterator (mime_parts);
2888 part = (TnyMimePart *) tny_iterator_get_current (iter);
2889 g_object_unref (iter);
2890 if (modest_tny_mime_part_is_msg (part)) {
2892 header = tny_msg_get_header (TNY_MSG (part));
2893 filename = tny_header_dup_subject (header);
2894 g_object_unref (header);
2895 if (filename == NULL)
2896 filename = g_strdup (_("mail_va_no_subject"));
2898 filename = g_strdup (tny_mime_part_get_filename (TNY_MIME_PART (part)));
2900 confirmation_message = g_strdup_printf (_("mcen_nc_purge_file_text"), filename);
2902 g_object_unref (part);
2904 confirmation_message = g_strdup_printf (ngettext("mcen_nc_purge_file_text",
2905 "mcen_nc_purge_files_text",
2906 n_attachments), n_attachments);
2908 response = modest_platform_run_confirmation_dialog (GTK_WINDOW (window),
2909 confirmation_message);
2910 g_free (confirmation_message);
2912 if (response != GTK_RESPONSE_OK) {
2913 g_object_unref (mime_parts);
2917 priv->purge_timeout = g_timeout_add (2000, show_remove_attachment_information, window);
2918 /* folder = tny_msg_get_folder (msg); */
2919 /* tny_msg_uncache_attachments (msg); */
2920 /* tny_folder_refresh (folder, NULL); */
2921 /* g_object_unref (folder); */
2923 iter = tny_list_create_iterator (mime_parts);
2924 while (!tny_iterator_is_done (iter)) {
2927 part = (TnyMimePart *) tny_iterator_get_current (iter);
2928 tny_mime_part_set_purged (TNY_MIME_PART (part));
2929 /* modest_msg_view_remove_attachment (MODEST_MSG_VIEW (priv->msg_view), node->data); */
2930 g_object_unref (part);
2931 tny_iterator_next (iter);
2933 g_object_unref (iter);
2935 msg = tny_msg_view_get_msg (TNY_MSG_VIEW (priv->msg_view));
2936 tny_msg_view_clear (TNY_MSG_VIEW (priv->msg_view));
2937 tny_msg_rewrite_cache (msg);
2938 tny_msg_view_set_msg (TNY_MSG_VIEW (priv->msg_view), msg);
2939 g_object_unref (msg);
2941 g_object_unref (mime_parts);
2943 if (priv->purge_timeout > 0) {
2944 g_source_remove (priv->purge_timeout);
2945 priv->purge_timeout = 0;
2948 if (priv->remove_attachment_banner) {
2949 gtk_widget_destroy (priv->remove_attachment_banner);
2950 g_object_unref (priv->remove_attachment_banner);
2951 priv->remove_attachment_banner = NULL;
2959 update_window_title (ModestMsgViewWindow *window)
2961 ModestMsgViewWindowPrivate *priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
2963 TnyHeader *header = NULL;
2964 gchar *subject = NULL;
2966 msg = tny_msg_view_get_msg (TNY_MSG_VIEW (priv->msg_view));
2969 header = tny_msg_get_header (msg);
2970 subject = tny_header_dup_subject (header);
2971 g_object_unref (header);
2972 g_object_unref (msg);
2975 if ((subject == NULL)||(subject[0] == '\0')) {
2977 subject = g_strdup (_("mail_va_no_subject"));
2980 gtk_window_set_title (GTK_WINDOW (window), subject);
2984 static void on_move_focus (GtkWidget *widget,
2985 GtkDirectionType direction,
2988 g_signal_stop_emission_by_name (G_OBJECT (widget), "move-focus");
2992 fetch_image_open_stream (TnyStreamCache *self, gint64 *expected_size, gchar *uri)
2994 GnomeVFSResult result;
2995 GnomeVFSHandle *handle = NULL;
2996 GnomeVFSFileInfo *info = NULL;
2999 result = gnome_vfs_open (&handle, uri, GNOME_VFS_OPEN_READ);
3000 if (result != GNOME_VFS_OK) {
3005 info = gnome_vfs_file_info_new ();
3006 result = gnome_vfs_get_file_info_from_handle (handle, info, GNOME_VFS_FILE_INFO_DEFAULT);
3007 if (result != GNOME_VFS_OK || ! (info->valid_fields & GNOME_VFS_FILE_INFO_FIELDS_SIZE)) {
3008 /* We put a "safe" default size for going to cache */
3009 *expected_size = (300*1024);
3011 *expected_size = info->size;
3013 gnome_vfs_file_info_unref (info);
3015 stream = tny_vfs_stream_new (handle);
3024 TnyStream *output_stream;
3025 GtkWidget *msg_view;
3029 on_fetch_image_idle_refresh_view (gpointer userdata)
3032 FetchImageData *fidata = (FetchImageData *) userdata;
3033 g_message ("REFRESH VIEW");
3034 if (GTK_WIDGET_DRAWABLE (fidata->msg_view)) {
3035 g_message ("QUEUING DRAW");
3036 gtk_widget_queue_draw (fidata->msg_view);
3038 g_object_unref (fidata->msg_view);
3039 g_slice_free (FetchImageData, fidata);
3044 on_fetch_image_thread (gpointer userdata)
3046 FetchImageData *fidata = (FetchImageData *) userdata;
3047 TnyStreamCache *cache;
3048 TnyStream *cache_stream;
3050 cache = modest_runtime_get_images_cache ();
3051 cache_stream = tny_stream_cache_get_stream (cache, fidata->cache_id, (TnyStreamCacheOpenStreamFetcher) fetch_image_open_stream, (gpointer) fidata->uri);
3052 g_free (fidata->cache_id);
3053 g_free (fidata->uri);
3055 if (cache_stream != NULL) {
3056 tny_stream_write_to_stream (cache_stream, fidata->output_stream);
3057 tny_stream_close (cache_stream);
3058 g_object_unref (cache_stream);
3061 tny_stream_close (fidata->output_stream);
3062 g_object_unref (fidata->output_stream);
3065 gdk_threads_enter ();
3066 g_idle_add (on_fetch_image_idle_refresh_view, fidata);
3067 gdk_threads_leave ();
3073 on_fetch_image (ModestMsgView *msgview,
3076 ModestMsgViewWindow *window)
3078 const gchar *current_account;
3079 ModestMsgViewWindowPrivate *priv;
3080 FetchImageData *fidata;
3082 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
3084 current_account = modest_window_get_active_account (MODEST_WINDOW (window));
3086 fidata = g_slice_new0 (FetchImageData);
3087 fidata->msg_view = g_object_ref (msgview);
3088 fidata->uri = g_strdup (uri);
3089 fidata->cache_id = modest_images_cache_get_id (current_account, uri);
3090 fidata->output_stream = g_object_ref (stream);
3092 if (g_thread_create (on_fetch_image_thread, fidata, FALSE, NULL) == NULL) {
3093 g_object_unref (fidata->output_stream);
3094 g_free (fidata->cache_id);
3095 g_free (fidata->uri);
3096 g_object_unref (fidata->msg_view);
3097 g_slice_free (FetchImageData, fidata);
3098 tny_stream_close (stream);