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-progress-object.h>
46 #include <modest-runtime.h>
47 #include <modest-window-priv.h>
48 #include <modest-tny-folder.h>
49 #include <modest-text-utils.h>
50 #include <modest-account-mgr-helpers.h>
51 #include "hildon/hildon-pannable-area.h"
52 #include <hildon/hildon-app-menu.h>
53 #include "modest-defs.h"
54 #include "modest-hildon-includes.h"
55 #include "modest-ui-dimming-manager.h"
56 #include <gdk/gdkkeysyms.h>
57 #include <modest-tny-account.h>
58 #include <modest-mime-part-view.h>
59 #include <modest-isearch-view.h>
60 #include <modest-tny-mime-part.h>
63 #include <glib/gstdio.h>
64 #include <modest-debug.h>
65 #include <modest-header-window.h>
67 #define DEFAULT_FOLDER "MyDocs/.documents"
69 typedef struct _ModestMsgViewWindowPrivate ModestMsgViewWindowPrivate;
70 struct _ModestMsgViewWindowPrivate {
73 GtkWidget *main_scroll;
74 GtkWidget *find_toolbar;
77 /* Progress observers */
78 GSList *progress_widgets;
81 GtkWidget *prev_toolitem;
82 GtkWidget *next_toolitem;
83 gboolean progress_hint;
85 /* Optimized view enabled */
86 gboolean optimized_view;
88 /* Whether this was created via the *_new_for_search_result() function. */
89 gboolean is_search_result;
91 /* Whether the message is in outbox */
94 /* A reference to the @model of the header view
95 * to allow selecting previous/next messages,
96 * if the message is currently selected in the header view.
98 const gchar *header_folder_id;
99 GtkTreeModel *header_model;
100 GtkTreeRowReference *row_reference;
101 GtkTreeRowReference *next_row_reference;
103 gulong clipboard_change_handler;
104 gulong queue_change_handler;
105 gulong account_removed_handler;
106 gulong row_changed_handler;
107 gulong row_deleted_handler;
108 gulong row_inserted_handler;
109 gulong rows_reordered_handler;
112 GtkWidget *remove_attachment_banner;
119 static void modest_msg_view_window_class_init (ModestMsgViewWindowClass *klass);
120 static void modest_msg_view_window_init (ModestMsgViewWindow *obj);
121 static void modest_header_view_observer_init(
122 ModestHeaderViewObserverIface *iface_class);
123 static void modest_msg_view_window_finalize (GObject *obj);
124 static void modest_msg_view_window_toggle_find_toolbar (GtkToggleAction *obj,
126 static void modest_msg_view_window_find_toolbar_close (GtkWidget *widget,
127 ModestMsgViewWindow *obj);
128 static void modest_msg_view_window_find_toolbar_search (GtkWidget *widget,
129 ModestMsgViewWindow *obj);
131 static void modest_msg_view_window_disconnect_signals (ModestWindow *self);
132 static void modest_msg_view_window_set_zoom (ModestWindow *window,
134 static gdouble modest_msg_view_window_get_zoom (ModestWindow *window);
135 static gboolean modest_msg_view_window_zoom_minus (ModestWindow *window);
136 static gboolean modest_msg_view_window_zoom_plus (ModestWindow *window);
137 static gboolean modest_msg_view_window_key_event (GtkWidget *window,
140 static gboolean modest_msg_view_window_toggle_menu (HildonWindow *window,
143 static void modest_msg_view_window_update_priority (ModestMsgViewWindow *window);
145 static void modest_msg_view_window_show_toolbar (ModestWindow *window,
146 gboolean show_toolbar);
148 static void modest_msg_view_window_clipboard_owner_change (GtkClipboard *clipboard,
150 ModestMsgViewWindow *window);
152 static void modest_msg_view_window_on_row_changed (GtkTreeModel *header_model,
155 ModestMsgViewWindow *window);
157 static void modest_msg_view_window_on_row_deleted (GtkTreeModel *header_model,
159 ModestMsgViewWindow *window);
161 static void modest_msg_view_window_on_row_inserted (GtkTreeModel *header_model,
162 GtkTreePath *tree_path,
163 GtkTreeIter *tree_iter,
164 ModestMsgViewWindow *window);
166 static void modest_msg_view_window_on_row_reordered (GtkTreeModel *header_model,
170 ModestMsgViewWindow *window);
172 static void modest_msg_view_window_update_model_replaced (ModestHeaderViewObserver *window,
174 const gchar *tny_folder_id);
176 static void on_queue_changed (ModestMailOperationQueue *queue,
177 ModestMailOperation *mail_op,
178 ModestMailOperationQueueNotification type,
179 ModestMsgViewWindow *self);
181 static void on_account_removed (TnyAccountStore *account_store,
185 static void on_move_focus (GtkWidget *widget,
186 GtkDirectionType direction,
189 static void view_msg_cb (ModestMailOperation *mail_op,
196 static void set_progress_hint (ModestMsgViewWindow *self,
199 static void update_window_title (ModestMsgViewWindow *window);
201 static gboolean set_toolbar_transfer_mode (ModestMsgViewWindow *self);
202 static void init_window (ModestMsgViewWindow *obj);
204 static gboolean msg_is_visible (TnyHeader *header, gboolean check_outbox);
206 static void check_dimming_rules_after_change (ModestMsgViewWindow *window);
208 static gboolean on_fetch_image (ModestMsgView *msgview,
211 ModestMsgViewWindow *window);
213 static gboolean modest_msg_view_window_scroll_child (ModestMsgViewWindow *self,
214 GtkScrollType scroll_type,
217 static gboolean message_reader (ModestMsgViewWindow *window,
218 ModestMsgViewWindowPrivate *priv,
220 GtkTreeRowReference *row_reference);
222 static void add_to_menu (ModestMsgViewWindow *self,
226 ModestDimmingRulesGroup *group,
227 GCallback dimming_callback);
228 static void setup_menu (ModestMsgViewWindow *self,
229 ModestDimmingRulesGroup *group);
231 /* list my signals */
238 static const GtkToggleActionEntry msg_view_toggle_action_entries [] = {
239 { "FindInMessage", MODEST_TOOLBAR_ICON_FIND, N_("qgn_toolb_gene_find"), NULL, NULL, G_CALLBACK (modest_msg_view_window_toggle_find_toolbar), FALSE },
240 { "ToolsFindInMessage", NULL, N_("mcen_me_viewer_find"), "<CTRL>F", NULL, G_CALLBACK (modest_msg_view_window_toggle_find_toolbar), FALSE },
243 #define MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE((o), \
244 MODEST_TYPE_MSG_VIEW_WINDOW, \
245 ModestMsgViewWindowPrivate))
247 static GtkWindowClass *parent_class = NULL;
249 /* uncomment the following if you have defined any signals */
250 static guint signals[LAST_SIGNAL] = {0};
253 modest_msg_view_window_get_type (void)
255 static GType my_type = 0;
257 static const GTypeInfo my_info = {
258 sizeof(ModestMsgViewWindowClass),
259 NULL, /* base init */
260 NULL, /* base finalize */
261 (GClassInitFunc) modest_msg_view_window_class_init,
262 NULL, /* class finalize */
263 NULL, /* class data */
264 sizeof(ModestMsgViewWindow),
266 (GInstanceInitFunc) modest_msg_view_window_init,
269 my_type = g_type_register_static (MODEST_TYPE_WINDOW,
270 "ModestMsgViewWindow",
273 static const GInterfaceInfo modest_header_view_observer_info =
275 (GInterfaceInitFunc) modest_header_view_observer_init,
276 NULL, /* interface_finalize */
277 NULL /* interface_data */
280 g_type_add_interface_static (my_type,
281 MODEST_TYPE_HEADER_VIEW_OBSERVER,
282 &modest_header_view_observer_info);
288 save_state (ModestWindow *self)
290 modest_widget_memory_save (modest_runtime_get_conf (),
292 MODEST_CONF_MSG_VIEW_WINDOW_KEY);
297 restore_settings (ModestMsgViewWindow *self)
301 conf = modest_runtime_get_conf ();
302 modest_widget_memory_restore (conf,
304 MODEST_CONF_MSG_VIEW_WINDOW_KEY);
307 static gboolean modest_msg_view_window_scroll_child (ModestMsgViewWindow *self,
308 GtkScrollType scroll_type,
312 ModestMsgViewWindowPrivate *priv;
313 gboolean return_value;
315 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(self);
316 g_signal_emit_by_name (priv->main_scroll, "scroll-child", scroll_type, horizontal, &return_value);
321 add_scroll_binding (GtkBindingSet *binding_set,
323 GtkScrollType scroll)
325 guint keypad_keyval = keyval - GDK_Left + GDK_KP_Left;
327 gtk_binding_entry_add_signal (binding_set, keyval, 0,
329 GTK_TYPE_SCROLL_TYPE, scroll,
330 G_TYPE_BOOLEAN, FALSE);
331 gtk_binding_entry_add_signal (binding_set, keypad_keyval, 0,
333 GTK_TYPE_SCROLL_TYPE, scroll,
334 G_TYPE_BOOLEAN, FALSE);
338 modest_msg_view_window_class_init (ModestMsgViewWindowClass *klass)
340 GObjectClass *gobject_class;
341 HildonWindowClass *hildon_window_class;
342 ModestWindowClass *modest_window_class;
343 GtkBindingSet *binding_set;
345 gobject_class = (GObjectClass*) klass;
346 hildon_window_class = (HildonWindowClass *) klass;
347 modest_window_class = (ModestWindowClass *) klass;
349 parent_class = g_type_class_peek_parent (klass);
350 gobject_class->finalize = modest_msg_view_window_finalize;
352 hildon_window_class->toggle_menu = modest_msg_view_window_toggle_menu;
354 modest_window_class->set_zoom_func = modest_msg_view_window_set_zoom;
355 modest_window_class->get_zoom_func = modest_msg_view_window_get_zoom;
356 modest_window_class->zoom_minus_func = modest_msg_view_window_zoom_minus;
357 modest_window_class->zoom_plus_func = modest_msg_view_window_zoom_plus;
358 modest_window_class->show_toolbar_func = modest_msg_view_window_show_toolbar;
359 modest_window_class->disconnect_signals_func = modest_msg_view_window_disconnect_signals;
361 modest_window_class->save_state_func = save_state;
363 klass->scroll_child = modest_msg_view_window_scroll_child;
365 signals[MSG_CHANGED_SIGNAL] =
366 g_signal_new ("msg-changed",
367 G_TYPE_FROM_CLASS (gobject_class),
369 G_STRUCT_OFFSET (ModestMsgViewWindowClass, msg_changed),
371 modest_marshal_VOID__POINTER_POINTER,
372 G_TYPE_NONE, 2, G_TYPE_POINTER, G_TYPE_POINTER);
374 signals[SCROLL_CHILD_SIGNAL] =
375 g_signal_new ("scroll-child",
376 G_TYPE_FROM_CLASS (gobject_class),
377 G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
378 G_STRUCT_OFFSET (ModestMsgViewWindowClass, scroll_child),
380 modest_marshal_BOOLEAN__ENUM_BOOLEAN,
381 G_TYPE_BOOLEAN, 2, GTK_TYPE_SCROLL_TYPE, G_TYPE_BOOLEAN);
383 binding_set = gtk_binding_set_by_class (klass);
384 add_scroll_binding (binding_set, GDK_Up, GTK_SCROLL_STEP_UP);
385 add_scroll_binding (binding_set, GDK_Down, GTK_SCROLL_STEP_DOWN);
386 add_scroll_binding (binding_set, GDK_Page_Up, GTK_SCROLL_PAGE_UP);
387 add_scroll_binding (binding_set, GDK_Page_Down, GTK_SCROLL_PAGE_DOWN);
388 add_scroll_binding (binding_set, GDK_Home, GTK_SCROLL_START);
389 add_scroll_binding (binding_set, GDK_End, GTK_SCROLL_END);
391 g_type_class_add_private (gobject_class, sizeof(ModestMsgViewWindowPrivate));
395 static void modest_header_view_observer_init(
396 ModestHeaderViewObserverIface *iface_class)
398 iface_class->update_func = modest_msg_view_window_update_model_replaced;
402 modest_msg_view_window_init (ModestMsgViewWindow *obj)
404 ModestMsgViewWindowPrivate *priv;
405 ModestWindowPrivate *parent_priv = NULL;
406 GtkActionGroup *action_group = NULL;
407 GError *error = NULL;
408 GdkPixbuf *window_icon;
410 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(obj);
411 parent_priv = MODEST_WINDOW_GET_PRIVATE(obj);
412 parent_priv->ui_manager = gtk_ui_manager_new();
414 action_group = gtk_action_group_new ("ModestMsgViewWindowActions");
415 gtk_action_group_set_translation_domain (action_group, GETTEXT_PACKAGE);
417 /* Add common actions */
418 gtk_action_group_add_actions (action_group,
419 modest_action_entries,
420 G_N_ELEMENTS (modest_action_entries),
422 gtk_action_group_add_toggle_actions (action_group,
423 modest_toggle_action_entries,
424 G_N_ELEMENTS (modest_toggle_action_entries),
426 gtk_action_group_add_toggle_actions (action_group,
427 msg_view_toggle_action_entries,
428 G_N_ELEMENTS (msg_view_toggle_action_entries),
431 gtk_ui_manager_insert_action_group (parent_priv->ui_manager, action_group, 0);
432 g_object_unref (action_group);
434 /* Load the UI definition */
435 gtk_ui_manager_add_ui_from_file (parent_priv->ui_manager, MODEST_UIDIR "modest-msg-view-window-ui.xml",
438 g_printerr ("modest: could not merge modest-msg-view-window-ui.xml: %s\n", error->message);
439 g_error_free (error);
444 /* Add accelerators */
445 gtk_window_add_accel_group (GTK_WINDOW (obj),
446 gtk_ui_manager_get_accel_group (parent_priv->ui_manager));
448 priv->is_search_result = FALSE;
449 priv->is_outbox = FALSE;
451 priv->msg_view = NULL;
452 priv->header_model = NULL;
453 priv->header_folder_id = NULL;
454 priv->clipboard_change_handler = 0;
455 priv->queue_change_handler = 0;
456 priv->account_removed_handler = 0;
457 priv->row_changed_handler = 0;
458 priv->row_deleted_handler = 0;
459 priv->row_inserted_handler = 0;
460 priv->rows_reordered_handler = 0;
461 priv->progress_hint = FALSE;
463 priv->optimized_view = FALSE;
464 priv->purge_timeout = 0;
465 priv->remove_attachment_banner = NULL;
466 priv->msg_uid = NULL;
468 priv->sighandlers = NULL;
471 init_window (MODEST_MSG_VIEW_WINDOW(obj));
473 /* Set window icon */
474 window_icon = modest_platform_get_icon (MODEST_APP_MSG_VIEW_ICON, MODEST_ICON_SIZE_BIG);
476 gtk_window_set_icon (GTK_WINDOW (obj), window_icon);
477 g_object_unref (window_icon);
480 hildon_program_add_window (hildon_program_get_instance(),
483 modest_window_mgr_register_help_id (modest_runtime_get_window_mgr(),
484 GTK_WINDOW(obj),"applications_email_viewer");
489 set_toolbar_transfer_mode (ModestMsgViewWindow *self)
491 ModestMsgViewWindowPrivate *priv = NULL;
493 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self), FALSE);
495 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(self);
497 set_progress_hint (self, TRUE);
503 set_progress_hint (ModestMsgViewWindow *self,
506 ModestWindowPrivate *parent_priv;
507 ModestMsgViewWindowPrivate *priv;
509 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self));
511 parent_priv = MODEST_WINDOW_GET_PRIVATE(self);
512 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(self);
514 /* Sets current progress hint */
515 priv->progress_hint = enabled;
517 if (GTK_WIDGET_VISIBLE (self)) {
518 hildon_gtk_window_set_progress_indicator (GTK_WINDOW (self), enabled?1:0);
525 init_window (ModestMsgViewWindow *obj)
527 GtkWidget *main_vbox;
528 ModestMsgViewWindowPrivate *priv;
530 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(obj);
532 priv->msg_view = GTK_WIDGET (tny_platform_factory_new_msg_view (modest_tny_platform_factory_get_instance ()));
533 modest_msg_view_set_shadow_type (MODEST_MSG_VIEW (priv->msg_view), GTK_SHADOW_NONE);
534 main_vbox = gtk_vbox_new (FALSE, 6);
535 #ifdef MODEST_TOOLKIT_HILDON2
536 priv->main_scroll = hildon_pannable_area_new ();
537 gtk_container_add (GTK_CONTAINER (priv->main_scroll), priv->msg_view);
539 #ifdef MODEST_USE_MOZEMBED
540 priv->main_scroll = priv->msg_view;
541 gtk_widget_set_size_request (priv->msg_view, -1, 1600);
543 priv->main_scroll = gtk_scrolled_window_new (NULL, NULL);
544 gtk_container_add (GTK_CONTAINER (priv->main_scroll), priv->msg_view);
546 gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (priv->main_scroll), GTK_POLICY_NEVER, GTK_POLICY_AUTOMATIC);
547 gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW (priv->main_scroll), GTK_SHADOW_NONE);
548 modest_maemo_set_thumbable_scrollbar (GTK_SCROLLED_WINDOW(priv->main_scroll), TRUE);
551 gtk_box_pack_start (GTK_BOX(main_vbox), priv->main_scroll, TRUE, TRUE, 0);
552 gtk_container_add (GTK_CONTAINER(obj), main_vbox);
554 priv->find_toolbar = hildon_find_toolbar_new (NULL);
555 hildon_window_add_toolbar (HILDON_WINDOW (obj), GTK_TOOLBAR (priv->find_toolbar));
556 gtk_widget_set_no_show_all (priv->find_toolbar, TRUE);
558 gtk_widget_show_all (GTK_WIDGET(main_vbox));
562 modest_msg_view_window_disconnect_signals (ModestWindow *self)
564 ModestMsgViewWindowPrivate *priv;
565 GtkWidget *header_view = NULL;
566 GtkWindow *parent_window = NULL;
568 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
570 if (gtk_clipboard_get (GDK_SELECTION_PRIMARY) &&
571 g_signal_handler_is_connected (gtk_clipboard_get (GDK_SELECTION_PRIMARY),
572 priv->clipboard_change_handler))
573 g_signal_handler_disconnect (gtk_clipboard_get (GDK_SELECTION_PRIMARY),
574 priv->clipboard_change_handler);
576 if (g_signal_handler_is_connected (G_OBJECT (modest_runtime_get_mail_operation_queue ()),
577 priv->queue_change_handler))
578 g_signal_handler_disconnect (G_OBJECT (modest_runtime_get_mail_operation_queue ()),
579 priv->queue_change_handler);
581 if (g_signal_handler_is_connected (G_OBJECT (modest_runtime_get_account_store ()),
582 priv->account_removed_handler))
583 g_signal_handler_disconnect (G_OBJECT (modest_runtime_get_account_store ()),
584 priv->account_removed_handler);
586 if (priv->header_model) {
587 if (g_signal_handler_is_connected(G_OBJECT (priv->header_model),
588 priv->row_changed_handler))
589 g_signal_handler_disconnect(G_OBJECT (priv->header_model),
590 priv->row_changed_handler);
592 if (g_signal_handler_is_connected(G_OBJECT (priv->header_model),
593 priv->row_deleted_handler))
594 g_signal_handler_disconnect(G_OBJECT (priv->header_model),
595 priv->row_deleted_handler);
597 if (g_signal_handler_is_connected(G_OBJECT (priv->header_model),
598 priv->row_inserted_handler))
599 g_signal_handler_disconnect(G_OBJECT (priv->header_model),
600 priv->row_inserted_handler);
602 if (g_signal_handler_is_connected(G_OBJECT (priv->header_model),
603 priv->rows_reordered_handler))
604 g_signal_handler_disconnect(G_OBJECT (priv->header_model),
605 priv->rows_reordered_handler);
608 modest_signal_mgr_disconnect_all_and_destroy (priv->sighandlers);
609 priv->sighandlers = NULL;
611 parent_window = gtk_window_get_transient_for (GTK_WINDOW (self));
612 if (parent_window && MODEST_IS_HEADER_WINDOW (parent_window)) {
613 header_view = GTK_WIDGET (modest_header_window_get_header_view (MODEST_HEADER_WINDOW (parent_window)));
615 modest_header_view_remove_observer(MODEST_HEADER_VIEW (header_view),
616 MODEST_HEADER_VIEW_OBSERVER(self));
622 modest_msg_view_window_finalize (GObject *obj)
624 ModestMsgViewWindowPrivate *priv;
626 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (obj);
628 /* Sanity check: shouldn't be needed, the window mgr should
629 call this function before */
630 modest_msg_view_window_disconnect_signals (MODEST_WINDOW (obj));
632 if (priv->header_model != NULL) {
633 g_object_unref (priv->header_model);
634 priv->header_model = NULL;
637 if (priv->remove_attachment_banner) {
638 gtk_widget_destroy (priv->remove_attachment_banner);
639 g_object_unref (priv->remove_attachment_banner);
640 priv->remove_attachment_banner = NULL;
643 if (priv->purge_timeout > 0) {
644 g_source_remove (priv->purge_timeout);
645 priv->purge_timeout = 0;
648 if (priv->row_reference) {
649 gtk_tree_row_reference_free (priv->row_reference);
650 priv->row_reference = NULL;
653 if (priv->next_row_reference) {
654 gtk_tree_row_reference_free (priv->next_row_reference);
655 priv->next_row_reference = NULL;
659 g_free (priv->msg_uid);
660 priv->msg_uid = NULL;
663 G_OBJECT_CLASS(parent_class)->finalize (obj);
667 select_next_valid_row (GtkTreeModel *model,
668 GtkTreeRowReference **row_reference,
672 GtkTreeIter tmp_iter;
674 GtkTreePath *next = NULL;
675 gboolean retval = FALSE, finished;
677 g_return_val_if_fail (gtk_tree_row_reference_valid (*row_reference), FALSE);
679 path = gtk_tree_row_reference_get_path (*row_reference);
680 gtk_tree_model_get_iter (model, &tmp_iter, path);
681 gtk_tree_row_reference_free (*row_reference);
682 *row_reference = NULL;
686 TnyHeader *header = NULL;
688 if (gtk_tree_model_iter_next (model, &tmp_iter)) {
689 gtk_tree_model_get (model, &tmp_iter,
690 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
694 if (msg_is_visible (header, is_outbox)) {
695 next = gtk_tree_model_get_path (model, &tmp_iter);
696 *row_reference = gtk_tree_row_reference_new (model, next);
697 gtk_tree_path_free (next);
701 g_object_unref (header);
704 } else if (cycle && gtk_tree_model_get_iter_first (model, &tmp_iter)) {
705 next = gtk_tree_model_get_path (model, &tmp_iter);
707 /* Ensure that we are not selecting the same */
708 if (gtk_tree_path_compare (path, next) != 0) {
709 gtk_tree_model_get (model, &tmp_iter,
710 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
713 if (msg_is_visible (header, is_outbox)) {
714 *row_reference = gtk_tree_row_reference_new (model, next);
718 g_object_unref (header);
722 /* If we ended up in the same message
723 then there is no valid next
727 gtk_tree_path_free (next);
729 /* If there are no more messages and we don't
730 want to start again in the first one then
731 there is no valid next message */
737 gtk_tree_path_free (path);
742 /* TODO: This should be in _init(), with the parameters as properties. */
744 modest_msg_view_window_construct (ModestMsgViewWindow *self,
745 const gchar *modest_account_name,
746 const gchar *msg_uid)
749 ModestMsgViewWindowPrivate *priv = NULL;
750 ModestWindowPrivate *parent_priv = NULL;
751 ModestDimmingRulesGroup *menu_rules_group = NULL;
752 ModestDimmingRulesGroup *toolbar_rules_group = NULL;
753 ModestDimmingRulesGroup *clipboard_rules_group = NULL;
755 obj = G_OBJECT (self);
756 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(obj);
757 parent_priv = MODEST_WINDOW_GET_PRIVATE(obj);
759 priv->msg_uid = g_strdup (msg_uid);
762 parent_priv->menubar = NULL;
763 parent_priv->ui_dimming_manager = modest_ui_dimming_manager_new();
765 menu_rules_group = modest_dimming_rules_group_new (MODEST_DIMMING_RULES_MENU, FALSE);
766 toolbar_rules_group = modest_dimming_rules_group_new (MODEST_DIMMING_RULES_TOOLBAR, TRUE);
767 clipboard_rules_group = modest_dimming_rules_group_new (MODEST_DIMMING_RULES_CLIPBOARD, FALSE);
769 setup_menu (self, menu_rules_group);
770 /* Add common dimming rules */
771 modest_dimming_rules_group_add_rules (menu_rules_group,
772 modest_msg_view_menu_dimming_entries,
773 G_N_ELEMENTS (modest_msg_view_menu_dimming_entries),
774 MODEST_WINDOW (self));
775 modest_dimming_rules_group_add_rules (toolbar_rules_group,
776 modest_msg_view_toolbar_dimming_entries,
777 G_N_ELEMENTS (modest_msg_view_toolbar_dimming_entries),
778 MODEST_WINDOW (self));
779 modest_dimming_rules_group_add_rules (clipboard_rules_group,
780 modest_msg_view_clipboard_dimming_entries,
781 G_N_ELEMENTS (modest_msg_view_clipboard_dimming_entries),
782 MODEST_WINDOW (self));
784 /* Insert dimming rules group for this window */
785 modest_ui_dimming_manager_insert_rules_group (parent_priv->ui_dimming_manager, menu_rules_group);
786 modest_ui_dimming_manager_insert_rules_group (parent_priv->ui_dimming_manager, toolbar_rules_group);
787 modest_ui_dimming_manager_insert_rules_group (parent_priv->ui_dimming_manager, clipboard_rules_group);
788 g_object_unref (menu_rules_group);
789 g_object_unref (toolbar_rules_group);
790 g_object_unref (clipboard_rules_group);
792 restore_settings (MODEST_MSG_VIEW_WINDOW(obj));
794 /* g_signal_connect (G_OBJECT(obj), "delete-event", G_CALLBACK(on_delete_event), obj); */
796 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);
797 g_signal_connect (G_OBJECT(priv->msg_view), "activate_link",
798 G_CALLBACK (modest_ui_actions_on_msg_link_clicked), obj);
799 g_signal_connect (G_OBJECT(priv->msg_view), "link_hover",
800 G_CALLBACK (modest_ui_actions_on_msg_link_hover), obj);
801 g_signal_connect (G_OBJECT(priv->msg_view), "attachment_clicked",
802 G_CALLBACK (modest_ui_actions_on_msg_attachment_clicked), obj);
803 g_signal_connect (G_OBJECT(priv->msg_view), "recpt_activated",
804 G_CALLBACK (modest_ui_actions_on_msg_recpt_activated), obj);
805 g_signal_connect (G_OBJECT(priv->msg_view), "show_details",
806 G_CALLBACK (modest_ui_actions_on_details), obj);
807 g_signal_connect (G_OBJECT(priv->msg_view), "link_contextual",
808 G_CALLBACK (modest_ui_actions_on_msg_link_contextual), obj);
809 g_signal_connect (G_OBJECT (priv->msg_view), "fetch_image",
810 G_CALLBACK (on_fetch_image), obj);
812 g_signal_connect (G_OBJECT (obj), "key-release-event",
813 G_CALLBACK (modest_msg_view_window_key_event),
816 g_signal_connect (G_OBJECT (obj), "key-press-event",
817 G_CALLBACK (modest_msg_view_window_key_event),
820 g_signal_connect (G_OBJECT (obj), "move-focus",
821 G_CALLBACK (on_move_focus), obj);
823 /* Mail Operation Queue */
824 priv->queue_change_handler = g_signal_connect (G_OBJECT (modest_runtime_get_mail_operation_queue ()),
826 G_CALLBACK (on_queue_changed),
829 /* Account manager */
830 priv->account_removed_handler = g_signal_connect (G_OBJECT (modest_runtime_get_account_store ()),
832 G_CALLBACK(on_account_removed),
835 modest_window_set_active_account (MODEST_WINDOW(obj), modest_account_name);
837 g_signal_connect (G_OBJECT (priv->find_toolbar), "close", G_CALLBACK (modest_msg_view_window_find_toolbar_close), obj);
838 g_signal_connect (G_OBJECT (priv->find_toolbar), "search", G_CALLBACK (modest_msg_view_window_find_toolbar_search), obj);
839 priv->last_search = NULL;
841 modest_msg_view_window_show_toolbar (MODEST_WINDOW (obj), TRUE);
843 /* Init the clipboard actions dim status */
844 modest_msg_view_grab_focus(MODEST_MSG_VIEW (priv->msg_view));
846 update_window_title (MODEST_MSG_VIEW_WINDOW (obj));
851 /* FIXME: parameter checks */
853 modest_msg_view_window_new_with_header_model (TnyMsg *msg,
854 const gchar *modest_account_name,
855 const gchar *msg_uid,
857 GtkTreeRowReference *row_reference)
859 ModestMsgViewWindow *window = NULL;
860 ModestMsgViewWindowPrivate *priv = NULL;
861 TnyFolder *header_folder = NULL;
862 ModestHeaderView *header_view = NULL;
863 ModestWindow *main_window = NULL;
864 ModestWindowMgr *mgr = NULL;
867 modest_tny_mime_part_to_string (TNY_MIME_PART (msg), 0);
870 mgr = modest_runtime_get_window_mgr ();
871 window = MODEST_MSG_VIEW_WINDOW (modest_window_mgr_get_msg_view_window (mgr));
872 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), NULL);
874 modest_msg_view_window_construct (window, modest_account_name, msg_uid);
876 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
878 /* Remember the message list's TreeModel so we can detect changes
879 * and change the list selection when necessary: */
881 main_window = modest_window_mgr_get_main_window(mgr, FALSE); /* don't create */
883 header_view = MODEST_HEADER_VIEW(modest_main_window_get_child_widget(
884 MODEST_MAIN_WINDOW(main_window),
885 MODEST_MAIN_WINDOW_WIDGET_TYPE_HEADER_VIEW));
888 if (header_view != NULL){
889 header_folder = modest_header_view_get_folder(header_view);
890 /* This could happen if the header folder was
891 unseleted before opening this msg window (for
892 example if the user selects an account in the
893 folder view of the main window */
895 priv->is_outbox = (modest_tny_folder_guess_folder_type (header_folder) == TNY_FOLDER_TYPE_OUTBOX);
896 priv->header_folder_id = tny_folder_get_id(header_folder);
897 g_assert(priv->header_folder_id != NULL);
898 g_object_unref(header_folder);
902 /* Setup row references and connect signals */
903 priv->header_model = g_object_ref (model);
906 priv->row_reference = gtk_tree_row_reference_copy (row_reference);
907 priv->next_row_reference = gtk_tree_row_reference_copy (row_reference);
908 select_next_valid_row (model, &(priv->next_row_reference), TRUE, priv->is_outbox);
910 priv->row_reference = NULL;
911 priv->next_row_reference = NULL;
914 /* Connect signals */
915 priv->row_changed_handler =
916 g_signal_connect (GTK_TREE_MODEL(model), "row-changed",
917 G_CALLBACK(modest_msg_view_window_on_row_changed),
919 priv->row_deleted_handler =
920 g_signal_connect (GTK_TREE_MODEL(model), "row-deleted",
921 G_CALLBACK(modest_msg_view_window_on_row_deleted),
923 priv->row_inserted_handler =
924 g_signal_connect (GTK_TREE_MODEL(model), "row-inserted",
925 G_CALLBACK(modest_msg_view_window_on_row_inserted),
927 priv->rows_reordered_handler =
928 g_signal_connect(GTK_TREE_MODEL(model), "rows-reordered",
929 G_CALLBACK(modest_msg_view_window_on_row_reordered),
932 if (header_view != NULL){
933 modest_header_view_add_observer(header_view,
934 MODEST_HEADER_VIEW_OBSERVER(window));
937 tny_msg_view_set_msg (TNY_MSG_VIEW (priv->msg_view), msg);
938 update_window_title (MODEST_MSG_VIEW_WINDOW (window));
940 /* gtk_widget_show_all (GTK_WIDGET (window)); */
941 modest_msg_view_window_update_priority (window);
942 /* Check dimming rules */
943 modest_ui_actions_check_toolbar_dimming_rules (MODEST_WINDOW (window));
944 modest_ui_actions_check_menu_dimming_rules (MODEST_WINDOW (window));
945 modest_window_check_dimming_rules_group (MODEST_WINDOW (window), MODEST_DIMMING_RULES_CLIPBOARD);
947 return MODEST_WINDOW(window);
951 modest_msg_view_window_new_from_header_view (ModestHeaderView *header_view,
952 const gchar *modest_account_name,
953 const gchar *msg_uid,
954 GtkTreeRowReference *row_reference)
956 ModestMsgViewWindow *window = NULL;
957 ModestMsgViewWindowPrivate *priv = NULL;
958 TnyFolder *header_folder = NULL;
959 ModestWindowMgr *mgr = NULL;
963 mgr = modest_runtime_get_window_mgr ();
964 window = MODEST_MSG_VIEW_WINDOW (modest_window_mgr_get_msg_view_window (mgr));
965 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), NULL);
967 modest_msg_view_window_construct (window, modest_account_name, msg_uid);
969 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
971 /* Remember the message list's TreeModel so we can detect changes
972 * and change the list selection when necessary: */
974 if (header_view != NULL){
975 header_folder = modest_header_view_get_folder(header_view);
976 /* This could happen if the header folder was
977 unseleted before opening this msg window (for
978 example if the user selects an account in the
979 folder view of the main window */
981 priv->is_outbox = (modest_tny_folder_guess_folder_type (header_folder) == TNY_FOLDER_TYPE_OUTBOX);
982 priv->header_folder_id = tny_folder_get_id(header_folder);
983 g_assert(priv->header_folder_id != NULL);
984 g_object_unref(header_folder);
988 /* Setup row references and connect signals */
989 priv->header_model = gtk_tree_view_get_model (GTK_TREE_VIEW (header_view));
992 priv->row_reference = gtk_tree_row_reference_copy (row_reference);
993 priv->next_row_reference = gtk_tree_row_reference_copy (row_reference);
994 select_next_valid_row (priv->header_model, &(priv->next_row_reference), TRUE, priv->is_outbox);
996 priv->row_reference = NULL;
997 priv->next_row_reference = NULL;
1000 /* Connect signals */
1001 priv->row_changed_handler =
1002 g_signal_connect (GTK_TREE_MODEL(priv->header_model), "row-changed",
1003 G_CALLBACK(modest_msg_view_window_on_row_changed),
1005 priv->row_deleted_handler =
1006 g_signal_connect (GTK_TREE_MODEL(priv->header_model), "row-deleted",
1007 G_CALLBACK(modest_msg_view_window_on_row_deleted),
1009 priv->row_inserted_handler =
1010 g_signal_connect (GTK_TREE_MODEL(priv->header_model), "row-inserted",
1011 G_CALLBACK(modest_msg_view_window_on_row_inserted),
1013 priv->rows_reordered_handler =
1014 g_signal_connect(GTK_TREE_MODEL(priv->header_model), "rows-reordered",
1015 G_CALLBACK(modest_msg_view_window_on_row_reordered),
1018 if (header_view != NULL){
1019 modest_header_view_add_observer(header_view,
1020 MODEST_HEADER_VIEW_OBSERVER(window));
1023 tny_msg_view_set_msg (TNY_MSG_VIEW (priv->msg_view), NULL);
1025 path = gtk_tree_row_reference_get_path (row_reference);
1026 if (gtk_tree_model_get_iter (priv->header_model, &iter, path)) {
1028 gtk_tree_model_get (priv->header_model, &iter,
1029 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
1031 message_reader (window, priv, header, row_reference);
1033 gtk_tree_path_free (path);
1035 /* Check dimming rules */
1036 modest_ui_actions_check_toolbar_dimming_rules (MODEST_WINDOW (window));
1037 modest_ui_actions_check_menu_dimming_rules (MODEST_WINDOW (window));
1038 modest_window_check_dimming_rules_group (MODEST_WINDOW (window), MODEST_DIMMING_RULES_CLIPBOARD);
1040 return MODEST_WINDOW(window);
1044 modest_msg_view_window_new_for_search_result (TnyMsg *msg,
1045 const gchar *modest_account_name,
1046 const gchar *msg_uid)
1048 ModestMsgViewWindow *window = NULL;
1049 ModestMsgViewWindowPrivate *priv = NULL;
1050 ModestWindowMgr *mgr = NULL;
1052 mgr = modest_runtime_get_window_mgr ();
1053 window = MODEST_MSG_VIEW_WINDOW (modest_window_mgr_get_msg_view_window (mgr));
1054 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), NULL);
1055 modest_msg_view_window_construct (window, modest_account_name, msg_uid);
1057 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1059 /* Remember that this is a search result,
1060 * so we can disable some UI appropriately: */
1061 priv->is_search_result = TRUE;
1063 tny_msg_view_set_msg (TNY_MSG_VIEW (priv->msg_view), msg);
1065 update_window_title (window);
1066 /* gtk_widget_show_all (GTK_WIDGET (window));*/
1067 modest_msg_view_window_update_priority (window);
1069 /* Check dimming rules */
1070 modest_ui_actions_check_toolbar_dimming_rules (MODEST_WINDOW (window));
1071 modest_ui_actions_check_menu_dimming_rules (MODEST_WINDOW (window));
1072 modest_window_check_dimming_rules_group (MODEST_WINDOW (window), MODEST_DIMMING_RULES_CLIPBOARD);
1074 return MODEST_WINDOW(window);
1078 modest_msg_view_window_new_for_attachment (TnyMsg *msg,
1079 const gchar *modest_account_name,
1080 const gchar *msg_uid)
1082 GObject *obj = NULL;
1083 ModestMsgViewWindowPrivate *priv;
1084 ModestWindowMgr *mgr = NULL;
1086 g_return_val_if_fail (msg, NULL);
1087 mgr = modest_runtime_get_window_mgr ();
1088 obj = G_OBJECT (modest_window_mgr_get_msg_view_window (mgr));
1089 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (obj);
1090 modest_msg_view_window_construct (MODEST_MSG_VIEW_WINDOW (obj),
1091 modest_account_name, msg_uid);
1093 tny_msg_view_set_msg (TNY_MSG_VIEW (priv->msg_view), msg);
1094 update_window_title (MODEST_MSG_VIEW_WINDOW (obj));
1096 /* gtk_widget_show_all (GTK_WIDGET (obj)); */
1098 /* Check dimming rules */
1099 modest_ui_actions_check_toolbar_dimming_rules (MODEST_WINDOW (obj));
1100 modest_ui_actions_check_menu_dimming_rules (MODEST_WINDOW (obj));
1101 modest_window_check_dimming_rules_group (MODEST_WINDOW (obj), MODEST_DIMMING_RULES_CLIPBOARD);
1103 return MODEST_WINDOW(obj);
1107 modest_msg_view_window_on_row_changed (GtkTreeModel *header_model,
1110 ModestMsgViewWindow *window)
1112 check_dimming_rules_after_change (window);
1116 modest_msg_view_window_on_row_deleted(GtkTreeModel *header_model,
1118 ModestMsgViewWindow *window)
1120 check_dimming_rules_after_change (window);
1122 /* The window could have dissapeared */
1125 check_dimming_rules_after_change (ModestMsgViewWindow *window)
1127 modest_ui_actions_check_menu_dimming_rules (MODEST_WINDOW (window));
1128 modest_ui_actions_check_toolbar_dimming_rules (MODEST_WINDOW (window));
1132 /* On insertions we check if the folder still has the message we are
1133 * showing or do not. If do not, we do nothing. Which means we are still
1134 * not attached to any header folder and thus next/prev buttons are
1135 * still dimmed. Once the message that is shown by msg-view is found, the
1136 * new model of header-view will be attached and the references will be set.
1137 * On each further insertions dimming rules will be checked. However
1138 * this requires extra CPU time at least works.
1139 * (An message might be deleted from TnyFolder and thus will not be
1140 * inserted into the model again for example if it is removed by the
1141 * imap server and the header view is refreshed.)
1144 modest_msg_view_window_on_row_inserted (GtkTreeModel *model,
1145 GtkTreePath *tree_path,
1146 GtkTreeIter *tree_iter,
1147 ModestMsgViewWindow *window)
1149 ModestMsgViewWindowPrivate *priv = NULL;
1150 TnyHeader *header = NULL;
1152 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window));
1153 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1155 g_assert (model == priv->header_model);
1157 /* Check if the newly inserted message is the same we are actually
1158 * showing. IF not, we should remain detached from the header model
1159 * and thus prev and next toolbar buttons should remain dimmed. */
1160 gtk_tree_model_get (model, tree_iter,
1161 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
1164 if (TNY_IS_HEADER (header)) {
1167 uid = modest_tny_folder_get_header_unique_id (header);
1168 if (!g_str_equal(priv->msg_uid, uid)) {
1169 check_dimming_rules_after_change (window);
1171 g_object_unref (G_OBJECT(header));
1175 g_object_unref(G_OBJECT(header));
1178 if (priv->row_reference) {
1179 gtk_tree_row_reference_free (priv->row_reference);
1182 /* Setup row_reference for the actual msg. */
1183 priv->row_reference = gtk_tree_row_reference_new (priv->header_model, tree_path);
1184 if (priv->row_reference == NULL) {
1185 g_warning("No reference for msg header item.");
1189 /* Now set up next_row_reference. */
1190 if (priv->next_row_reference) {
1191 gtk_tree_row_reference_free (priv->next_row_reference);
1194 priv->next_row_reference = gtk_tree_row_reference_copy (priv->row_reference);
1195 select_next_valid_row (priv->header_model,
1196 &(priv->next_row_reference), FALSE, priv->is_outbox);
1198 /* Connect the remaining callbacks to become able to detect
1199 * changes in header-view. */
1200 priv->row_changed_handler =
1201 g_signal_connect (priv->header_model, "row-changed",
1202 G_CALLBACK (modest_msg_view_window_on_row_changed),
1204 priv->row_deleted_handler =
1205 g_signal_connect (priv->header_model, "row-deleted",
1206 G_CALLBACK (modest_msg_view_window_on_row_deleted),
1208 priv->rows_reordered_handler =
1209 g_signal_connect (priv->header_model, "rows-reordered",
1210 G_CALLBACK (modest_msg_view_window_on_row_reordered),
1213 check_dimming_rules_after_change (window);
1217 modest_msg_view_window_on_row_reordered (GtkTreeModel *header_model,
1221 ModestMsgViewWindow *window)
1223 ModestMsgViewWindowPrivate *priv = NULL;
1224 gboolean already_changed = FALSE;
1226 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(window);
1228 /* If the current row was reordered select the proper next
1229 valid row. The same if the next row reference changes */
1230 if (priv->row_reference &&
1231 gtk_tree_row_reference_valid (priv->row_reference)) {
1233 path = gtk_tree_row_reference_get_path (priv->row_reference);
1234 if (gtk_tree_path_compare (path, arg1) == 0) {
1235 if (priv->next_row_reference) {
1236 gtk_tree_row_reference_free (priv->next_row_reference);
1238 priv->next_row_reference = gtk_tree_row_reference_copy (priv->row_reference);
1239 select_next_valid_row (header_model, &(priv->next_row_reference), FALSE, priv->is_outbox);
1240 already_changed = TRUE;
1242 gtk_tree_path_free (path);
1244 if (!already_changed &&
1245 priv->next_row_reference &&
1246 gtk_tree_row_reference_valid (priv->next_row_reference)) {
1248 path = gtk_tree_row_reference_get_path (priv->next_row_reference);
1249 if (gtk_tree_path_compare (path, arg1) == 0) {
1250 if (priv->next_row_reference) {
1251 gtk_tree_row_reference_free (priv->next_row_reference);
1253 priv->next_row_reference = gtk_tree_row_reference_copy (priv->row_reference);
1254 select_next_valid_row (header_model, &(priv->next_row_reference), FALSE, priv->is_outbox);
1256 gtk_tree_path_free (path);
1258 check_dimming_rules_after_change (window);
1261 /* The modest_msg_view_window_update_model_replaced implements update
1262 * function for ModestHeaderViewObserver. Checks whether the TnyFolder
1263 * actually belongs to the header-view is the same as the TnyFolder of
1264 * the message of msg-view or not. If they are different, there is
1265 * nothing to do. If they are the same, then the model has replaced and
1266 * the reference in msg-view shall be replaced from the old model to
1267 * the new model. In this case the view will be detached from it's
1268 * header folder. From this point the next/prev buttons are dimmed.
1271 modest_msg_view_window_update_model_replaced (ModestHeaderViewObserver *observer,
1272 GtkTreeModel *model,
1273 const gchar *tny_folder_id)
1275 ModestMsgViewWindowPrivate *priv = NULL;
1276 ModestMsgViewWindow *window = NULL;
1278 g_assert(MODEST_IS_HEADER_VIEW_OBSERVER(observer));
1279 g_assert(MODEST_IS_MSG_VIEW_WINDOW(observer));
1281 window = MODEST_MSG_VIEW_WINDOW(observer);
1282 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(window);
1284 /* If there is an other folder in the header-view then we do
1285 * not care about it's model (msg list). Else if the
1286 * header-view shows the folder the msg shown by us is in, we
1287 * shall replace our model reference and make some check. */
1288 if(model == NULL || tny_folder_id == NULL ||
1289 (priv->header_folder_id && !g_str_equal(tny_folder_id, priv->header_folder_id)))
1292 /* Model is changed(replaced), so we should forget the old
1293 * one. Because there might be other references and there
1294 * might be some change on the model even if we unreferenced
1295 * it, we need to disconnect our signals here. */
1296 if (priv->header_model) {
1297 if (g_signal_handler_is_connected(G_OBJECT (priv->header_model),
1298 priv->row_changed_handler))
1299 g_signal_handler_disconnect(G_OBJECT (priv->header_model),
1300 priv->row_changed_handler);
1301 if (g_signal_handler_is_connected(G_OBJECT (priv->header_model),
1302 priv->row_deleted_handler))
1303 g_signal_handler_disconnect(G_OBJECT (priv->header_model),
1304 priv->row_deleted_handler);
1305 if (g_signal_handler_is_connected(G_OBJECT (priv->header_model),
1306 priv->row_inserted_handler))
1307 g_signal_handler_disconnect(G_OBJECT (priv->header_model),
1308 priv->row_inserted_handler);
1309 if (g_signal_handler_is_connected(G_OBJECT (priv->header_model),
1310 priv->rows_reordered_handler))
1311 g_signal_handler_disconnect(G_OBJECT (priv->header_model),
1312 priv->rows_reordered_handler);
1315 if (priv->row_reference)
1316 gtk_tree_row_reference_free (priv->row_reference);
1317 if (priv->next_row_reference)
1318 gtk_tree_row_reference_free (priv->next_row_reference);
1319 g_object_unref(priv->header_model);
1322 priv->row_changed_handler = 0;
1323 priv->row_deleted_handler = 0;
1324 priv->row_inserted_handler = 0;
1325 priv->rows_reordered_handler = 0;
1326 priv->next_row_reference = NULL;
1327 priv->row_reference = NULL;
1328 priv->header_model = NULL;
1331 priv->header_model = g_object_ref (model);
1333 /* Also we must connect to the new model for row insertions.
1334 * Only for insertions now. We will need other ones only after
1335 * the msg is show by msg-view is added to the new model. */
1336 priv->row_inserted_handler =
1337 g_signal_connect (priv->header_model, "row-inserted",
1338 G_CALLBACK(modest_msg_view_window_on_row_inserted),
1341 modest_ui_actions_check_menu_dimming_rules(MODEST_WINDOW(window));
1342 modest_ui_actions_check_toolbar_dimming_rules(MODEST_WINDOW(window));
1346 modest_msg_view_window_toolbar_on_transfer_mode (ModestMsgViewWindow *self)
1348 ModestMsgViewWindowPrivate *priv= NULL;
1350 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self), FALSE);
1351 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
1353 return priv->progress_hint;
1357 modest_msg_view_window_get_header (ModestMsgViewWindow *self)
1359 ModestMsgViewWindowPrivate *priv= NULL;
1361 TnyHeader *header = NULL;
1362 GtkTreePath *path = NULL;
1365 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self), NULL);
1366 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
1368 /* If the message was not obtained from a treemodel,
1369 * for instance if it was opened directly by the search UI:
1371 if (priv->header_model == NULL ||
1372 priv->row_reference == NULL ||
1373 !gtk_tree_row_reference_valid (priv->row_reference)) {
1374 msg = modest_msg_view_window_get_message (self);
1376 header = tny_msg_get_header (msg);
1377 g_object_unref (msg);
1382 /* Get iter of the currently selected message in the header view: */
1383 path = gtk_tree_row_reference_get_path (priv->row_reference);
1384 g_return_val_if_fail (path != NULL, NULL);
1385 gtk_tree_model_get_iter (priv->header_model,
1389 /* Get current message header */
1390 gtk_tree_model_get (priv->header_model, &iter,
1391 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
1394 gtk_tree_path_free (path);
1399 modest_msg_view_window_get_message (ModestMsgViewWindow *self)
1401 ModestMsgViewWindowPrivate *priv;
1403 g_return_val_if_fail (self, NULL);
1405 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(self);
1407 return tny_msg_view_get_msg (TNY_MSG_VIEW (priv->msg_view));
1411 modest_msg_view_window_get_message_uid (ModestMsgViewWindow *self)
1413 ModestMsgViewWindowPrivate *priv;
1415 g_return_val_if_fail (self, NULL);
1417 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
1419 return (const gchar*) priv->msg_uid;
1423 modest_msg_view_window_toggle_find_toolbar (GtkToggleAction *toggle,
1426 ModestMsgViewWindow *window = MODEST_MSG_VIEW_WINDOW (data);
1427 ModestMsgViewWindowPrivate *priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1428 ModestWindowPrivate *parent_priv = MODEST_WINDOW_GET_PRIVATE (window);
1432 is_active = gtk_toggle_action_get_active (toggle);
1435 gtk_widget_show (priv->find_toolbar);
1436 hildon_find_toolbar_highlight_entry (HILDON_FIND_TOOLBAR (priv->find_toolbar), TRUE);
1438 gtk_widget_hide (priv->find_toolbar);
1439 modest_msg_view_grab_focus (MODEST_MSG_VIEW (priv->msg_view));
1442 /* update the toggle buttons status */
1443 action = gtk_ui_manager_get_action (parent_priv->ui_manager, "/ToolBar/FindInMessage");
1445 modest_utils_toggle_action_set_active_block_notify (GTK_TOGGLE_ACTION (action), is_active);
1450 modest_msg_view_window_find_toolbar_close (GtkWidget *widget,
1451 ModestMsgViewWindow *obj)
1453 GtkToggleAction *toggle;
1454 ModestWindowPrivate *parent_priv;
1455 ModestMsgViewWindowPrivate *priv;
1457 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (obj);
1458 parent_priv = MODEST_WINDOW_GET_PRIVATE (obj);
1460 toggle = GTK_TOGGLE_ACTION (gtk_ui_manager_get_action (parent_priv->ui_manager, "/ToolBar/FindInMessage"));
1461 gtk_toggle_action_set_active (toggle, FALSE);
1462 modest_msg_view_grab_focus (MODEST_MSG_VIEW (priv->msg_view));
1466 modest_msg_view_window_find_toolbar_search (GtkWidget *widget,
1467 ModestMsgViewWindow *obj)
1469 gchar *current_search;
1470 ModestMsgViewWindowPrivate *priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (obj);
1472 if (modest_mime_part_view_is_empty (MODEST_MIME_PART_VIEW (priv->msg_view))) {
1473 hildon_banner_show_information (NULL, NULL, _("mail_ib_nothing_to_find"));
1477 g_object_get (G_OBJECT (widget), "prefix", ¤t_search, NULL);
1479 if ((current_search == NULL) || (strcmp (current_search, "") == 0)) {
1480 g_free (current_search);
1481 hildon_banner_show_information (NULL, NULL, dgettext("hildon-common-strings", "ecdg_ib_find_rep_enter_text"));
1485 if ((priv->last_search == NULL) || (strcmp (priv->last_search, current_search) != 0)) {
1487 g_free (priv->last_search);
1488 priv->last_search = g_strdup (current_search);
1489 result = modest_isearch_view_search (MODEST_ISEARCH_VIEW (priv->msg_view),
1492 hildon_banner_show_information (NULL, NULL, dgettext("hildon-libs", "ckct_ib_find_no_matches"));
1493 g_free (priv->last_search);
1494 priv->last_search = NULL;
1496 modest_msg_view_grab_focus (MODEST_MSG_VIEW (priv->msg_view));
1497 hildon_find_toolbar_highlight_entry (HILDON_FIND_TOOLBAR (priv->find_toolbar), TRUE);
1500 if (!modest_isearch_view_search_next (MODEST_ISEARCH_VIEW (priv->msg_view))) {
1501 hildon_banner_show_information (NULL, NULL, dgettext("hildon-libs", "ckct_ib_find_search_complete"));
1502 g_free (priv->last_search);
1503 priv->last_search = NULL;
1505 modest_msg_view_grab_focus (MODEST_MSG_VIEW (priv->msg_view));
1506 hildon_find_toolbar_highlight_entry (HILDON_FIND_TOOLBAR (priv->find_toolbar), TRUE);
1510 g_free (current_search);
1515 modest_msg_view_window_set_zoom (ModestWindow *window,
1518 ModestMsgViewWindowPrivate *priv;
1519 ModestWindowPrivate *parent_priv;
1521 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window));
1523 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1524 parent_priv = MODEST_WINDOW_GET_PRIVATE (window);
1525 modest_zoomable_set_zoom (MODEST_ZOOMABLE (priv->msg_view), zoom);
1530 modest_msg_view_window_get_zoom (ModestWindow *window)
1532 ModestMsgViewWindowPrivate *priv;
1534 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), 1.0);
1536 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1537 return modest_zoomable_get_zoom (MODEST_ZOOMABLE (priv->msg_view));
1541 modest_msg_view_window_zoom_plus (ModestWindow *window)
1544 ModestMsgViewWindowPrivate *priv;
1546 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), 1.0);
1547 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1549 zoom_level = modest_zoomable_get_zoom (MODEST_ZOOMABLE (priv->msg_view));
1551 if (zoom_level >= 2.0) {
1552 hildon_banner_show_information (NULL, NULL, dgettext("hildon-common-strings", "ckct_ib_max_zoom_level_reached"));
1554 } else if (zoom_level >= 1.5) {
1556 } else if (zoom_level >= 1.2) {
1558 } else if (zoom_level >= 1.0) {
1560 } else if (zoom_level >= 0.8) {
1562 } else if (zoom_level >= 0.5) {
1568 /* set zoom level */
1569 modest_zoomable_set_zoom (MODEST_ZOOMABLE (priv->msg_view), zoom_level);
1576 modest_msg_view_window_zoom_minus (ModestWindow *window)
1579 ModestMsgViewWindowPrivate *priv;
1581 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), 1.0);
1582 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1584 zoom_level = modest_zoomable_get_zoom (MODEST_ZOOMABLE (priv->msg_view));
1586 if (zoom_level <= 0.5) {
1587 hildon_banner_show_information (NULL, NULL, dgettext("hildon-common-strings", "ckct_ib_min_zoom_level_reached"));
1589 } else if (zoom_level <= 0.8) {
1591 } else if (zoom_level <= 1.0) {
1593 } else if (zoom_level <= 1.2) {
1595 } else if (zoom_level <= 1.5) {
1597 } else if (zoom_level <= 2.0) {
1603 /* set zoom level */
1604 modest_zoomable_set_zoom (MODEST_ZOOMABLE (priv->msg_view), zoom_level);
1611 modest_msg_view_window_key_event (GtkWidget *window,
1617 focus = gtk_window_get_focus (GTK_WINDOW (window));
1619 /* for the find toolbar case */
1620 if (focus && GTK_IS_ENTRY (focus)) {
1621 if (event->keyval == GDK_BackSpace) {
1623 copy = gdk_event_copy ((GdkEvent *) event);
1624 gtk_widget_event (focus, copy);
1625 gdk_event_free (copy);
1630 if (event->keyval == GDK_Up || event->keyval == GDK_KP_Up ||
1631 event->keyval == GDK_Down || event->keyval == GDK_KP_Down ||
1632 event->keyval == GDK_Page_Up || event->keyval == GDK_KP_Page_Up ||
1633 event->keyval == GDK_Page_Down || event->keyval == GDK_KP_Page_Down ||
1634 event->keyval == GDK_Home || event->keyval == GDK_KP_Home ||
1635 event->keyval == GDK_End || event->keyval == GDK_KP_End) {
1636 /* ModestMsgViewWindowPrivate *priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window); */
1637 /* gboolean return_value; */
1639 if (event->type == GDK_KEY_PRESS) {
1640 GtkScrollType scroll_type;
1642 switch (event->keyval) {
1645 scroll_type = GTK_SCROLL_STEP_UP; break;
1648 scroll_type = GTK_SCROLL_STEP_DOWN; break;
1650 case GDK_KP_Page_Up:
1651 scroll_type = GTK_SCROLL_PAGE_UP; break;
1653 case GDK_KP_Page_Down:
1654 scroll_type = GTK_SCROLL_PAGE_DOWN; break;
1657 scroll_type = GTK_SCROLL_START; break;
1660 scroll_type = GTK_SCROLL_END; break;
1661 default: scroll_type = GTK_SCROLL_NONE;
1664 /* g_signal_emit_by_name (G_OBJECT (priv->main_scroll), "scroll-child", */
1665 /* scroll_type, FALSE, &return_value); */
1676 modest_msg_view_window_last_message_selected (ModestMsgViewWindow *window)
1679 ModestMsgViewWindowPrivate *priv;
1680 GtkTreeIter tmp_iter;
1681 gboolean is_last_selected;
1683 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), TRUE);
1684 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1686 /*if no model (so no rows at all), then virtually we are the last*/
1687 if (!priv->header_model || !priv->row_reference)
1690 if (!gtk_tree_row_reference_valid (priv->row_reference))
1693 path = gtk_tree_row_reference_get_path (priv->row_reference);
1697 is_last_selected = TRUE;
1698 while (is_last_selected) {
1700 gtk_tree_path_next (path);
1701 if (!gtk_tree_model_get_iter (priv->header_model, &tmp_iter, path))
1703 gtk_tree_model_get (priv->header_model, &tmp_iter,
1704 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
1707 if (msg_is_visible (header, priv->is_outbox))
1708 is_last_selected = FALSE;
1709 g_object_unref(G_OBJECT(header));
1712 gtk_tree_path_free (path);
1713 return is_last_selected;
1717 modest_msg_view_window_has_headers_model (ModestMsgViewWindow *window)
1719 ModestMsgViewWindowPrivate *priv;
1721 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), TRUE);
1722 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1724 return priv->header_model != NULL;
1728 modest_msg_view_window_is_search_result (ModestMsgViewWindow *window)
1730 ModestMsgViewWindowPrivate *priv;
1732 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), TRUE);
1733 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1735 return priv->is_search_result;
1739 msg_is_visible (TnyHeader *header, gboolean check_outbox)
1741 if ((tny_header_get_flags(header) & TNY_HEADER_FLAG_DELETED))
1743 if (!check_outbox) {
1746 ModestTnySendQueueStatus status;
1747 status = modest_tny_all_send_queues_get_msg_status (header);
1748 return ((status != MODEST_TNY_SEND_QUEUE_FAILED) &&
1749 (status != MODEST_TNY_SEND_QUEUE_SENDING));
1754 modest_msg_view_window_first_message_selected (ModestMsgViewWindow *window)
1757 ModestMsgViewWindowPrivate *priv;
1758 gboolean is_first_selected;
1759 GtkTreeIter tmp_iter;
1761 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), TRUE);
1762 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1764 /*if no model (so no rows at all), then virtually we are the first*/
1765 if (!priv->header_model || !priv->row_reference)
1768 if (!gtk_tree_row_reference_valid (priv->row_reference))
1771 path = gtk_tree_row_reference_get_path (priv->row_reference);
1775 is_first_selected = TRUE;
1776 while (is_first_selected) {
1778 if(!gtk_tree_path_prev (path))
1780 /* Here the 'if' is needless for logic, but let make sure
1781 * iter is valid for gtk_tree_model_get. */
1782 if (!gtk_tree_model_get_iter (priv->header_model, &tmp_iter, path))
1784 gtk_tree_model_get (priv->header_model, &tmp_iter,
1785 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
1788 if (msg_is_visible (header, priv->is_outbox))
1789 is_first_selected = FALSE;
1790 g_object_unref(G_OBJECT(header));
1793 gtk_tree_path_free (path);
1794 return is_first_selected;
1799 GtkTreeRowReference *row_reference;
1803 message_reader_performer (gboolean canceled,
1805 GtkWindow *parent_window,
1806 TnyAccount *account,
1809 ModestMailOperation *mail_op = NULL;
1810 MsgReaderInfo *info;
1812 info = (MsgReaderInfo *) user_data;
1813 if (canceled || err) {
1817 /* Register the header - it'll be unregistered in the callback */
1818 modest_window_mgr_register_header (modest_runtime_get_window_mgr (), info->header, NULL);
1820 /* New mail operation */
1821 mail_op = modest_mail_operation_new_with_error_handling (G_OBJECT(parent_window),
1822 modest_ui_actions_disk_operations_error_handler,
1825 modest_mail_operation_queue_add (modest_runtime_get_mail_operation_queue (), mail_op);
1826 modest_mail_operation_get_msg (mail_op, info->header, TRUE, view_msg_cb, info->row_reference);
1827 g_object_unref (mail_op);
1829 /* Update dimming rules */
1830 modest_ui_actions_check_toolbar_dimming_rules (MODEST_WINDOW (parent_window));
1831 modest_ui_actions_check_menu_dimming_rules (MODEST_WINDOW (parent_window));
1834 /* Frees. The row_reference will be freed by the view_msg_cb callback */
1835 g_object_unref (info->header);
1836 g_slice_free (MsgReaderInfo, info);
1841 * Reads the message whose summary item is @header. It takes care of
1842 * several things, among others:
1844 * If the message was not previously downloaded then ask the user
1845 * before downloading. If there is no connection launch the connection
1846 * dialog. Update toolbar dimming rules.
1848 * Returns: TRUE if the mail operation was started, otherwise if the
1849 * user do not want to download the message, or if the user do not
1850 * want to connect, then the operation is not issued
1853 message_reader (ModestMsgViewWindow *window,
1854 ModestMsgViewWindowPrivate *priv,
1856 GtkTreeRowReference *row_reference)
1858 ModestWindowMgr *mgr;
1859 TnyAccount *account;
1861 MsgReaderInfo *info;
1863 g_return_val_if_fail (row_reference != NULL, FALSE);
1865 mgr = modest_runtime_get_window_mgr ();
1866 /* Msg download completed */
1867 if (!(tny_header_get_flags (header) & TNY_HEADER_FLAG_CACHED)) {
1868 /* Ask the user if he wants to download the message if
1870 if (!tny_device_is_online (modest_runtime_get_device())) {
1871 GtkResponseType response;
1873 response = modest_platform_run_confirmation_dialog (GTK_WINDOW (window),
1874 _("mcen_nc_get_msg"));
1875 if (response == GTK_RESPONSE_CANCEL)
1878 folder = tny_header_get_folder (header);
1879 info = g_slice_new (MsgReaderInfo);
1880 info->header = g_object_ref (header);
1881 info->row_reference = gtk_tree_row_reference_copy (row_reference);
1883 /* Offer the connection dialog if necessary */
1884 modest_platform_connect_if_remote_and_perform ((GtkWindow *) window,
1886 TNY_FOLDER_STORE (folder),
1887 message_reader_performer,
1889 g_object_unref (folder);
1894 folder = tny_header_get_folder (header);
1895 account = tny_folder_get_account (folder);
1896 info = g_slice_new (MsgReaderInfo);
1897 info->header = g_object_ref (header);
1898 info->row_reference = gtk_tree_row_reference_copy (row_reference);
1900 message_reader_performer (FALSE, NULL, (GtkWindow *) window, account, info);
1901 g_object_unref (account);
1902 g_object_unref (folder);
1908 modest_msg_view_window_select_next_message (ModestMsgViewWindow *window)
1910 ModestMsgViewWindowPrivate *priv;
1911 GtkTreePath *path= NULL;
1912 GtkTreeIter tmp_iter;
1914 gboolean retval = TRUE;
1915 GtkTreeRowReference *row_reference = NULL;
1917 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), FALSE);
1918 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1920 if (!priv->row_reference)
1923 /* Update the next row reference if it's not valid. This could
1924 happen if for example the header which it was pointing to,
1925 was deleted. The best place to do it is in the row-deleted
1926 handler but the tinymail model do not work like the glib
1927 tree models and reports the deletion when the row is still
1929 if (!gtk_tree_row_reference_valid (priv->next_row_reference)) {
1930 if (gtk_tree_row_reference_valid (priv->row_reference)) {
1931 priv->next_row_reference = gtk_tree_row_reference_copy (priv->row_reference);
1932 select_next_valid_row (priv->header_model, &(priv->next_row_reference), FALSE, priv->is_outbox);
1935 if (priv->next_row_reference)
1936 path = gtk_tree_row_reference_get_path (priv->next_row_reference);
1940 row_reference = gtk_tree_row_reference_copy (priv->next_row_reference);
1942 gtk_tree_model_get_iter (priv->header_model,
1945 gtk_tree_path_free (path);
1947 gtk_tree_model_get (priv->header_model, &tmp_iter,
1948 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
1951 /* Read the message & show it */
1952 if (!message_reader (window, priv, header, row_reference)) {
1955 gtk_tree_row_reference_free (row_reference);
1958 g_object_unref (header);
1964 modest_msg_view_window_select_previous_message (ModestMsgViewWindow *window)
1966 ModestMsgViewWindowPrivate *priv = NULL;
1968 gboolean finished = FALSE;
1969 gboolean retval = FALSE;
1971 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), FALSE);
1972 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1974 /* Return inmediatly if there is no header model */
1975 if (!priv->header_model || !priv->row_reference)
1978 path = gtk_tree_row_reference_get_path (priv->row_reference);
1979 while (!finished && gtk_tree_path_prev (path)) {
1983 gtk_tree_model_get_iter (priv->header_model, &iter, path);
1984 gtk_tree_model_get (priv->header_model, &iter,
1985 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
1989 if (msg_is_visible (header, priv->is_outbox)) {
1990 GtkTreeRowReference *row_reference;
1991 row_reference = gtk_tree_row_reference_new (priv->header_model, path);
1992 /* Read the message & show it */
1993 retval = message_reader (window, priv, header, row_reference);
1994 gtk_tree_row_reference_free (row_reference);
1998 g_object_unref (header);
2002 gtk_tree_path_free (path);
2007 view_msg_cb (ModestMailOperation *mail_op,
2014 ModestMsgViewWindow *self = NULL;
2015 ModestMsgViewWindowPrivate *priv = NULL;
2016 GtkTreeRowReference *row_reference = NULL;
2018 /* Unregister the header (it was registered before creating the mail operation) */
2019 modest_window_mgr_unregister_header (modest_runtime_get_window_mgr (), header);
2021 row_reference = (GtkTreeRowReference *) user_data;
2023 gtk_tree_row_reference_free (row_reference);
2027 /* If there was any error */
2028 if (!modest_ui_actions_msg_retrieval_check (mail_op, header, msg)) {
2029 gtk_tree_row_reference_free (row_reference);
2033 /* Get the window */
2034 self = (ModestMsgViewWindow *) modest_mail_operation_get_source (mail_op);
2035 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self));
2036 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
2038 /* Update the row reference */
2039 if (priv->row_reference != NULL) {
2040 gtk_tree_row_reference_free (priv->row_reference);
2041 priv->row_reference = gtk_tree_row_reference_copy (row_reference);
2042 if (priv->next_row_reference != NULL) {
2043 gtk_tree_row_reference_free (priv->next_row_reference);
2045 priv->next_row_reference = gtk_tree_row_reference_copy (priv->row_reference);
2046 select_next_valid_row (priv->header_model, &(priv->next_row_reference), TRUE, priv->is_outbox);
2049 /* Mark header as read */
2050 if (!(tny_header_get_flags (header) & TNY_HEADER_FLAG_SEEN))
2051 tny_header_set_flag (header, TNY_HEADER_FLAG_SEEN);
2053 /* Set new message */
2054 if (priv->msg_view != NULL && TNY_IS_MSG_VIEW (priv->msg_view)) {
2055 tny_msg_view_set_msg (TNY_MSG_VIEW (priv->msg_view), msg);
2056 modest_msg_view_window_update_priority (self);
2057 update_window_title (MODEST_MSG_VIEW_WINDOW (self));
2058 modest_msg_view_grab_focus (MODEST_MSG_VIEW (priv->msg_view));
2061 /* Set the new message uid of the window */
2062 if (priv->msg_uid) {
2063 g_free (priv->msg_uid);
2064 priv->msg_uid = modest_tny_folder_get_header_unique_id (header);
2067 /* Notify the observers */
2068 g_signal_emit (G_OBJECT (self), signals[MSG_CHANGED_SIGNAL],
2069 0, priv->header_model, priv->row_reference);
2072 g_object_unref (self);
2073 gtk_tree_row_reference_free (row_reference);
2077 modest_msg_view_window_get_folder_type (ModestMsgViewWindow *window)
2079 ModestMsgViewWindowPrivate *priv;
2081 TnyFolderType folder_type;
2083 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
2085 folder_type = TNY_FOLDER_TYPE_UNKNOWN;
2087 msg = tny_msg_view_get_msg (TNY_MSG_VIEW (priv->msg_view));
2091 folder = tny_msg_get_folder (msg);
2093 folder_type = modest_tny_folder_guess_folder_type (folder);
2094 g_object_unref (folder);
2096 g_object_unref (msg);
2104 modest_msg_view_window_update_priority (ModestMsgViewWindow *window)
2106 ModestMsgViewWindowPrivate *priv;
2107 TnyHeader *header = NULL;
2108 TnyHeaderFlags flags = 0;
2110 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
2112 if (priv->header_model && priv->row_reference) {
2114 GtkTreePath *path = NULL;
2116 path = gtk_tree_row_reference_get_path (priv->row_reference);
2117 g_return_if_fail (path != NULL);
2118 gtk_tree_model_get_iter (priv->header_model,
2120 gtk_tree_row_reference_get_path (priv->row_reference));
2122 gtk_tree_model_get (priv->header_model, &iter, TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
2124 gtk_tree_path_free (path);
2127 msg = tny_msg_view_get_msg (TNY_MSG_VIEW (priv->msg_view));
2129 header = tny_msg_get_header (msg);
2130 g_object_unref (msg);
2135 flags = tny_header_get_flags (header);
2136 g_object_unref(G_OBJECT(header));
2139 modest_msg_view_set_priority (MODEST_MSG_VIEW(priv->msg_view), flags);
2144 toolbar_resize (ModestMsgViewWindow *self)
2146 ModestMsgViewWindowPrivate *priv = NULL;
2147 ModestWindowPrivate *parent_priv = NULL;
2149 gint static_button_size;
2150 ModestWindowMgr *mgr;
2152 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self));
2153 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
2154 parent_priv = MODEST_WINDOW_GET_PRIVATE(self);
2156 mgr = modest_runtime_get_window_mgr ();
2157 static_button_size = modest_window_mgr_get_fullscreen_mode (mgr)?118:108;
2159 if (parent_priv->toolbar) {
2160 /* left size buttons */
2161 widget = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/ToolbarMessageReply");
2162 gtk_tool_item_set_expand (GTK_TOOL_ITEM (widget), FALSE);
2163 gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (widget), FALSE);
2164 gtk_widget_set_size_request (GTK_WIDGET (widget), static_button_size, -1);
2165 widget = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/ToolbarMessageMoveTo");
2166 gtk_tool_item_set_expand (GTK_TOOL_ITEM (widget), FALSE);
2167 gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (widget), FALSE);
2168 gtk_widget_set_size_request (GTK_WIDGET (widget), static_button_size, -1);
2169 widget = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/ToolbarDeleteMessage");
2170 gtk_tool_item_set_expand (GTK_TOOL_ITEM (widget), FALSE);
2171 gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (widget), FALSE);
2172 gtk_widget_set_size_request (GTK_WIDGET (widget), static_button_size, -1);
2173 widget = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/FindInMessage");
2174 gtk_tool_item_set_expand (GTK_TOOL_ITEM (widget), FALSE);
2175 gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (widget), FALSE);
2176 gtk_widget_set_size_request (GTK_WIDGET (widget), static_button_size, -1);
2178 gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (priv->next_toolitem), TRUE);
2179 gtk_tool_item_set_expand (GTK_TOOL_ITEM (priv->next_toolitem), TRUE);
2180 gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (priv->prev_toolitem), TRUE);
2181 gtk_tool_item_set_expand (GTK_TOOL_ITEM (priv->prev_toolitem), TRUE);
2188 modest_msg_view_window_show_toolbar (ModestWindow *self,
2189 gboolean show_toolbar)
2191 ModestMsgViewWindowPrivate *priv = NULL;
2192 ModestWindowPrivate *parent_priv;
2193 GtkWidget *reply_button = NULL, *menu = NULL;
2195 parent_priv = MODEST_WINDOW_GET_PRIVATE(self);
2196 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(self);
2198 /* Set optimized view status */
2199 priv->optimized_view = !show_toolbar;
2201 if (!parent_priv->toolbar) {
2202 parent_priv->toolbar = gtk_ui_manager_get_widget (parent_priv->ui_manager,
2204 gtk_widget_set_no_show_all (parent_priv->toolbar, TRUE);
2206 priv->next_toolitem = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/ToolbarMessageNext");
2207 priv->prev_toolitem = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/ToolbarMessageBack");
2208 toolbar_resize (MODEST_MSG_VIEW_WINDOW (self));
2211 hildon_window_add_toolbar (HILDON_WINDOW (self),
2212 GTK_TOOLBAR (parent_priv->toolbar));
2214 /* Set reply button tap and hold menu */
2215 reply_button = gtk_ui_manager_get_widget (parent_priv->ui_manager,
2216 "/ToolBar/ToolbarMessageReply");
2217 menu = gtk_ui_manager_get_widget (parent_priv->ui_manager,
2218 "/ToolbarReplyCSM");
2219 if (menu && reply_button)
2220 gtk_widget_tap_and_hold_setup (GTK_WIDGET (reply_button), menu, NULL, 0);
2224 /* Quick hack: this prevents toolbar icons "dance" when progress bar show status is changed */
2225 /* TODO: resize mode migth be GTK_RESIZE_QUEUE, in order to avoid unneccesary shows */
2226 gtk_container_set_resize_mode (GTK_CONTAINER(parent_priv->toolbar), GTK_RESIZE_IMMEDIATE);
2228 gtk_widget_show (GTK_WIDGET (parent_priv->toolbar));
2229 if (modest_msg_view_window_transfer_mode_enabled (MODEST_MSG_VIEW_WINDOW (self)))
2230 set_progress_hint (MODEST_MSG_VIEW_WINDOW (self), TRUE);
2232 set_progress_hint (MODEST_MSG_VIEW_WINDOW (self), FALSE);
2235 gtk_widget_set_no_show_all (parent_priv->toolbar, TRUE);
2236 gtk_widget_hide (GTK_WIDGET (parent_priv->toolbar));
2242 modest_msg_view_window_clipboard_owner_change (GtkClipboard *clipboard,
2244 ModestMsgViewWindow *window)
2246 if (!GTK_WIDGET_VISIBLE (window))
2249 modest_window_check_dimming_rules_group (MODEST_WINDOW (window), MODEST_DIMMING_RULES_CLIPBOARD);
2253 modest_msg_view_window_transfer_mode_enabled (ModestMsgViewWindow *self)
2255 ModestMsgViewWindowPrivate *priv;
2257 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self), FALSE);
2258 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(self);
2260 return priv->progress_hint;
2264 observers_empty (ModestMsgViewWindow *self)
2267 ModestMsgViewWindowPrivate *priv;
2268 gboolean is_empty = TRUE;
2269 guint pending_ops = 0;
2271 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(self);
2272 tmp = priv->progress_widgets;
2274 /* Check all observers */
2275 while (tmp && is_empty) {
2276 pending_ops = modest_progress_object_num_pending_operations (MODEST_PROGRESS_OBJECT(tmp->data));
2277 is_empty = pending_ops == 0;
2279 tmp = g_slist_next(tmp);
2286 on_account_removed (TnyAccountStore *account_store,
2287 TnyAccount *account,
2290 /* Do nothing if it's a transport account, because we only
2291 show the messages of a store account */
2292 if (tny_account_get_account_type(account) == TNY_ACCOUNT_TYPE_STORE) {
2293 const gchar *parent_acc = NULL;
2294 const gchar *our_acc = NULL;
2296 our_acc = modest_window_get_active_account (MODEST_WINDOW (user_data));
2297 parent_acc = modest_tny_account_get_parent_modest_account_name_for_server_account (account);
2299 /* Close this window if I'm showing a message of the removed account */
2300 if (strcmp (parent_acc, our_acc) == 0)
2301 modest_ui_actions_on_close_window (NULL, MODEST_WINDOW (user_data));
2306 on_mail_operation_started (ModestMailOperation *mail_op,
2309 ModestMsgViewWindow *self;
2310 ModestMailOperationTypeOperation op_type;
2312 ModestMsgViewWindowPrivate *priv;
2313 GObject *source = NULL;
2315 self = MODEST_MSG_VIEW_WINDOW (user_data);
2316 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
2317 op_type = modest_mail_operation_get_type_operation (mail_op);
2318 tmp = priv->progress_widgets;
2319 source = modest_mail_operation_get_source(mail_op);
2320 if (G_OBJECT (self) == source) {
2321 if (op_type == MODEST_MAIL_OPERATION_TYPE_RECEIVE ) {
2322 set_toolbar_transfer_mode(self);
2324 modest_progress_object_add_operation (
2325 MODEST_PROGRESS_OBJECT (tmp->data),
2327 tmp = g_slist_next (tmp);
2331 g_object_unref (source);
2335 on_mail_operation_finished (ModestMailOperation *mail_op,
2338 ModestMsgViewWindow *self;
2339 ModestMailOperationTypeOperation op_type;
2341 ModestMsgViewWindowPrivate *priv;
2343 self = MODEST_MSG_VIEW_WINDOW (user_data);
2344 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
2345 op_type = modest_mail_operation_get_type_operation (mail_op);
2346 tmp = priv->progress_widgets;
2348 if (op_type == MODEST_MAIL_OPERATION_TYPE_RECEIVE ) {
2350 modest_progress_object_remove_operation (MODEST_PROGRESS_OBJECT (tmp->data),
2352 tmp = g_slist_next (tmp);
2355 /* If no more operations are being observed, NORMAL mode is enabled again */
2356 if (observers_empty (self)) {
2357 set_progress_hint (self, FALSE);
2360 /* Update dimming rules. We have to do this right here
2361 and not in view_msg_cb because at that point the
2362 transfer mode is still enabled so the dimming rule
2363 won't let the user delete the message that has been
2364 readed for example */
2365 modest_ui_actions_check_toolbar_dimming_rules (MODEST_WINDOW (self));
2366 modest_ui_actions_check_menu_dimming_rules (MODEST_WINDOW (self));
2371 on_queue_changed (ModestMailOperationQueue *queue,
2372 ModestMailOperation *mail_op,
2373 ModestMailOperationQueueNotification type,
2374 ModestMsgViewWindow *self)
2376 ModestMsgViewWindowPrivate *priv;
2378 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
2380 /* If this operations was created by another window, do nothing */
2381 if (!modest_mail_operation_is_mine (mail_op, G_OBJECT(self)))
2384 if (type == MODEST_MAIL_OPERATION_QUEUE_OPERATION_ADDED) {
2385 priv->sighandlers = modest_signal_mgr_connect (priv->sighandlers,
2387 "operation-started",
2388 G_CALLBACK (on_mail_operation_started),
2390 priv->sighandlers = modest_signal_mgr_connect (priv->sighandlers,
2392 "operation-finished",
2393 G_CALLBACK (on_mail_operation_finished),
2395 } else if (type == MODEST_MAIL_OPERATION_QUEUE_OPERATION_REMOVED) {
2396 priv->sighandlers = modest_signal_mgr_disconnect (priv->sighandlers,
2398 "operation-started");
2399 priv->sighandlers = modest_signal_mgr_disconnect (priv->sighandlers,
2401 "operation-finished");
2406 modest_msg_view_window_get_attachments (ModestMsgViewWindow *win)
2408 ModestMsgViewWindowPrivate *priv;
2409 TnyList *selected_attachments = NULL;
2411 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (win), NULL);
2412 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (win);
2414 selected_attachments = modest_msg_view_get_selected_attachments (MODEST_MSG_VIEW (priv->msg_view));
2416 return selected_attachments;
2422 guint banner_idle_id;
2423 } DecodeAsyncHelper;
2426 decode_async_banner_idle (gpointer user_data)
2428 DecodeAsyncHelper *helper = (DecodeAsyncHelper *) user_data;
2430 helper->banner_idle_id = 0;
2431 helper->banner = hildon_banner_show_animation (NULL, NULL, _("mail_me_opening"));
2432 g_object_ref (helper->banner);
2438 on_decode_to_stream_async_handler (TnyMimePart *mime_part,
2444 DecodeAsyncHelper *helper = (DecodeAsyncHelper *) user_data;
2446 if (helper->banner_idle_id > 0) {
2447 g_source_remove (helper->banner_idle_id);
2448 helper->banner_idle_id = 0;
2450 if (helper->banner) {
2451 gtk_widget_destroy (helper->banner);
2453 if (cancelled || err) {
2454 modest_platform_information_banner (NULL, NULL,
2455 _("mail_ib_file_operation_failed"));
2459 /* make the file read-only */
2460 g_chmod(helper->filepath, 0444);
2462 /* Activate the file */
2463 modest_platform_activate_file (helper->filepath, tny_mime_part_get_content_type (mime_part));
2467 g_free (helper->filepath);
2468 g_object_unref (helper->banner);
2469 g_slice_free (DecodeAsyncHelper, helper);
2473 modest_msg_view_window_view_attachment (ModestMsgViewWindow *window,
2474 TnyMimePart *mime_part)
2476 ModestMsgViewWindowPrivate *priv;
2477 const gchar *msg_uid;
2478 gchar *attachment_uid = NULL;
2479 gint attachment_index = 0;
2480 TnyList *attachments;
2482 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window));
2483 g_return_if_fail (TNY_IS_MIME_PART (mime_part) || (mime_part == NULL));
2484 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
2486 msg_uid = modest_msg_view_window_get_message_uid (MODEST_MSG_VIEW_WINDOW (window));
2487 attachments = modest_msg_view_get_attachments (MODEST_MSG_VIEW (priv->msg_view));
2488 attachment_index = modest_list_index (attachments, (GObject *) mime_part);
2489 g_object_unref (attachments);
2491 if (msg_uid && attachment_index >= 0) {
2492 attachment_uid = g_strdup_printf ("%s/%d", msg_uid, attachment_index);
2495 if (mime_part == NULL) {
2496 gboolean error = FALSE;
2497 TnyList *selected_attachments = modest_msg_view_get_selected_attachments (MODEST_MSG_VIEW (priv->msg_view));
2498 if (selected_attachments == NULL || tny_list_get_length (selected_attachments) == 0) {
2500 } else if (tny_list_get_length (selected_attachments) > 1) {
2501 hildon_banner_show_information (NULL, NULL, _("mcen_ib_unable_to_display_more"));
2505 iter = tny_list_create_iterator (selected_attachments);
2506 mime_part = (TnyMimePart *) tny_iterator_get_current (iter);
2507 g_object_unref (iter);
2509 g_object_unref (selected_attachments);
2514 g_object_ref (mime_part);
2517 if (tny_mime_part_is_purged (mime_part)) {
2518 g_object_unref (mime_part);
2522 if (!modest_tny_mime_part_is_msg (mime_part)) {
2523 gchar *filepath = NULL;
2524 const gchar *att_filename = tny_mime_part_get_filename (mime_part);
2525 gboolean show_error_banner = FALSE;
2526 TnyFsStream *temp_stream = NULL;
2527 temp_stream = modest_utils_create_temp_stream (att_filename, attachment_uid,
2530 if (temp_stream != NULL) {
2531 DecodeAsyncHelper *helper = g_slice_new (DecodeAsyncHelper);
2532 helper->filepath = g_strdup (filepath);
2533 helper->banner = NULL;
2534 helper->banner_idle_id = g_timeout_add (1000, decode_async_banner_idle, helper);
2535 tny_mime_part_decode_to_stream_async (mime_part, TNY_STREAM (temp_stream),
2536 on_decode_to_stream_async_handler,
2539 g_object_unref (temp_stream);
2540 /* NOTE: files in the temporary area will be automatically
2541 * cleaned after some time if they are no longer in use */
2544 const gchar *content_type;
2545 /* the file may already exist but it isn't writable,
2546 * let's try to open it anyway */
2547 content_type = tny_mime_part_get_content_type (mime_part);
2548 modest_platform_activate_file (filepath, content_type);
2550 g_warning ("%s: modest_utils_create_temp_stream failed", __FUNCTION__);
2551 show_error_banner = TRUE;
2556 if (show_error_banner)
2557 modest_platform_information_banner (NULL, NULL, _("mail_ib_file_operation_failed"));
2559 /* message attachment */
2560 TnyHeader *header = NULL;
2561 ModestWindowMgr *mgr;
2562 ModestWindow *msg_win = NULL;
2565 header = tny_msg_get_header (TNY_MSG (mime_part));
2566 mgr = modest_runtime_get_window_mgr ();
2567 found = modest_window_mgr_find_registered_header (mgr, header, &msg_win);
2570 /* if it's found, but there is no msg_win, it's probably in the process of being created;
2571 * thus, we don't do anything */
2572 g_warning ("window for is already being created");
2574 /* it's not found, so create a new window for it */
2575 modest_window_mgr_register_header (mgr, header, attachment_uid); /* register the uid before building the window */
2576 gchar *account = g_strdup (modest_window_get_active_account (MODEST_WINDOW (window)));
2578 account = modest_account_mgr_get_default_account (modest_runtime_get_account_mgr ());
2579 msg_win = modest_msg_view_window_new_for_attachment (TNY_MSG (mime_part), account, attachment_uid);
2580 modest_window_set_zoom (MODEST_WINDOW (msg_win),
2581 modest_window_get_zoom (MODEST_WINDOW (window)));
2582 modest_window_mgr_register_window (mgr, msg_win, MODEST_WINDOW (window));
2583 gtk_widget_show_all (GTK_WIDGET (msg_win));
2586 g_object_unref (mime_part);
2599 GnomeVFSResult result;
2602 static void save_mime_part_info_free (SaveMimePartInfo *info, gboolean with_struct);
2603 static gboolean idle_save_mime_part_show_result (SaveMimePartInfo *info);
2604 static gpointer save_mime_part_to_file (SaveMimePartInfo *info);
2605 static void save_mime_parts_to_file_with_checks (SaveMimePartInfo *info);
2608 save_mime_part_info_free (SaveMimePartInfo *info, gboolean with_struct)
2612 for (node = info->pairs; node != NULL; node = g_list_next (node)) {
2613 SaveMimePartPair *pair = (SaveMimePartPair *) node->data;
2614 g_free (pair->filename);
2615 g_object_unref (pair->part);
2616 g_slice_free (SaveMimePartPair, pair);
2618 g_list_free (info->pairs);
2621 gtk_widget_destroy (info->banner);
2622 g_slice_free (SaveMimePartInfo, info);
2627 idle_save_mime_part_show_result (SaveMimePartInfo *info)
2629 if (info->pairs != NULL) {
2630 save_mime_part_to_file (info);
2632 /* This is a GDK lock because we are an idle callback and
2633 * hildon_banner_show_information is or does Gtk+ code */
2635 gdk_threads_enter (); /* CHECKED */
2636 save_mime_part_info_free (info, TRUE);
2637 if (info->result == GNOME_VFS_OK) {
2638 hildon_banner_show_information (NULL, NULL, _CS("sfil_ib_saved"));
2639 } else if (info->result == GNOME_VFS_ERROR_NO_SPACE) {
2640 hildon_banner_show_information (NULL, NULL, dgettext("ke-recv",
2641 "cerm_device_memory_full"));
2643 hildon_banner_show_information (NULL, NULL, _("mail_ib_file_operation_failed"));
2645 gdk_threads_leave (); /* CHECKED */
2652 save_mime_part_to_file (SaveMimePartInfo *info)
2654 GnomeVFSHandle *handle;
2656 SaveMimePartPair *pair = (SaveMimePartPair *) info->pairs->data;
2658 info->result = gnome_vfs_create (&handle, pair->filename, GNOME_VFS_OPEN_WRITE, FALSE, 0644);
2659 if (info->result == GNOME_VFS_OK) {
2660 GError *error = NULL;
2661 stream = tny_vfs_stream_new (handle);
2662 if (tny_mime_part_decode_to_stream (pair->part, stream, &error) < 0) {
2663 g_warning ("modest: could not save attachment %s: %d (%s)\n", pair->filename, error?error->code:-1, error?error->message:"Unknown error");
2665 info->result = GNOME_VFS_ERROR_IO;
2667 g_object_unref (G_OBJECT (stream));
2668 g_object_unref (pair->part);
2669 g_slice_free (SaveMimePartPair, pair);
2670 info->pairs = g_list_delete_link (info->pairs, info->pairs);
2672 g_warning ("modest: could not create save attachment %s: %s\n", pair->filename, gnome_vfs_result_to_string (info->result));
2673 save_mime_part_info_free (info, FALSE);
2676 g_idle_add ((GSourceFunc) idle_save_mime_part_show_result, info);
2681 save_mime_parts_to_file_with_checks (SaveMimePartInfo *info)
2683 gboolean is_ok = TRUE;
2684 gint replaced_files = 0;
2685 const GList *files = info->pairs;
2688 for (iter = files; (iter != NULL) && (replaced_files < 2); iter = g_list_next(iter)) {
2689 SaveMimePartPair *pair = iter->data;
2690 if (modest_utils_file_exists (pair->filename)) {
2694 if (replaced_files) {
2695 GtkWidget *confirm_overwrite_dialog;
2696 const gchar *message = (replaced_files == 1) ?
2697 _FM("docm_nc_replace_file") : _FM("docm_nc_replace_multiple");
2698 confirm_overwrite_dialog = hildon_note_new_confirmation (NULL, message);
2699 if (gtk_dialog_run (GTK_DIALOG (confirm_overwrite_dialog)) != GTK_RESPONSE_OK) {
2702 gtk_widget_destroy (confirm_overwrite_dialog);
2706 save_mime_part_info_free (info, TRUE);
2708 GtkWidget *banner = hildon_banner_show_animation (NULL, NULL,
2709 _CS("sfil_ib_saving"));
2710 info->banner = banner;
2711 g_thread_create ((GThreadFunc)save_mime_part_to_file, info, FALSE, NULL);
2717 save_attachments_response (GtkDialog *dialog,
2721 TnyList *mime_parts;
2723 GList *files_to_save = NULL;
2725 mime_parts = TNY_LIST (user_data);
2727 if (arg1 != GTK_RESPONSE_OK)
2730 chooser_uri = gtk_file_chooser_get_uri (GTK_FILE_CHOOSER (dialog));
2732 if (!modest_utils_folder_writable (chooser_uri)) {
2733 hildon_banner_show_information
2734 (NULL, NULL, dgettext("hildon-fm", "sfil_ib_readonly_location"));
2738 iter = tny_list_create_iterator (mime_parts);
2739 while (!tny_iterator_is_done (iter)) {
2740 TnyMimePart *mime_part = (TnyMimePart *) tny_iterator_get_current (iter);
2742 if ((modest_tny_mime_part_is_attachment_for_modest (mime_part)) &&
2743 !tny_mime_part_is_purged (mime_part) &&
2744 (tny_mime_part_get_filename (mime_part) != NULL)) {
2745 SaveMimePartPair *pair;
2747 pair = g_slice_new0 (SaveMimePartPair);
2749 if (tny_list_get_length (mime_parts) > 1) {
2751 gnome_vfs_escape_slashes (tny_mime_part_get_filename (mime_part));
2752 pair->filename = g_build_filename (chooser_uri, escaped, NULL);
2755 pair->filename = g_strdup (chooser_uri);
2757 pair->part = mime_part;
2758 files_to_save = g_list_prepend (files_to_save, pair);
2760 tny_iterator_next (iter);
2762 g_object_unref (iter);
2764 g_free (chooser_uri);
2766 if (files_to_save != NULL) {
2767 SaveMimePartInfo *info = g_slice_new0 (SaveMimePartInfo);
2768 info->pairs = files_to_save;
2769 info->result = TRUE;
2770 save_mime_parts_to_file_with_checks (info);
2774 /* Free and close the dialog */
2775 g_object_unref (mime_parts);
2776 gtk_widget_destroy (GTK_WIDGET (dialog));
2780 modest_msg_view_window_save_attachments (ModestMsgViewWindow *window, TnyList *mime_parts)
2782 ModestMsgViewWindowPrivate *priv;
2783 GtkWidget *save_dialog = NULL;
2784 gchar *folder = NULL;
2785 gchar *filename = NULL;
2786 gchar *save_multiple_str = NULL;
2788 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window));
2789 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
2791 if (mime_parts == NULL) {
2792 mime_parts = modest_msg_view_get_selected_attachments (MODEST_MSG_VIEW (priv->msg_view));
2793 if (mime_parts == NULL || tny_list_get_length (mime_parts) == 0)
2796 g_object_ref (mime_parts);
2799 /* prepare dialog */
2800 if (tny_list_get_length (mime_parts) == 1) {
2802 /* only one attachment selected */
2803 iter = tny_list_create_iterator (mime_parts);
2804 TnyMimePart *mime_part = (TnyMimePart *) tny_iterator_get_current (iter);
2805 g_object_unref (iter);
2806 if (!modest_tny_mime_part_is_msg (mime_part) &&
2807 modest_tny_mime_part_is_attachment_for_modest (mime_part) &&
2808 !tny_mime_part_is_purged (mime_part)) {
2809 filename = g_strdup (tny_mime_part_get_filename (mime_part));
2811 /* TODO: show any error? */
2812 g_warning ("Tried to save a non-file attachment");
2813 g_object_unref (mime_parts);
2816 g_object_unref (mime_part);
2818 save_multiple_str = g_strdup_printf (_FM("sfil_va_number_of_objects_attachments"),
2819 tny_list_get_length (mime_parts));
2822 save_dialog = hildon_file_chooser_dialog_new (GTK_WINDOW (window),
2823 GTK_FILE_CHOOSER_ACTION_SAVE);
2826 folder = g_build_filename (g_get_home_dir (), DEFAULT_FOLDER, NULL);
2827 gtk_file_chooser_set_current_folder (GTK_FILE_CHOOSER (save_dialog), folder);
2832 gtk_file_chooser_set_current_name (GTK_FILE_CHOOSER (save_dialog),
2837 /* if multiple, set multiple string */
2838 if (save_multiple_str) {
2839 g_object_set (G_OBJECT (save_dialog), "save-multiple", save_multiple_str, NULL);
2840 gtk_window_set_title (GTK_WINDOW (save_dialog), _FM("sfil_ti_save_objects_files"));
2843 /* We must run this asynchronously, because the hildon dialog
2844 performs a gtk_dialog_run by itself which leads to gdk
2846 g_signal_connect (save_dialog, "response",
2847 G_CALLBACK (save_attachments_response), mime_parts);
2849 gtk_widget_show_all (save_dialog);
2853 show_remove_attachment_information (gpointer userdata)
2855 ModestMsgViewWindow *window = (ModestMsgViewWindow *) userdata;
2856 ModestMsgViewWindowPrivate *priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
2858 /* We're outside the main lock */
2859 gdk_threads_enter ();
2861 if (priv->remove_attachment_banner != NULL) {
2862 gtk_widget_destroy (priv->remove_attachment_banner);
2863 g_object_unref (priv->remove_attachment_banner);
2866 priv->remove_attachment_banner = g_object_ref (
2867 hildon_banner_show_animation (NULL, NULL, _("mcen_ib_removing_attachment")));
2869 gdk_threads_leave ();
2875 modest_msg_view_window_remove_attachments (ModestMsgViewWindow *window, gboolean get_all)
2877 ModestMsgViewWindowPrivate *priv;
2878 TnyList *mime_parts = NULL;
2879 gchar *confirmation_message;
2885 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window));
2886 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
2889 mime_parts = modest_msg_view_get_attachments (MODEST_MSG_VIEW (priv->msg_view));
2891 mime_parts = modest_msg_view_get_selected_attachments (MODEST_MSG_VIEW (priv->msg_view));
2893 /* Remove already purged messages from mime parts list */
2894 iter = tny_list_create_iterator (mime_parts);
2895 while (!tny_iterator_is_done (iter)) {
2896 TnyMimePart *part = TNY_MIME_PART (tny_iterator_get_current (iter));
2897 tny_iterator_next (iter);
2898 if (tny_mime_part_is_purged (part)) {
2899 tny_list_remove (mime_parts, (GObject *) part);
2901 g_object_unref (part);
2903 g_object_unref (iter);
2905 if (tny_list_get_length (mime_parts) == 0) {
2906 g_object_unref (mime_parts);
2910 n_attachments = tny_list_get_length (mime_parts);
2911 if (n_attachments == 1) {
2915 iter = tny_list_create_iterator (mime_parts);
2916 part = (TnyMimePart *) tny_iterator_get_current (iter);
2917 g_object_unref (iter);
2918 if (modest_tny_mime_part_is_msg (part)) {
2920 header = tny_msg_get_header (TNY_MSG (part));
2921 filename = tny_header_dup_subject (header);
2922 g_object_unref (header);
2923 if (filename == NULL)
2924 filename = g_strdup (_("mail_va_no_subject"));
2926 filename = g_strdup (tny_mime_part_get_filename (TNY_MIME_PART (part)));
2928 confirmation_message = g_strdup_printf (_("mcen_nc_purge_file_text"), filename);
2930 g_object_unref (part);
2932 confirmation_message = g_strdup_printf (ngettext("mcen_nc_purge_file_text",
2933 "mcen_nc_purge_files_text",
2934 n_attachments), n_attachments);
2936 response = modest_platform_run_confirmation_dialog (GTK_WINDOW (window),
2937 confirmation_message);
2938 g_free (confirmation_message);
2940 if (response != GTK_RESPONSE_OK) {
2941 g_object_unref (mime_parts);
2945 priv->purge_timeout = g_timeout_add (2000, show_remove_attachment_information, window);
2947 iter = tny_list_create_iterator (mime_parts);
2948 while (!tny_iterator_is_done (iter)) {
2951 part = (TnyMimePart *) tny_iterator_get_current (iter);
2952 tny_mime_part_set_purged (TNY_MIME_PART (part));
2953 g_object_unref (part);
2954 tny_iterator_next (iter);
2956 g_object_unref (iter);
2958 msg = tny_msg_view_get_msg (TNY_MSG_VIEW (priv->msg_view));
2959 tny_msg_view_clear (TNY_MSG_VIEW (priv->msg_view));
2960 tny_msg_rewrite_cache (msg);
2961 tny_msg_view_set_msg (TNY_MSG_VIEW (priv->msg_view), msg);
2962 g_object_unref (msg);
2964 g_object_unref (mime_parts);
2966 if (priv->purge_timeout > 0) {
2967 g_source_remove (priv->purge_timeout);
2968 priv->purge_timeout = 0;
2971 if (priv->remove_attachment_banner) {
2972 gtk_widget_destroy (priv->remove_attachment_banner);
2973 g_object_unref (priv->remove_attachment_banner);
2974 priv->remove_attachment_banner = NULL;
2982 update_window_title (ModestMsgViewWindow *window)
2984 ModestMsgViewWindowPrivate *priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
2986 TnyHeader *header = NULL;
2987 gchar *subject = NULL;
2989 msg = tny_msg_view_get_msg (TNY_MSG_VIEW (priv->msg_view));
2992 header = tny_msg_get_header (msg);
2993 subject = tny_header_dup_subject (header);
2994 g_object_unref (header);
2995 g_object_unref (msg);
2998 if ((subject == NULL)||(subject[0] == '\0')) {
3000 subject = g_strdup (_("mail_va_no_subject"));
3003 gtk_window_set_title (GTK_WINDOW (window), subject);
3007 static void on_move_focus (GtkWidget *widget,
3008 GtkDirectionType direction,
3011 g_signal_stop_emission_by_name (G_OBJECT (widget), "move-focus");
3015 fetch_image_open_stream (TnyStreamCache *self, gint64 *expected_size, gchar *uri)
3017 GnomeVFSResult result;
3018 GnomeVFSHandle *handle = NULL;
3019 GnomeVFSFileInfo *info = NULL;
3022 result = gnome_vfs_open (&handle, uri, GNOME_VFS_OPEN_READ);
3023 if (result != GNOME_VFS_OK) {
3028 info = gnome_vfs_file_info_new ();
3029 result = gnome_vfs_get_file_info_from_handle (handle, info, GNOME_VFS_FILE_INFO_DEFAULT);
3030 if (result != GNOME_VFS_OK || ! (info->valid_fields & GNOME_VFS_FILE_INFO_FIELDS_SIZE)) {
3031 /* We put a "safe" default size for going to cache */
3032 *expected_size = (300*1024);
3034 *expected_size = info->size;
3036 gnome_vfs_file_info_unref (info);
3038 stream = tny_vfs_stream_new (handle);
3047 TnyStream *output_stream;
3048 GtkWidget *msg_view;
3052 on_fetch_image_idle_refresh_view (gpointer userdata)
3055 FetchImageData *fidata = (FetchImageData *) userdata;
3056 g_message ("REFRESH VIEW");
3057 if (GTK_WIDGET_DRAWABLE (fidata->msg_view)) {
3058 g_message ("QUEUING DRAW");
3059 gtk_widget_queue_draw (fidata->msg_view);
3061 g_object_unref (fidata->msg_view);
3062 g_slice_free (FetchImageData, fidata);
3067 on_fetch_image_thread (gpointer userdata)
3069 FetchImageData *fidata = (FetchImageData *) userdata;
3070 TnyStreamCache *cache;
3071 TnyStream *cache_stream;
3073 cache = modest_runtime_get_images_cache ();
3074 cache_stream = tny_stream_cache_get_stream (cache, fidata->cache_id, (TnyStreamCacheOpenStreamFetcher) fetch_image_open_stream, (gpointer) fidata->uri);
3075 g_free (fidata->cache_id);
3076 g_free (fidata->uri);
3078 if (cache_stream != NULL) {
3079 tny_stream_write_to_stream (cache_stream, fidata->output_stream);
3080 tny_stream_close (cache_stream);
3081 g_object_unref (cache_stream);
3084 tny_stream_close (fidata->output_stream);
3085 g_object_unref (fidata->output_stream);
3088 gdk_threads_enter ();
3089 g_idle_add (on_fetch_image_idle_refresh_view, fidata);
3090 gdk_threads_leave ();
3096 on_fetch_image (ModestMsgView *msgview,
3099 ModestMsgViewWindow *window)
3101 const gchar *current_account;
3102 ModestMsgViewWindowPrivate *priv;
3103 FetchImageData *fidata;
3105 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
3107 current_account = modest_window_get_active_account (MODEST_WINDOW (window));
3109 fidata = g_slice_new0 (FetchImageData);
3110 fidata->msg_view = g_object_ref (msgview);
3111 fidata->uri = g_strdup (uri);
3112 fidata->cache_id = modest_images_cache_get_id (current_account, uri);
3113 fidata->output_stream = g_object_ref (stream);
3115 if (g_thread_create (on_fetch_image_thread, fidata, FALSE, NULL) == NULL) {
3116 g_object_unref (fidata->output_stream);
3117 g_free (fidata->cache_id);
3118 g_free (fidata->uri);
3119 g_object_unref (fidata->msg_view);
3120 g_slice_free (FetchImageData, fidata);
3121 tny_stream_close (stream);
3129 add_to_menu (ModestMsgViewWindow *self,
3130 HildonAppMenu *menu,
3133 ModestDimmingRulesGroup *dimming_group,
3134 GCallback dimming_callback)
3138 button = gtk_button_new_with_label (label);
3139 g_signal_connect_after (G_OBJECT (button), "clicked",
3140 callback, (gpointer) self);
3141 modest_dimming_rules_group_add_widget_rule (dimming_group,
3144 MODEST_WINDOW (self));
3145 hildon_app_menu_append (menu, GTK_BUTTON (button));
3149 setup_menu (ModestMsgViewWindow *self, ModestDimmingRulesGroup *group)
3151 ModestMsgViewWindowPrivate *priv = NULL;
3152 GtkWidget *app_menu;
3154 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW(self));
3156 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
3158 app_menu = hildon_app_menu_new ();
3160 /* Settings menu buttons */
3161 add_to_menu (self, HILDON_APP_MENU (app_menu), _("mcen_me_inbox_reply"),
3162 G_CALLBACK (modest_ui_actions_on_reply),
3163 group, G_CALLBACK (modest_ui_dimming_rules_on_reply_msg));
3164 add_to_menu (self, HILDON_APP_MENU (app_menu), _("mcen_me_inbox_replytoall"),
3165 G_CALLBACK (modest_ui_actions_on_reply_all),
3166 group, G_CALLBACK (modest_ui_dimming_rules_on_reply_msg));
3167 add_to_menu (self, HILDON_APP_MENU (app_menu), _("mcen_me_inbox_forward"),
3168 G_CALLBACK (modest_ui_actions_on_forward),
3169 group, G_CALLBACK (modest_ui_dimming_rules_on_reply_msg));
3170 add_to_menu (self, HILDON_APP_MENU (app_menu), _("mcen_me_viewer_newemail"),
3171 G_CALLBACK (modest_ui_actions_on_new_msg),
3172 group, G_CALLBACK (modest_ui_dimming_rules_on_new_msg));
3173 add_to_menu (self, HILDON_APP_MENU (app_menu), _("mcen_me_inbox_messagedetails"),
3174 G_CALLBACK (modest_ui_actions_on_details),
3175 group, G_CALLBACK (modest_ui_dimming_rules_on_details));
3176 add_to_menu (self, HILDON_APP_MENU (app_menu), _("mcen_me_viewer_save_attachments"),
3177 G_CALLBACK (modest_ui_actions_save_attachments),
3178 group, G_CALLBACK (modest_ui_dimming_rules_on_save_attachments));
3179 add_to_menu (self, HILDON_APP_MENU (app_menu), _("mcen_me_inbox_remove_attachments"),
3180 G_CALLBACK (modest_ui_actions_remove_attachments),
3181 group, G_CALLBACK (modest_ui_dimming_rules_on_remove_attachments));
3182 add_to_menu (self, HILDON_APP_MENU (app_menu), _("mcen_me_viewer_addtocontacts"),
3183 G_CALLBACK (modest_ui_actions_on_add_to_contacts),
3184 group, G_CALLBACK (modest_ui_dimming_rules_on_add_to_contacts));
3186 hildon_stackable_window_set_main_menu (HILDON_STACKABLE_WINDOW (self),
3187 HILDON_APP_MENU (app_menu));
3191 modest_msg_view_window_toggle_menu (HildonWindow *window,
3195 modest_ui_actions_check_menu_dimming_rules (MODEST_WINDOW (window));
3197 return HILDON_WINDOW_CLASS (parent_class)->toggle_menu (window, button, time);